Spring Boot搭建Model Context Protocol (MCP) 服务

 更新时间:2026年03月09日 16:54:57   作者:core321  
本文将手把手教你使用Spring Boot构建一个标准的MCP服务(Server),通过SSE(Server-Sent Events)和JSON-RPC 协议,暴露本地方法供大模型调用,本文通过代码示例给大家讲解的非常详细,感兴趣的朋友跟随小编一起看看吧

📖 前言

随着大模型(LLM)应用的爆发,如何让模型安全、标准地连接本地数据和工具成为了核心痛点。Model Context Protocol (MCP) 是由 Anthropic 提出的一项开放标准,旨在为 AI 助手与系统(数据库、工具、API)之间提供通用的连接接口。

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

本文将手把手教你使用 Spring Boot 构建一个标准的 MCP 服务(Server),通过 SSE(Server-Sent Events)和 JSON-RPC 协议,暴露本地方法供大模型调用。

🏗️ 架构设计

在开始写代码之前,我们先理清 MCP 的 HTTP 通信流程。MCP over HTTP 通常包含两个端点:

  1. SSE 端点:用于建立连接,服务器通过此通道推送通知。
  2. POST 端点:客户端(大模型或 MCP 宿主)通过此接口发送 JSON-RPC 请求。

通信时序图 (Mermaid)

🛠️ 代码实战

1. 项目初始化

创建一个 Spring Boot 项目 (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。我们需要定义请求和响应的基础类。

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;

    // 成功的静态工厂方法
    public static JsonRpcResponse success(Object id, Object result) {
        return new JsonRpcResponse("2.0", result, null, id);
    }
}

3. 核心服务层:处理 MCP 协议逻辑

这里是核心逻辑。我们需要处理三种主要方法:

  1. initialize:告诉客户端我是谁,我有什能力。
  2. tools/list:列出我可以提供的工具(Function Calling 定义)。
  3. tools/call:真正执行业务逻辑。

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":
                    // 客户端确认初始化完成,通常无需回复内容,但需保持连接
                    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. 握手协议
    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", "计算两个数字的和",
            "inputSchema", Map.of(
                "type", "object",
                "properties", Map.of(
                    "a", Map.of("type", "number", "description", "第一个数字"),
                    "b", Map.of("type", "number", "description", "第二个数字")
                ),
                "required", List.of("a", "b")
            )
        );
        
        // 定义一个系统信息工具
        Map<String, Object> sysInfoTool = Map.of(
            "name", "get_system_info",
            "description", "获取当前服务器运行环境信息",
            "inputSchema", Map.of("type", "object", "properties", Map.of())
        );

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

    // 3. 执行工具逻辑
    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连接(生产环境可能需要更复杂的Session管理)
    private final ConcurrentHashMap<String, SseEmitter> emitters = new ConcurrentHashMap<>();

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

    /**
     * 1. SSE 连接端点
     * 客户端连接到这里监听服务端事件
     */
    @GetMapping("/sse")
    public SseEmitter handleSse(HttpServletResponse response) {
        // 设置 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 标准:连接建立后,服务端发送 endpoint 事件,告知客户端去哪里发 POST 消息
            // 注意:这里硬编码了本地地址,实际部署需改为配置的域名/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. 消息接收端点 (POST)
     * 客户端发送 JSON-RPC 请求到这里
     */
    @PostMapping("/messages")
    public JsonRpcResponse handleMessage(
            @RequestParam(required = false) String sessionId,
            @RequestBody JsonRpcRequest request) {
        
        System.out.println("Received: " + request.getMethod());
        
        // 处理核心业务逻辑
        JsonRpcResponse response = mcpService.handleRequest(request);
        
        return response;
    }
}

🧪 测试与验证

要验证我们的服务是否符合 MCP 标准,我们可以使用 curl 或者 Anthropic 官方提供的 mcp-inspector(如果你有 Node.js 环境)。

这里我们使用简单的 HTTP 请求流程来模拟验证。

1. 启动服务

运行 Spring Boot 应用,默认端口 8080。

2. 模拟连接 (SSE)

在终端使用 curl 监听 SSE:

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

预期输出:

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

(保持这个窗口打开,或者记下 sessionId)

