最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Boot搭建Model?Context?Protocol?(MCP)?服務(wù)

 更新時間:2026年03月09日 16:54:57   作者:core321  
本文將手把手教你使用Spring?Boot構(gòu)建一個標(biāo)準(zhǔn)的MCP服務(wù)(Server),通過SSE(Server-Sent?Events)和JSON-RPC?協(xié)議,暴露本地方法供大模型調(diào)用,本文通過代碼示例給大家講解的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧

?? 前言

隨著大模型(LLM)應(yīng)用的爆發(fā),如何讓模型安全、標(biāo)準(zhǔn)地連接本地數(shù)據(jù)和工具成為了核心痛點(diǎn)。Model Context Protocol (MCP) 是由 Anthropic 提出的一項(xiàng)開放標(biāo)準(zhǔn),旨在為 AI 助手與系統(tǒng)(數(shù)據(jù)庫、工具、API)之間提供通用的連接接口。

想象一下:MCP 就是 AI 時代的 USB 接口。

本文將手把手教你使用 Spring Boot 構(gòu)建一個標(biāo)準(zhǔn)的 MCP 服務(wù)(Server),通過 SSE(Server-Sent Events)和 JSON-RPC 協(xié)議,暴露本地方法供大模型調(diào)用。

??? 架構(gòu)設(shè)計

在開始寫代碼之前,我們先理清 MCP 的 HTTP 通信流程。MCP over HTTP 通常包含兩個端點(diǎn):

  1. SSE 端點(diǎn):用于建立連接,服務(wù)器通過此通道推送通知。
  2. POST 端點(diǎn):客戶端(大模型或 MCP 宿主)通過此接口發(fā)送 JSON-RPC 請求。

通信時序圖 (Mermaid)

??? 代碼實(shí)戰(zhàn)

1. 項(xiàng)目初始化

創(chuàng)建一個 Spring Boot 項(xiàng)目 (JDK 17+),在 pom.xml 中引入必要的依賴。主要依賴是 Web 模塊和用于處理 JSON 的 Jackson。

<dependencies>
    <!-- Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Lombok (可選,簡化代碼) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2. 定義 JSON-RPC 模型

MCP 基于 JSON-RPC 2.0。我們需要定義請求和響應(yīng)的基礎(chǔ)類。

JsonRpcRequest.java

package com.example.mcp.model;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;

@Data
public class JsonRpcRequest {
    private String jsonrpc = "2.0";
    private String method;
    private JsonNode params;
    private Object id;
}

JsonRpcResponse.java

package com.example.mcp.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonRpcResponse {
    private String jsonrpc = "2.0";
    private Object result;
    private Object error;
    private Object id;

    // 成功的靜態(tài)工廠方法
    public static JsonRpcResponse success(Object id, Object result) {
        return new JsonRpcResponse("2.0", result, null, id);
    }
}

3. 核心服務(wù)層:處理 MCP 協(xié)議邏輯

這里是核心邏輯。我們需要處理三種主要方法:

  1. initialize:告訴客戶端我是誰,我有什能力。
  2. tools/list:列出我可以提供的工具(Function Calling 定義)。
  3. tools/call:真正執(zhí)行業(yè)務(wù)邏輯。

McpService.java

package com.example.mcp.service;

