使用Spring AI开发MCP Client的配置与实战

By ref-nobody 创建时间 2025年5月3日 | 本文最后更新于 2025年5月13日 #mcp, #mcp client

模型上下文协议(Model Context Protocol,简称 MCP)是一种标准化的协议,它使得人工智能模型能够以结构化的方式与外部工具和资源进行交互。它支持多种传输机制,以适应不同环境中的灵活性需求。

MCP Java SDK 提供了模型上下文协议的 Java 实现,通过同步和异步通信模式,实现了与人工智能模型和工具的标准化交互。

Spring AI MCP 在 MCP Java SDK 的基础上进行了扩展,加入了 Spring Boot 集成,提供了客户端和服务器端的启动器。您可以使用 Spring Initializer 快速启动支持 MCP 的人工智能应用程序。

MCP Java SDK架构

Java的MCP实现采用三层架构:

  • 客户端/服务器层:McpClient 负责处理客户端操作,而 McpServer 管理服务器端协议操作。两者均通过 McpSession 进行通信管理。
  • 会话层(McpSession):通过 DefaultMcpSession 实现来管理通信模式和状态。
  • 传输层(McpTransport):负责处理 JSON-RPC 消息的序列化和反序列化,并支持多种传输实现。

MCP 客户端 mcp client


MCP 客户端是模型上下文协议(Model Context Protocol,简称 MCP)架构中的关键组件,负责与 MCP 服务器建立和管理连接。它实现了协议的客户端部分,处理以下内容:

  • 协议版本协商:以确保与服务器的兼容性。
  • 功能协商:以确定可用的功能。
  • 消息传输和 JSON-RPC 通信
  • 工具发现和执行
  • 资源访问和管理
  • 提示系统交互
  • 可选功能包括:
    • 根目录管理
    • 采样支持
  • 同步和异步操作
  • 传输选项包括:
    • 用于响应式 HTTP 流的 WebFlux SSE 客户端传输
    • 基于标准输入/输出(Stdio)的传输:用于基于进程的通信。
    • 基于 Java HttpClient 的服务器发送事件(SSE)客户端传输

MCP 服务端 mcp server


MCP 服务器是模型上下文协议(Model Context Protocol,简称 MCP)架构中的基础组件,它为客户端提供工具、资源和功能。它实现了协议的服务器端部分,负责以下功能:

  • 服务器端协议操作的实现
    • 工具的暴露和发现
    • 基于 URI 的资源管理和访问
    • 提示模板的提供和处理
    • 与客户端的功能协商
    • 结构化日志记录和通知
  • 并发客户端连接管理
  • 同步和异步 API 支持
  • 传输实现包括:
    • 用于基于 Servlet 的 HTTP 流的 WebMVC SSE 服务器传输
    • 基于标准输入/输出(Stdio)的传输:用于基于进程的通信。
    • 基于 Servlet 的 SSE 服务器传输
    • 用于响应式 HTTP 流的 WebFlux SSE 服务器传输

MCP Client Boot Starter

Spring AI MCP(模型上下文协议)客户端启动器为 Spring Boot 应用程序中的 MCP 客户端功能提供了自动配置。它支持同步和异步客户端实现,并提供多种传输选项。

MCP 客户端启动器提供以下功能:

  • 管理多个客户端实例
  • 自动客户端初始化(如果启用)
  • 支持多个命名传输
  • 与 Spring AI 的工具执行框架集成
  • 在应用程序上下文关闭时,自动清理资源的生命周期管理
  • 通过自定义器实现可定制的客户端创建

标准的MCP客户端

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

标准启动器会同时通过 STDIO(进程内)和/或 SSE(远程)传输连接到一个或多个 MCP 服务器。SSE 连接使用基于 HttpClient 的传输实现。每个连接到 MCP 服务器的操作都会创建一个新的 MCP 客户端实例。您可以选择使用同步(SYNC)或异步(ASYNC)MCP 客户端(注意:不能混用同步和异步客户端)。对于生产部署,我们建议使用基于 WebFlux 的 SSE 连接,并搭配 spring-ai-starter-mcp-client-webflux

WebFlux客户端

WebFlux 启动器提供了与标准启动器类似的功能,但使用了基于 WebFlux 的 SSE(服务器发送事件)传输实现。

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>

属性配置

公共属性配置

通用属性以 spring.ai.mcp.client 为前缀:

属性描述默认值
enabled启用/禁用 MCP 客户端true
nameMCP 客户端实例的名称(用于兼容性检查)spring-ai-mcp-client
versionMCP 客户端实例的版本1.0.0
initialized是否在创建时初始化客户端true
request-timeoutMCP 客户端请求的超时时间20s
type客户端类型(SYNCASYNC)。所有客户端必须是同步或异步的;不支持混合使用SYNC
root-change-notification启用/禁用所有客户端的根更改通知true
toolcallback.enabled启用/禁用 MCP 工具回调与 Spring AI 工具执行框架的集成false

stdio传输属性配置

标准输入/输出(Standard I/O)传输相关的属性以 spring.ai.mcp.client.stdio 为前缀:

