前言

在当今快速发展的AI应用生态中,我们从优化提示词到使用Tool Calling突破大模型语言生成的界限,实现调用外部系统完成实际任务。但随着工具数量的增长和应用场景的复杂化,传统的Tool Calling实现方式暴露出一系列挑战:工具接口格式不统一、模型需硬编码工具定义、跨平台兼容性差等问题日益凸显。

为了解决这些痛点,一个全新的概念——MCP应运而生。

什么是MCP

MCP全称为Model Context Protocol,即模型上下文协议。需要明确的是,MCP并不是Tool Calling的替代品,而是为其提供一个统一、可扩展、跨平台的连接基础设施。

简单来说,MCP就是Tool Calling的管理者:

  • Tool Calling:负责决策——模型决定要不要调用工具、调用哪个工具
  • MCP:负责规范——规定如何描述工具、如何发起调用、如何传递结果

有了MCP之后,AI模型只需要学习一种标准格式,所有的协议转换工作都由MCP Server负责完成,极大降低了多工具集成的复杂度。

MCP Java SDK 架构

MCP Java SDK提供了MCP协议的Java语言实现,支持通过同步和异步两种通信模式,与AI模型及各类工具进行标准化交互。

其核心架构可以用餐饮服务类比来理解:

  • Client/Server:分别代表请求的发起者和请求的接收者,如同顾客和后厨的关系
  • Session:负责管理会话的整个生命周期,相当于订单管理系统
  • Transport:定义消息传递的具体方式,就像手机订餐加上骑手送餐的完整配送链路

Transport传输协议

MCP Java SDK提供了多种"通信方式",让AI应用(客户端)能够灵活地与各种工具服务(服务器)进行对话,目前主要支持三种传输方式:

  1. 基于Stdio的进程间通信传输协议 通过标准输入/输出(stdin和stdout)在两个程序之间传递消息,高效直接,适合本地进程间通信

  2. 基于Java HttpClient的SSE客户端传输协议 基于Java HttpClient发起HTTP请求,并接收服务端持续发送的事件流,适合远程服务调用

  3. WebFlux SSE客户端传输协议 基于WebFlux的非阻塞、响应式编程模型处理SSE流,适合高并发场景

MCP Server与MCP Client

MCP Server

MCP Server是MCP协议的具体服务端实现,是对外提供特定功能的服务程序。开发者可以直接到MCP Server社区查找并调用所需的功能,目前活跃的社区资源包括:

  • GitHub MCP Registry:https://github.com/mcp/registry
  • MCP Flow:https://mcpflow.io/home
  • 火山引擎MCP广场:www.volcengine.com/mcp-marketplace

MCP Client

有了MCP服务端后,如何在代码中调用这些服务?这就需要借助MCP客户端。MCP客户端是连接本地开发环境或应用与远程MCP服务器之间的桥梁,不同客户端对MCP协议的支持程度各不相同,用户可以根据自己的需求选择合适的客户端。

MCP使用实战

在使用MCP Client之前,通常需要先运行一个或多个MCP Server实例。许多MCP Server是通过npx或uv命令启动的,在开始使用前,必须正确配置相应的运行环境。

第一步:准备MCP Server配置

可以到各大MCP社区获取相应的服务配置,以百度地图服务为例:

{
    "mcpServers": {
        "baidu-map": {
            "command": "npx",
            "args": [
                "-y",
                "@baidumap/mcp-server-baidu-map"
            ],
            "env": {
                "BAIDU_MAP_API_KEY": "xxx"
            }
        }
    }
}

第二步:Spring AI项目接入

除了通过Cursor等客户端使用外,我们也可以通过Spring AI代码来接入这些MCP资源。首先引入相关依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-ai-project</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mcp-client-demo</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud.ai</groupId>
                <artifactId>spring-ai-alibaba-bom</artifactId>
                <version>1.0.0.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

第三步:配置文件设置

配置application.yml文件:

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
    mcp:
      client:
        request-timeout: 60000
        stdio:
          servers-configuration: classpath:/mcp/mcp-servers-config.json

同时创建MCP服务配置文件mcp/mcp-servers-config.json

{
  "mcpServers": {
    "baidu-map": {
      "command": "npx.cmd",
      "args": ["-y", "@baidumap/mcp-server-baidu-map"],
      "env": {
        "BAIDU_MAP_API_KEY": "nLpm876DutMXGl6gEwzJksuya0gbfk0B"
      }
    }
  }
}

第四步:编写Controller

在Controller中集成MCP工具调用:

@RestController
@RequestMapping("/chat")
public class ChatController {
    private ChatClient chatClient;

