Spring AI MCP 实践
本文用两个 Spring Boot 应用跑通一次工具调用。服务端发布加法工具 add,客户端通过 Streamable HTTP 发现并调用它,随后把同一个工具接入 ChatClient。手动调用只需要本机环境和 Maven 依赖。协议角色与调用时序见 MCP 协议与调用原理。
1. 准备环境
准备 JDK 17 或以上版本和 Maven 3.6.3 或以上版本。示例使用 Spring Boot 3.5.15 和 Spring AI 1.1.8,通过 Streamable HTTP 连接同一台机器上的两个应用。Spring AI 1.1.8 发布记录
2. 服务端发布 add 工具
新建 mcp-server 目录,写入 pom.xml。Spring AI BOM 统一管理相关 starter 的版本。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.15</version>
<relativePath/>
</parent>
<groupId>example</groupId>
<artifactId>mcp-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>在 src/main/resources/application.yml 中启用 Streamable HTTP。默认端点为 /mcp。Spring AI 服务端配置
server:
address: 127.0.0.1
port: 8080
spring:
ai:
mcp:
server:
protocol: STREAMABLE创建 src/main/java/example/server/ServerApplication.java。Spring AI 扫描 Spring Bean 上的 @McpTool,生成工具定义并注册方法。Spring AI 服务端注解
package example.server;
import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpToolParam;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
@SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@Component
class CalculatorTools {
@McpTool(name = "add", description = "返回两个整数的和")
public String add(@McpToolParam(description = "第一个整数") int a,
@McpToolParam(description = "第二个整数") int b) {
return Long.toString((long) a + b);
}
}3. 客户端发现并调用工具
新建 mcp-client 目录并复制上面的 pom.xml。把项目 artifactId 改为 mcp-client,把依赖 spring-ai-starter-mcp-server-webmvc 改为 spring-ai-starter-mcp-client。写入客户端的 src/main/resources/application.yml。Spring AI 客户端配置
spring:
main:
web-application-type: none
ai:
mcp:
client:
streamable-http:
connections:
calculator:
url: http://127.0.0.1:8080
endpoint: /mcp创建 src/main/java/example/client/ClientApplication.java。客户端 starter 默认创建同步 Client 并完成初始化。
package example.client;
import java.util.List;
import java.util.Map;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.spec.McpSchema;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args).close();
}
@Bean
CommandLineRunner invokeTool(List<McpSyncClient> clients) {
return args -> {
var client = clients.get(0);
var tools = client.listTools().tools().stream().map(McpSchema.Tool::name).toList();
System.out.println("tools = " + tools);
var request = McpSchema.CallToolRequest.builder()
.name("add")
.arguments(Map.of("a", 19, "b", 23))
.build();
var result = client.callTool(request);
if (Boolean.TRUE.equals(result.isError())) {
throw new IllegalStateException("工具执行失败 " + result.content());
}
System.out.println("result = " + ((McpSchema.TextContent) result.content().get(0)).text());
};
}
}在两个终端中,先进入 mcp-server 执行 mvn spring-boot:run。等端口 8080 启动后,再进入 mcp-client 执行同一命令。客户端应打印以下内容,然后退出。
tools = [add]
result = 424. 让模型触发同一个工具
在客户端加入模型 starter 并配置支持工具调用的模型。例如,使用 spring-ai-starter-model-openai 时,通过 SPRING_AI_OPENAI_API_KEY 提供凭证,并设置 spring.ai.openai.chat.options.model。将上面的 invokeTool 方法替换为下方片段,并导入 org.springframework.ai.chat.client.ChatClient 和 org.springframework.ai.mcp.SyncMcpToolCallbackProvider。Spring AI ChatClient、Spring AI MCP 客户端集成
@Bean
CommandLineRunner askModel(ChatClient.Builder builder, List<McpSyncClient> clients) {
return args -> {
var tools = SyncMcpToolCallbackProvider.builder().mcpClients(clients).build();
var chat = builder.defaultToolCallbacks(tools).build();
var answer = chat.prompt().user("请调用加法工具计算 19 加 23。").call().content();
System.out.println(answer);
};
}SyncMcpToolCallbackProvider 把 Server 发布的工具转换成 Spring AI 的工具回调。模型选择工具后,回调发起 tools/call,Spring AI 将返回值交给模型继续生成回答。由于模型也可能直接给出 42,确认触发时应观察 Server 上的 add 方法是否被执行。
5. 排查常见问题
- 初始化失败或收到 404。 确认服务进程已启动,客户端使用的是服务端支持的传输,且
url与endpoint拼接后指向真实端点。 - 连接成功但列表没有工具。 先看服务端是否声明工具能力,再检查工具类是否为 Spring Bean,以及方法是否已被扫描并注册。
- 手动调用成功,模型却没有触发。 检查模型是否支持工具调用、请求是否注册了工具回调,以及工具描述是否足够明确。
- 需要接入其他机器。 示例绑定在本机回环地址。对外提供服务时,在 HTTP 入口鉴权,并在工具执行前校验调用者权限和参数。对写入或删除操作,由 Host 提供用户确认,再由 Server 执行权限检查。MCP 工具安全要求
