MCP服务器开发流程

mcp开发的三层架构

客户端(mcp client,mcp session, mcp transport) <-- stdio/ see--> 服务端(mcp server, mcp session, mcp transport)
客户端负责客户端操作,使用mcp session进行通信,mcp session 使用defaultMcpSession进行通信和状态管理,传输层支持 json-rpc的序列化和反序列化(基于stdio的标准io流,http see远程传输)

mcp提供的功能

提供资源,提示词(向客户端提供提示词模板,以及工作流),tools(工具调用能力),sampling(采样允许服务端向发模型发送生成内容的请求),roots(根目录,mcp的安全协议) transport是(传输)定义客户端和服务器件的通信方式

mcp接入

本地软件:从mcp软件市场下载之后,本地config里要写好其对应的mcp.json 以及申请的api key 在cursor或者codex里有mcp 直接选择后软件会自动扫描
程序客户端mcp:

  1. 引入spring ai mcp sdk开发依赖,在resource目录下新建mcp-server.json配置,定义需要用到的mcp服务
    2)主程序的yaml里写上对应的mcp服务器配置,保证mcp客户端启动后会使用子进程启动mcp服务
    3)创建ToolCallBackProvider类,内部注册了mcp创建的工具类,在app调用chatmodel时向tool方法传入ToolCallBackProvider实例

mcp程序开发

1)引入spring ai 依赖,spring ai-starter-mcp-client(支持stdio以及http的see支持);spring ai-starter-mcp-client-webFlux 基于webFlux的响应式的see传输实现
2)配置连接: 第一种直接写入配置文件

spring:
  ai:
    mcp:
      client:
        enabled: true
        name: my-mcp-client
        version: 1.0.0
        request-timeout: 30s
        type: SYNC
        sse:
          connections:
            server1:
              url: http://localhost:8080
        stdio:
          connections:
            server1:
              command: /path/to/server
              args:
                - --port=8080
              env:
                API_KEY: your-api-key

使用基于claude desktop格式的json文件(仅支持stdio连接方式)

spring:
  ai:
    mcp:
      client:
        stdio:
          servers-configuration: classpath:mcp-servers.json

配置文件(放到mcp客户端):

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/Desktop",
        "/Users/username/Downloads"
      ]
    }
  }
}

想要完全控制mcp客户端的行为:使用mcpClient bean,其中提供了很多方法进行交互,想要使用mcp的工具增强ai能力,使用ToolCallbackProvider进行注入

mcp服务端开发

1 引入依赖
spring ai-starter-mcp-server:提供stdio传输支持,不额外需要web依赖
spring ai-starter-mcp-server-webmvc:提供基于spring mvc的see传输和可选的stdio传(建议)
spring-ai-starter-mcp-server-webflux:基于spring webflux的响应式see传输以及可选stdio输出
2 yaml配置:stdio配置和see配置

# 使用 spring-ai-starter-mcp-server
spring:
  ai:
    mcp:
      server:
        name: stdio-mcp-server
        version: 1.0.0
        stdio: true
        type: SYNC # 同步
# see服务
# 使用 spring-ai-starter-mcp-server-webmvc
spring:
  ai:
    mcp:
      server:
        name: webmvc-mcp-server
        version: 1.0.0
        type: SYNC # 同步
        sse-message-endpoint: /mcp/message  # SSE 消息端点路径
        sse-endpoint: /sse                  # SSE 端点路径

3 服务开发
创建对应的服务功能类,功能类中创建对应的工具函数,工具函数使用tool注解修饰,函数参数用toolparameter参数进行修饰
在mcp的spring application主启动类中,注册toolcallbackprovider bean即可

@Service
public class ImageSearchTool {

    // 替换为你的 Pexels API 密钥(需从官网申请)
    private static final String API_KEY = "改为你的 API Key";

    // Pexels 常规搜索接口(请以文档为准)
    private static final String API_URL = "https://api.pexels.com/v1/search";

    @Tool(description = "search image from web")
    public String searchImage(@ToolParam(description = "Search query keyword") String query) {
        try {
            return String.join(",", searchMediumImages(query));
        } catch (Exception e) {
            return "Error search image: " + e.getMessage();
        }
    }

    /**
     * 搜索中等尺寸的图片列表
     *
     * @param query
     * @return
     */
    public List<String> searchMediumImages(String query) {
        // 设置请求头(包含API密钥)
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", API_KEY);

        // 设置请求参数(仅包含query,可根据文档补充page、per_page等参数)
        Map<String, Object> params = new HashMap<>();
        params.put("query", query);

        // 发送 GET 请求
        String response = HttpUtil.createGet(API_URL)
                .addHeaders(headers)
                .form(params)
                .execute()
                .body();

        // 解析响应JSON(假设响应结构包含"photos"数组,每个元素包含"medium"字段)
        return JSONUtil.parseObj(response)
                .getJSONArray("photos")
                .stream()
                .map(photoObj -> (JSONObject) photoObj)
                .map(photoObj -> photoObj.getJSONObject("src"))
                .map(photo -> photo.getStr("medium"))
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toList());
    }
}
启动类注入bean实例对象
@SpringBootApplication
public class YuImageSearchMcpServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(YuImageSearchMcpServerApplication.class, args);
    }

    @Bean
    public ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(imageSearchTool)
                .build();
    }

}

拓展问题:

mcp安全:mcp内部的信息处理过程对于模型来说是一个黑箱,模型通过程序调用mcp服务,mcp服务传输数据到用户程序,这个过程客户端程序无法进行安全检查只能做输入输出检查,大模型接收到的也是mcp的输出,因此不具备完备的安全性,要慎用
Stdio通信:适用于本地,单客户端,命令行工具,ide插件等,客户端启动mcp服务作为其子进程,通信通过管道进行,可u护短向服务器写入请求,服务器读取并处理,将响应写入到标准输出后客户端读取响应
优点:性能高,安全性好,不对外暴露端口,部署极简
局限:仅适用于与客户端在同一台机器上运行的服务器,无法远程访问;严格的进行间1:1关系,无法直接响应多客户端
SEE:适用于远程访问,多客户端,跨网络,公共服务等场景,客户端通过get请求连接到服务器的see端点,简历一条持久化的http长连接,服务器可以随时向客户端单向推送事件
客户端如果需发送请求,需要向服务器的另一个post端口发送http请求
优点:支持跨网络的远程服务,可服务多个客户端,兼容标准http认证和安全机制
局限:性能开销比stdio大,需要处理网络波动和重建逻辑

大模型与MCP的完整交互流程是动态发现和调用的标准化过程:

初始化阶段:MCP客户端启动或检测到有服务器加入时,向服务器发送 initialize 请求完成握手。

工具发现:客户端发送 tools/list JSON-RPC请求,服务器返回所有可用工具的列表,包含名称、描述、参数模式(JSON Schema)等元数据。

模型决策:大模型根据用户请求、历史对话和工具列表中的描述,决定调用哪个工具以及如何传参。

工具调用:客户端发送 tools/call 请求,服务器执行后返回结果。

动态更新:若服务器工具列表变化,发送 notifications/tools/list_changed 通知客户端刷新,大模型可即时感知新能力

导航