import com.example.mcp.model.JsonRpcRequest;
import com.example.mcp.model.JsonRpcResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class McpService {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public JsonRpcResponse handleRequest(JsonRpcRequest request) {
        String method = request.getMethod();

        try {
            switch (method) {
                case "initialize":
                    return handleInitialize(request);
                case "tools/list":
                    return handleListTools(request);
                case "tools/call":
                    return handleCallTool(request);
                case "notifications/initialized":
                    // 客戶端確認(rèn)初始化完成,通常無需回復(fù)內(nèi)容,但需保持連接
                    return null; 
                default:
                    throw new RuntimeException("Method not found: " + method);
            }
        } catch (Exception e) {
            // 簡單錯誤處理
            return new JsonRpcResponse("2.0", null, Map.of("code", -32603, "message", e.getMessage()), request.getId());
        }
    }

    // 1. 握手協(xié)議
    private JsonRpcResponse handleInitialize(JsonRpcRequest request) {
        Map<String, Object> result = new HashMap<>();
        result.put("protocolVersion", "2025-11-05");
        result.put("capabilities", Map.of("tools", Map.of())); // 聲明支持工具
        result.put("serverInfo", Map.of("name", "SpringBoot-MCP-Demo", "version", "1.0.0"));
        return JsonRpcResponse.success(request.getId(), result);
    }

    // 2. 定義工具列表
    private JsonRpcResponse handleListTools(JsonRpcRequest request) {
        // 定義一個簡單的加法工具
        Map<String, Object> addTool = Map.of(
            "name", "calculate_sum",
            "description", "計算兩個數(shù)字的和",
            "inputSchema", Map.of(
                "type", "object",
                "properties", Map.of(
                    "a", Map.of("type", "number", "description", "第一個數(shù)字"),
                    "b", Map.of("type", "number", "description", "第二個數(shù)字")
                ),
                "required", List.of("a", "b")
            )
        );
        
        // 定義一個系統(tǒng)信息工具
        Map<String, Object> sysInfoTool = Map.of(
            "name", "get_system_info",
            "description", "獲取當(dāng)前服務(wù)器運(yùn)行環(huán)境信息",
            "inputSchema", Map.of("type", "object", "properties", Map.of())
        );

        return JsonRpcResponse.success(request.getId(), Map.of("tools", List.of(addTool, sysInfoTool)));
    }

    // 3. 執(zhí)行工具邏輯
    private JsonRpcResponse handleCallTool(JsonRpcRequest request) {
        String name = request.getParams().get("name").asText();
        Map<String, Object> arguments = objectMapper.convertValue(request.getParams().get("arguments"), Map.class);

        String resultText = "";

        if ("calculate_sum".equals(name)) {
            double a = Double.parseDouble(arguments.get("a").toString());
            double b = Double.parseDouble(arguments.get("b").toString());
            resultText = String.valueOf(a + b);
        } else if ("get_system_info".equals(name)) {
            resultText = System.getProperty("os.name") + " - Java " + System.getProperty("java.version");
        } else {
            throw new RuntimeException("Unknown tool: " + name);
        }

        // MCP 要求的返回格式 content: [{type: "text", text: "..."}]
        Map<String, Object> content = Map.of(
            "content", List.of(Map.of("type", "text", "text", resultText))
        );
        
        return JsonRpcResponse.success(request.getId(), content);
    }
}

4. Controller 層:暴露 SSE 和 HTTP 接口

這是與外部世界交互的入口。

McpController.java

package com.example.mcp.controller;

import com.example.mcp.model.JsonRpcRequest;
import com.example.mcp.model.JsonRpcResponse;
import com.example.mcp.service.McpService;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/mcp")
public class McpController {

    private final McpService mcpService;
    // 用于保存SSE連接(生產(chǎn)環(huán)境可能需要更復(fù)雜的Session管理)
    private final ConcurrentHashMap<String, SseEmitter> emitters = new ConcurrentHashMap<>();

    public McpController(McpService mcpService) {
        this.mcpService = mcpService;
    }

    /**
     * 1. SSE 連接端點(diǎn)
     * 客戶端連接到這里監(jiān)聽服務(wù)端事件
     */
    @GetMapping("/sse")
    public SseEmitter handleSse(HttpServletResponse response) {
        // 設(shè)置 SSE 超時時間,0表示無限
        SseEmitter emitter = new SseEmitter(0L);
        String sessionId = UUID.randomUUID().toString();
        
        emitters.put(sessionId, emitter);

        emitter.onCompletion(() -> emitters.remove(sessionId));
        emitter.onTimeout(() -> emitters.remove(sessionId));

        try {
            // MCP 標(biāo)準(zhǔn):連接建立后,服務(wù)端發(fā)送 endpoint 事件,告知客戶端去哪里發(fā) POST 消息
            // 注意:這里硬編碼了本地地址,實(shí)際部署需改為配置的域名/IP
            String endpointUrl = "/mcp/messages?sessionId=" + sessionId;
            emitter.send(SseEmitter.event().name("endpoint").data(endpointUrl));
            System.out.println("Client connected, session: " + sessionId);
        } catch (IOException e) {
            emitters.remove(sessionId);
        }

        return emitter;
    }