属性描述默认值
servers-configuration包含以 JSON 格式配置的 MCP 服务器信息的资源
connections命名的 stdio 连接配置映射
connections.[name].command要为 MCP 服务器执行的命令
connections.[name].args命令参数列表
connections.[name].env服务器进程的环境变量映射

配置示例:

spring:
  ai:
    mcp:
      client:
        stdio:
          root-change-notification: true
          connections:
            server1:
              command: /path/to/server
              args:
                - --port=8080
                - --mode=production
              env:
                API_KEY: your-api-key
                DEBUG: "true"

除此之外,还可以使用Claude Desktop格式的json配置文件进行配置:

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

Claude Desktop格式的mcp-servers.json配置:

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

目前Claude Desktop格式只支持stdio的连接类型。

SSE传输属性配置

服务器发送事件(Server-Sent Events,简称 SSE)传输相关的属性以 spring.ai.mcp.client.sse 为前缀:

属性描述默认值
connections命名的 SSE 连接配置映射
connections.[name].url与 MCP 服务器进行 SSE 通信的基础 URL 端点
connections.[name].sse-endpoint连接使用的 SSE 端点(作为 URL 后缀)/sse

配置示例:

spring:
  ai:
    mcp:
      client:
        sse:
          connections:
            server1:
              url: http://localhost:8080
            server2:
              url: http://otherserver:8081
              sse-endpoint: /custom-sse

MCP Client实战

添加mcp client的项目依赖starter,并在application.yml配置文件中添加配置,这里找到的是一个与github进行交互的mcp server:

spring:
  ai:
    mcp:
      client:
        enabled: true
        stdio:
          connections:
            github:
              command: "npx"
              args:
                - "-y"
                - "@modelcontextprotocol/server-github"
              env:
                GITHUB_PERSONAL_ACCESS_TOKEN: ${github-key}
#          servers-configuration: classpath:mcp-server.json
        toolcallback:
          enabled: true

增加了上面的依赖和配置之后,mcp client的bean对象会自动的配置并且可以被自动注入:

@Autowired
private List<McpSyncClient> mcpSyncClients;  // For sync client

// OR

@Autowired
private List<McpAsyncClient> mcpAsyncClients;  // For async client

当工具回调被启用时,所有已注册的 MCP 工具(与所有 MCP 客户端相关联)将作为 ToolCallbackProvider 实例提供。

@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();

1. 添加依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-openai</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>

2. 配置applicaiton.yml

在使用mcp客户端和选择语言模型的时候,需要选择支持mcp工具调用的模型,否则mcp server的工具不会被调用。

spring:
  ai:
    mcp:
      client:
        enabled: true
        stdio:
          connections:
            github:
              command: "npx"
              args:
                - "-y"
                - "@modelcontextprotocol/server-github"
              env:
                GITHUB_PERSONAL_ACCESS_TOKEN: ${github-key}
#          servers-configuration: classpath:mcp-server.json
        toolcallback:
          enabled: true
    openai:
      api-key: ${api-key}
      base-url: https://api.deepseek.com
      chat:
        options:
          temperature: 0.5
          model: deepseek-chat

3. controller代码编写

import io.modelcontextprotocol.client.McpSyncClient;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

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

    private final List<McpSyncClient> mcpSyncClientList;
    private final ToolCallbackProvider toolCallbackProvider;
    private final ChatClient chatClient;

    public McpController(List<McpSyncClient> mcpSyncClientList, ToolCallbackProvider toolCallbackProvider, ChatClient.Builder chatClientBuilder) {
        this.mcpSyncClientList = mcpSyncClientList;
        this.toolCallbackProvider = toolCallbackProvider;
        this.chatClient = chatClientBuilder
                .defaultToolCallbacks(toolCallbackProvider.getToolCallbacks())
                .build();

    }

    @GetMapping("/getChat")
    public String getChatClient(@RequestParam("text") String text) {
        return chatClient.prompt(text)
//                .toolCallbacks(toolCallbackProvider.getToolCallbacks())
                .call()
                .content();
    }

}

4. 结果验证

问题记录

模型返回的工具调用不稳定

在使用qwen3:4b模型进行测试的时候,发现不是总能调用到mcp server的工具。github的工具有26个,在调用模型的时候会在参数重传递这些工具到模型中,然后模型会计算出该调用哪个工具。手动通过postman请求模型,将传递的工具数减少,再次发送发现工具少的时候,返回会更准确,模型更容易决定调用哪个工具。通过调试可以获取到发送给ollama的参数:

将请求参数转化为json序列话,发送给localhost:11434/api/chat,可以观察获取到的响应。尝试只设置一个工具,则能正确返回需要调用的工具信息:

另一个问题是,在调用的时候,发现模型返回的工具名称不在调用参数的工具列表中,返回了一个未知的工具方法名,目前不知道这个名称是如何生成的。在这里与ollama以及在ollama中部署的模型的交互成为了一个黑盒……

在这个图中返回的工具名称为get_user,并不在请求参数的工具列表中

环境信息:ollama0.6.6,模型qwen3:4b,mcp server:

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-github"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "......"
      }
    }
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

目录