3. 发送 Initialize 请求 (POST)

打开一个新的终端窗口,发送初始化请求:

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"}
  }
}'

预期响应: 返回包含 serverInfocapabilities 的 JSON。

4. 调用工具 (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}
  }
}'

预期响应:

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

💡 总结与展望

通过上述步骤,我们成功使用 Spring Boot 构建了一个最小化的 MCP Server

  • 我们实现了 tools/list,大模型通过它“看见”了我们的功能。
  • 我们实现了 tools/call,大模型通过它“执行”了我们的代码。

接下来的进阶玩法:

  1. 连接数据库:在 McpService 中注入 MyBatis/JPA Mapper,让 AI 可以查询 SQL 数据。
  2. 集成 Spring AI:结合 Spring AI 框架,让 Java 方法自动映射为 Tool 定义,减少手动编写 JSON Schema 的工作量。
  3. 安全认证:在 SSE 和 POST 接口增加 Token 校验,防止未授权调用。

👨‍💻 作者提示:MCP 协议仍在快速演进中,建议关注 Anthropic 官方文档获取最新规范。如果你觉得这篇文章有帮助,欢迎 点赞、收藏、关注

到此这篇关于使用Spring Boot搭建 Model Context Protocol (MCP) 服务的实战教程的文章就介绍到这了,更多相关springboot搭建mcp服务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot+POI实现给word添加水印功能

    SpringBoot+POI实现给word添加水印功能

    这篇文章主要介绍了SpringBoot+POI实现给word添加水印功能,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-06-06
  • Java中实现用户之间的通讯方式

    Java中实现用户之间的通讯方式

    在Java中实现用户间通讯主要有两种方法:Socket编程和WebSocket,Socket编程允许两个设备间进行数据交换,适用于基本的网络通讯,本文提供了两种方法的基本实现代码和相关配置,帮助开发者根据需求选择合适的通讯方式
    2024-09-09
  • java中Callback简单使用总结

    java中Callback简单使用总结

    正好学习到java Callback,就整理了一下,希望整理的文章内容对大家有所帮助
    2017-04-04
  • 公司一般使用的分布式RPC框架及其原理面试

    公司一般使用的分布式RPC框架及其原理面试

    这篇文章主要为大家介绍了公司一般使用的分布式RPC框架及其原理的面试问题解答,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-03-03
  • 详解JDK9特性之JPMS模块化

    详解JDK9特性之JPMS模块化

    JDK9引入了一个特性叫做JPMS(Java Platform Module System),也可以叫做Project Jigsaw。模块化的本质就是将一个大型的项目拆分成为一个一个的模块,每个模块都是独立的单元,并且不同的模块之间可以互相引用和调用。本文将详细介绍JDK9特性之JPMS模块化。
    2021-06-06
  • Spring IOC (DI) 依赖注入的四种方式示例详解

    Spring IOC (DI) 依赖注入的四种方式示例详解

    这篇文章主要介绍了Spring IOC (DI) 依赖注入的四种方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • 详解Java中如何正确书写单例模式

    详解Java中如何正确书写单例模式

    一般单例都是五种写法:懒汉,饿汉,双重校验锁,静态内部类和枚举。本文整理了几种常见的单例写法,下面跟着小编一起来看下吧
    2017-01-01
  • Spring中存储Bean的常见注解方式

    Spring中存储Bean的常见注解方式

    Spring框架中的控制反转(IoC)和依赖注入(DI)是核心概念,实现了对象的解耦和动态依赖,IoC容器负责对象的生命周期和对象间的依赖关系,通过DI方式注入依赖,本文介绍Spring中存储Bean的常见注解方式,感兴趣的朋友一起看看吧
    2024-09-09
  • SpringBoot搭建全局异常拦截

    SpringBoot搭建全局异常拦截

    这篇文章主要介绍了SpringBoot搭建全局异常拦截,本文通过详细的介绍与代码的展示,详细的说明了如何搭建该项目,包括创建,启动和测试步骤,需要的朋友可以参考下
    2021-06-06
  • java开发_图片截取工具实现原理

    java开发_图片截取工具实现原理

    本文将详细介绍java开发_图片截取工具实现原理,需要了解的朋友可以参考下
    2012-11-11

最新评论