    /**
     * 2. 消息接收端點(diǎn) (POST)
     * 客戶端發(fā)送 JSON-RPC 請求到這里
     */
    @PostMapping("/messages")
    public JsonRpcResponse handleMessage(
            @RequestParam(required = false) String sessionId,
            @RequestBody JsonRpcRequest request) {
        
        System.out.println("Received: " + request.getMethod());
        
        // 處理核心業(yè)務(wù)邏輯
        JsonRpcResponse response = mcpService.handleRequest(request);
        
        return response;
    }
}

?? 測試與驗(yàn)證

要驗(yàn)證我們的服務(wù)是否符合 MCP 標(biāo)準(zhǔn),我們可以使用 curl 或者 Anthropic 官方提供的 mcp-inspector(如果你有 Node.js 環(huán)境)。

這里我們使用簡單的 HTTP 請求流程來模擬驗(yàn)證。

1. 啟動服務(wù)

運(yùn)行 Spring Boot 應(yīng)用,默認(rèn)端口 8080。

2. 模擬連接 (SSE)

在終端使用 curl 監(jiān)聽 SSE:

curl -N http://localhost:8080/mcp/sse

預(yù)期輸出:

event:endpoint
data:/mcp/messages?sessionId=xxxx-xxxx-xxxx...

(保持這個窗口打開,或者記下 sessionId)

3. 發(fā)送 Initialize 請求 (POST)

打開一個新的終端窗口,發(fā)送初始化請求:

curl -X POST http://localhost:8080/mcp/messages \
-H "Content-Type: application/json" \
-d '{
  "jsonrpc": "2.0",
  "method": "initialize",
  "id": 1,
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": {"name": "curl-client", "version": "1.0"}
  }
}'

預(yù)期響應(yīng): 返回包含 serverInfocapabilities 的 JSON。

4. 調(diào)用工具 (Tools Call)

測試我們編寫的加法工具:

curl -X POST http://localhost:8080/mcp/messages \
-H "Content-Type: application/json" \
-d '{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "id": 2,
  "params": {
    "name": "calculate_sum",
    "arguments": {"a": 10, "b": 25.5}
  }
}'

預(yù)期響應(yīng):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "35.5"
      }
    ]
  },
  "id": 2
}

?? 總結(jié)與展望

通過上述步驟,我們成功使用 Spring Boot 構(gòu)建了一個最小化的 MCP Server

  • 我們實(shí)現(xiàn)了 tools/list,大模型通過它“看見”了我們的功能。
  • 我們實(shí)現(xiàn)了 tools/call,大模型通過它“執(zhí)行”了我們的代碼。

接下來的進(jìn)階玩法:

  1. 連接數(shù)據(jù)庫:在 McpService 中注入 MyBatis/JPA Mapper,讓 AI 可以查詢 SQL 數(shù)據(jù)。
  2. 集成 Spring AI:結(jié)合 Spring AI 框架,讓 Java 方法自動映射為 Tool 定義,減少手動編寫 JSON Schema 的工作量。
  3. 安全認(rèn)證:在 SSE 和 POST 接口增加 Token 校驗(yàn),防止未授權(quán)調(diào)用。

??‍?? 作者提示:MCP 協(xié)議仍在快速演進(jìn)中,建議關(guān)注 Anthropic 官方文檔獲取最新規(guī)范。如果你覺得這篇文章有幫助,歡迎 點(diǎn)贊、收藏、關(guān)注!