    public ChatController(DashScopeChatModel chatModel, ToolCallbackProvider toolCallbackProvider) {
        this.chatClient = ChatClient.builder(chatModel)
                .defaultToolCallbacks(toolCallbackProvider)
                .build();
    }
    @RequestMapping("/generate")
    public String generate(String message){
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

ToolCallbackProvider是Spring AI中用于向AI模型注册和提供可用工具的核心接口。只要有MCP配置进来,ToolCallbackProvider就可以自动发现并调用相应工具,完美解决了Tool Calling中各个工具接口不同的问题,实现了动态工具组合。

自定义MCP Server开发

MCP主要支持两种通信方式:Stdio和Streamable HTTP。在2025年3月26日的协议更新后,官方已用更强大的Streamable HTTP取代SSE,解决了SSE连接容易断开的痛点。

方式一:使用Stdio自定义MCP Server

首先创建天气查询服务类:

package com.zc.mcp.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class WeatherService {

    private final Map<String, WeatherInfo> weatherCache = new ConcurrentHashMap<>();

    public WeatherService() {
        weatherCache.put("北京", new WeatherInfo("晴转多云", "22~28", "南风2级", "78"));
        weatherCache.put("上海", new WeatherInfo("小雨", "18~24", "东风3级", "52"));
        weatherCache.put("广州", new WeatherInfo("晴朗", "25~33", "微风", "45"));
        weatherCache.put("深圳", new WeatherInfo("多云", "24~30", "东北风2级", "60"));
    }

    @Tool(description = "查询指定城市的天气信息,返回天气状况、温度范围、风力、空气质量")
    public String queryWeather(
            @ToolParam(description = "城市名称,如:北京、上海、广州、深圳") String city) {

        log.info("[HTTP MCP] queryWeather 调用,城市: {}", city);

        if (!weatherCache.containsKey(city)) {
            return String.format("未找到「%s」的天气信息,支持的城市:%s",
                    city, weatherCache.keySet());
        }

        WeatherInfo info = weatherCache.get(city);
        return String.format("""
            🌍 %s天气报告
            ─────────────────
            ☁️ 天气:%s
            🌡️ 温度:%s°C
            💨 风力:%s
            🏭 AQI:%s
            ─────────────────
            🕐 更新时间:%s
            """,
                city, info.weather, info.temp, info.wind, info.aqi,
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
        );
    }

    record WeatherInfo(String weather, String temp, String wind, String aqi) {}
}

然后创建工具配置类:

package com.zc.mcp.config;

import com.zc.mcp.service.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ToolConfig {
    @Bean
    public ToolCallbackProvider getWeather(WeatherService weatherService){
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

配置application.yml:

spring:
  ai:
    mcp:
      server:
        name: stdio-server
        version: 0.0.1
  main:
    web-application-type: none
    banner-mode: off

最后打包生成jar包即可使用。

方式二:使用SSE自定义MCP Server

使用SSE传输策略时,依赖选择取决于角色定位:

Starter 角色 被谁调用
spring-ai-starter-mcp-server MCP Server 本地MCP Client(通过Stdio)
spring-ai-starter-mcp-server-webmvc MCP Server 远程MCP Client(通过HTTP/SSE)
spring-ai-starter-mcp-server-webflux MCP Server 远程MCP Client(通过HTTP/SSE/WebFlux)
spring-ai-starter-mcp-client MCP Client Stdio/SSE客户端
spring-ai-starter-mcp-client-webflux MCP Client WebFlux客户端

注意:在Web项目中同时存在Servlet和Reactive依赖时,Spring Boot会默认优先使用Servlet堆栈,导致WebFlux的/sse端点失效。可以通过以下配置解决:

server:
  port: 8081
spring:
  ai:
    mcp:
      server:
        name: sse-server
        version: 0.0.1
  main:
    web-application-type: reactive

SSE服务的pom.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mcp</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mcp-sse</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Service实现工具业务逻辑,Config将工具暴露给MCP Server(代码与Stdio方式类似)。Config中的MethodToolCallbackProvider就像一个"扫描器",把所有@Tool注解的方法收集起来"交给"MCP Server。

SSE服务启动后,在Cursor中配置即可使用:

{
  "mcpServers": {
    "sse-server":{
      "url":"http://127.0.0.1:8081/sse"
    }
  }
}

在Spring AI中作为MCP Client调用该服务的Controller示例:

@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient chatClient;

    @Autowired
    public ChatController(DashScopeChatModel chatModel,
                          SyncMcpToolCallbackProvider mcpToolProvider) {
        this.chatClient = ChatClient.builder(chatModel)
                .defaultToolCallbacks(mcpToolProvider.getToolCallbacks())
                .build();
    }

    @RequestMapping("/generate")
    public String generate(String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

配合TreeRouter提升开发效率

在MCP标准化工具调用的基础上,搭配TreeRouter API中转站能够进一步提升AI应用开发效率。TreeRouter作为专业的API聚合中转平台,提供了统一的大模型API接入方案,支持GPT、Gemini、Claude、DeepSeek、Qwen等主流AI模型的统一调用。

开发者无需对接多家厂商的不同接口,只需通过TreeRouter一套标准API即可接入所有主流模型,配合MCP的工具管理能力,能够快速构建功能强大、扩展性强的AI应用系统,真正实现"一次开发,多模型兼容"的高效开发体验。