到此這篇關(guān)于使用Spring Boot搭建 Model Context Protocol (MCP) 服務(wù)的實(shí)戰(zhàn)教程的文章就介紹到這了,更多相關(guān)springboot搭建mcp服務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot 使用yml配置文件給靜態(tài)變量賦值教程

    springboot 使用yml配置文件給靜態(tài)變量賦值教程

    這篇文章主要介紹了springboot 使用yml配置文件給靜態(tài)變量賦值教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • SpringCloud3.x集成BigQuery的代碼實(shí)現(xiàn)

    SpringCloud3.x集成BigQuery的代碼實(shí)現(xiàn)

    Google BigQuery 是一種高性能、可應(yīng)用于大數(shù)據(jù)分析的公主云數(shù)據(jù)庫服務(wù),Spring Cloud 提供了完善的工具和核心功能,可以進(jìn)行泛動分布應(yīng)用構(gòu)建,本文給大家介紹了SpringCloud3.x集成BigQuery的代碼實(shí)現(xiàn),需要的朋友可以參考下
    2025-01-01
  • 基于Freemarker和xml實(shí)現(xiàn)Java導(dǎo)出word

    基于Freemarker和xml實(shí)現(xiàn)Java導(dǎo)出word

    這篇文章主要介紹了基于Freemarker和xml實(shí)現(xiàn)Java導(dǎo)出word,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • 一文詳解Spring是怎么讀取配置Xml文件的

    一文詳解Spring是怎么讀取配置Xml文件的

    這篇文章主要介紹了一文詳解Spring是怎么讀取配置Xml文件的,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下
    2022-08-08
  • 使用PoolingHttpClientConnectionManager實(shí)現(xiàn)http連接池過程

    使用PoolingHttpClientConnectionManager實(shí)現(xiàn)http連接池過程

    文章主要介紹了`PoolingHttpClientConnectionManager`實(shí)現(xiàn)HTTP連接池的原理、依賴實(shí)現(xiàn)、定時回收鏈接的方法,以及如何正確釋放連接以復(fù)用
    2025-11-11
  • 若依框架升級springBoot3啟動druid-spring-boot-starter報錯問題及解決

    若依框架升級springBoot3啟動druid-spring-boot-starter報錯問題及解決

    文章主要描述了在使用Spring Boot 3時,由于缺少`com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties`類型的bean而報錯,解決方案是考慮在配置中定義該類型bean
    2026-01-01
  • SpringBoot整合MybatisPlus的基本應(yīng)用指南

    SpringBoot整合MybatisPlus的基本應(yīng)用指南

    MyBatis-Plus ,簡稱 MP,是一個 MyBatis的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,下面小編就來和大家介紹一下SpringBoot整合MybatisPlus的一些基本應(yīng)用吧
    2025-03-03
  • RestTemplate文件上傳下載與大文件流式下載

    RestTemplate文件上傳下載與大文件流式下載

    這篇文章主要為大家介紹了RestTemplate文件上傳下載與大文件流式下載的使用方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步
    2022-03-03
  • 淺談java常量池

    淺談java常量池

    下面小編就為大家?guī)硪黄獪\談java常量池。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • SpringBoot2.x漏洞將logback1.2.x 升級至1.3.x

    SpringBoot2.x漏洞將logback1.2.x 升級至1.3.x

    安全部門在代碼漏洞掃描中發(fā)現(xiàn)logback 1.2.x版本存在CVE漏洞,建議升級至1.3.x版本,本文就來介紹了logback1.2.x 升級至1.3.x,具有一定的參考價值,感興趣的可以了解一下
    2024-09-09

最新評論

綦江县| 武城县| 兰西县| 吴堡县| 宝清县| 乡城县| 司法| 隆尧县| 华坪县| 通化县| 崇阳县| 潜江市| 敦化市| 广饶县| 正阳县| 儋州市| 南雄市| 澎湖县| 皋兰县| 咸丰县| 正定县| 宁德市| 深水埗区| 阿图什市| 阳春市| 阿拉善左旗| 宜丰县| 宁安市| 余庆县| 正定县| 景德镇市| 龙陵县| 宁武县| 江西省| 上饶县| 南丹县| 鹤庆县| 普定县| 黔南| 庆安县| 淮南市|