Spring AI MCP Client 实战:让 Java 后端通过 stdio 调用本地工具服务
摘要:本文用一个最小可落地的 Spring Boot 示例,讲清楚 Java 后端如何通过 Spring AI MCP Client 以 stdio 方式连接本地 MCP 工具服务。适合已经了解 Spring Boot,但还没把 MCP 真正接入 Java 项目的后端工程师。读完后你会知道依赖怎么加、配置怎么写、stdio server 怎么启动、工具如何暴露给 ChatClient,以及生产落地时最容易踩的超时、路径、进程和 Windows 命令包装问题。
验证状态说明:本文配置结构来自 Spring AI 官方 MCP Client Boot Starter 文档;代码为可落地的最小示例,需要根据你的 Spring AI 版本和模型供应商配置做本地回归。本文不使用公司内部日志或真实生产故障,所有日志均为模拟说明。
1. 为什么 Java 后端要关心 MCP Client
MCP(Model Context Protocol)可以把"外部工具能力"标准化暴露给大模型或 Agent。对 Java 后端来说,它最直接的价值不是概念,而是把已有系统能力接到 AI 应用里:
- 本地文件、脚本、知识库、搜索、工单、监控查询可以作为工具服务;
- Spring Boot 应用不用把所有工具逻辑写死在一个服务里;
- 工具服务可以独立进程运行,Java 侧只做连接、发现和调用;
- 后续可以从 stdio 平滑演进到 SSE / Streamable HTTP 这类远程传输方式。
在 Spring AI 里,MCP Client Boot Starter 已经提供了比较完整的自动配置能力:它可以管理多个 MCP client,支持 STDIO、SSE、Streamable HTTP 等连接方式,并能把 MCP 工具集成到 Spring AI 的 tool execution framework 中。
本文先讲最容易本地验证的 stdio 模式。
2. 整体调用链路
stdio 模式可以理解为:Spring Boot 启动时拉起一个本地子进程,这个子进程就是 MCP Server;双方通过标准输入输出交换 MCP 消息。
用户请求
-> Spring Boot Controller / Service
-> ChatClient
-> Spring AI Tool Callback
-> MCP Client
-> stdio transport
-> 本地 MCP Server 进程
-> 工具执行结果
-> 模型综合回答
和 HTTP 调用不同,stdio 的关键点在"进程生命周期"和"标准输入输出协议"。你的 Java 服务不仅要会调用工具,还要能正确启动、关闭、超时和观测这个本地工具进程。
3. 环境和依赖
本文示例环境建议如下:
| 项目 | 建议版本 / 说明 |
|---|---|
| JDK | 17 或 21 |
| Spring Boot | 3.x |
| Spring AI | 选择与你项目兼容的 1.x 版本 |
| 构建工具 | Maven / Gradle 均可 |
| MCP Server | 本地可执行命令,例如 npx、node、python、Java jar |
| 传输方式 | stdio |
Maven 依赖示例:
<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>
如果你使用 Spring AI BOM,版本通常放在 dependencyManagement 中统一管理:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
注意:Spring AI 版本更新较快,正式项目里不要只复制依赖名,先对照你当前版本的官方文档确认 artifact、BOM 版本和模型 starter 名称。
4. 用 application.yml 配置 stdio MCP Server
Spring AI 官方文档中,stdio 相关配置前缀是:
spring.ai.mcp.client.stdio
最小配置可以写成这样:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o-mini
mcp:
client:
enabled: true
name: spring-ai-mcp-client
version: 1.0.0
initialized: true
request-timeout: 20s
type: SYNC
toolcallback:
enabled: true
stdio:
connections:
filesystem:
command: npx
args:
- -y
- "@modelcontextprotocol/server-filesystem"
- "./workspace"
这段配置做了几件事:
- 启用 MCP Client;
- 使用同步 client,也就是
type: SYNC; - 开启 MCP 工具到 Spring AI Tool Callback 的集成;
- 定义一个名为
filesystem的 stdio 连接; - 通过
npx -y @modelcontextprotocol/server-filesystem ./workspace启动一个本地文件系统 MCP Server。
实际项目里你可以把 filesystem 换成自己的工具服务,例如:
spring:
ai:
mcp:
client:
stdio:
connections:
order-tool:
command: java
args:
- -jar
- ./tools/order-mcp-server.jar
env:
APP_ENV: dev
LOG_LEVEL: info
这里的 env 会作为 MCP Server 子进程的环境变量传入,适合放非敏感运行参数。敏感信息仍建议走密钥管理或环境变量注入,不要直接提交到 Git。
5. 使用外部 mcp-servers.json
如果你已经用过 Claude Desktop 或其他 MCP 客户端,也可以使用类似的 JSON 配置文件。
application.yml:
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:mcp-servers.json
src/main/resources/mcp-servers.json:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"./workspace"
]
}
}
}
这种方式的好处是跨客户端复用配置,坏处是 Java 项目中的环境差异更容易被忽略,比如工作目录、相对路径、Windows 命令包装等。
6. 在 ChatClient 里使用 MCP 工具
开启 spring.ai.mcp.client.toolcallback.enabled=true 后,Spring AI 可以把 MCP Server 暴露出来的工具接入 tool callback 体系。
一个典型 Service 可以这样写:
java
`package com.example.demo.ai;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class McpChatService {
private final ChatClient chatClient;
public McpChatService(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
public String ask(String question) {
return chatClient.prompt()
.system("你是一个 Java 项目助手。需要读取本地项目文件时,优先使用已接入的 MCP 工具。")
.user(question)
.call()
.content();
}
}
`
Controller 示例:
java
`package com.example.demo.web;
import com.example.demo.ai.McpChatService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class McpDemoController {
private final McpChatService mcpChatService;
public McpDemoController(McpChatService mcpChatService) {
this.mcpChatService = mcpChatService;
}
@GetMapping("/ai/ask")
public String ask(@RequestParam String q) {
return mcpChatService.ask(q);
}
}
`
启动后可以用 curl 验证:
curl "http://localhost:8080/ai/ask?q=请读取 workspace 目录下的 README.md 并总结"
预期现象是:模型在需要文件内容时,会触发 MCP filesystem 工具调用,再基于工具返回内容组织答案。
7. 启动和验证日志应该看什么
第一次接入时,不要上来就调业务接口,先看启动日志和 MCP client 初始化状态。
你应该重点确认三类信息:
模拟日志示例:
INFO o.s.ai.mcp.client - Creating MCP stdio connection: filesystem
INFO o.s.ai.mcp.client - Starting MCP server command: npx -y @modelcontextprotocol/server-filesystem ./workspace
INFO o.s.ai.mcp.client - MCP client initialized: filesystem
INFO o.s.ai.tool - Registered MCP tools: filesystem.read_file, filesystem.list_directory
如果工具服务启动失败,常见日志会接近下面这样:
ERROR o.s.ai.mcp.client - Failed to start MCP stdio server: filesystem
java.io.IOException: Cannot run program "npx": error=2, No such file or directory
这类问题通常不是 Spring AI 本身的问题,而是运行环境找不到命令。解决方向:
- 用绝对路径指定 command;
- 确认启动 Spring Boot 的用户能执行该命令;
- 在 systemd、Docker、IDEA 三种运行方式下分别检查 PATH;
- Node / Python / Java 工具服务尽量明确版本和路径。
8. Windows 下最容易踩的 cmd.exe 包装问题
Spring AI 官方文档专门提醒过 Windows stdio 配置:npx、npm、node、python 等在 Windows 上可能是 .cmd 批处理文件,而 Java ProcessBuilder 不能像 shell 一样直接执行这些批处理命令。
Windows 下建议写成:
{
"mcpServers": {
"filesystem": {
"command": "cmd.exe",
"args": [
"/c",
"npx",
"-y",
"@modelcontextprotocol/server-filesystem",
"C:\\Users\\jerry\\workspace"
]
}
}
}
Linux / macOS 则可以直接:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"./workspace"
]
}
}
}
如果你的团队同时支持 Windows 和 Linux,建议在配置层做区分,或者用代码按 OS 生成 ServerParameters,不要把 Windows 的命令写法硬塞给所有环境。
9. 生产落地时的 6 个边界
stdio 模式适合本地工具和单机 sidecar,但不等于生产里可以随便拉进程。下面几个点建议在上线前确认。
9.1 工具进程生命周期
Spring Boot 应用重启时,MCP Server 子进程要能跟着关闭。否则很容易出现旧进程占资源、新进程连不上、工具状态错乱。
检查点:
- 应用停止后,MCP Server 子进程是否退出
- 重启应用时是否会残留多个 server
- Docker / systemd 下 SIGTERM 是否能传递到子进程
9.2 请求超时
官方配置里有 spring.ai.mcp.client.request-timeout。不要使用无限等待。
建议:
spring:
ai:
mcp:
client:
request-timeout: 10s
如果工具本身可能执行慢任务,把慢任务改成异步任务 ID + 查询结果,而不是让一次工具调用卡住整个模型请求。
9.3 工具权限
MCP Server 能访问什么目录、什么接口、什么命令,要明确限制。filesystem 这类工具尤其要注意:
不要把 /、/home、生产配置目录、密钥目录直接暴露给 MCP 工具。
更合理的方式是只暴露一个工作目录,例如:
./workspace
9.4 工具命名冲突
多个 MCP Server 同时接入时,工具名可能冲突。Spring AI MCP Client 支持工具过滤和工具名前缀生成相关能力,实际项目里应给不同 server 使用清晰命名,例如:
filesystem.read_file
ticket.query_ticket
metrics.query_prometheus
9.5 可观测性
至少记录三类日志:
- MCP Server 启动命令和连接名,不记录密钥
- 工具调用耗时、成功/失败、错误类型
- 模型请求和工具调用的 traceId
不要把完整 prompt、用户隐私、文件内容和密钥直接打进日志。
9.6 从 stdio 演进到远程传输
stdio 更适合本地 demo、开发工具、sidecar。团队化和多实例部署时,SSE / Streamable HTTP 往往更好治理:
| 维度 | stdio | SSE / Streamable HTTP |
|---|---|---|
| 部署方式 | 跟应用同机子进程 | 独立服务 |
| 调试成本 | 本地简单,线上进程问题较多 | 网络链路更清晰 |
| 扩展性 | 适合单实例或 sidecar | 适合多应用复用 |
| 权限控制 | 依赖本机进程权限 | 可加网关、鉴权、限流 |
| 适用场景 | 本地工具、脚本、文件系统 | 团队工具平台、共享能力 |
10. 一个更工程化的配置样例
下面是一个稍微完整一点的配置,适合放到开发环境验证:
server:
port: 8080
spring:
application:
name: spring-ai-mcp-client-demo
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o-mini
mcp:
client:
enabled: true
name: ${spring.application.name}
version: 1.0.0
initialized: true
request-timeout: 10s
type: SYNC
toolcallback:
enabled: true
stdio:
connections:
filesystem:
command: npx
args:
- -y
- "@modelcontextprotocol/server-filesystem"
- "./workspace"
env:
NODE_ENV: development
logging:
level:
org.springframework.ai: INFO
org.springframework.ai.mcp: DEBUG
本地验证步骤:
mkdir -p workspace
printf "hello mcp from spring boot\n" > workspace/README.md
./mvnw spring-boot:run
curl "http://localhost:8080/ai/ask?q=读取 README.md,然后用一句话总结"
如果你没有配置可用的大模型供应商,MCP client 仍可能初始化成功,但 ChatClient 调用会失败。排查时要分清楚两层:
MCP Server 是否启动成功
模型调用是否成功
不要把模型 API Key 问题误判成 MCP 配置问题。
11. 常见问题排查清单
| 现象 | 可能原因 | 排查方式 |
|---|---|---|
Cannot run program "npx" |
PATH 中没有 npx | 使用绝对路径;检查运行用户环境变量 |
| Windows 下启动失败 | .cmd 不能被 ProcessBuilder 直接执行 |
用 cmd.exe /c npx ... 包装 |
| 应用启动卡住 | MCP Server 启动后没有按协议响应 | 单独运行 server 命令;检查 stdout/stderr |
| 工具列表为空 | toolcallback 未启用或 server 未暴露工具 | 检查 toolcallback.enabled 和 server 日志 |
| 调用超时 | 工具执行慢或模型等待工具结果 | 缩短 request-timeout;给工具加超时和降级 |
| 本地可用,Docker 不可用 | 容器里没有 npx/node/文件路径 | 在镜像中安装依赖;改用绝对路径或挂载目录 |
| 工具能读到敏感文件 | 暴露目录过大 | 只暴露工作目录;加白名单和权限控制 |
12. 总结
Spring AI MCP Client 的价值在于:Java 后端不需要自己发明一套工具调用协议,就可以把本地工具、脚本和外部能力接到 AI 应用里。stdio 模式非常适合做第一版验证,因为它配置少、反馈快,也容易用本地文件系统或脚本服务跑通闭环。
但如果要进入生产环境,重点就不再是"能不能调通",而是:工具进程是否可控、权限是否收敛、超时是否明确、日志是否可观测、不同操作系统下启动命令是否一致。尤其是 Windows 的 cmd.exe /c 包装、Docker 中的 PATH、MCP Server 子进程残留,这些都是 Java 后端接 stdio 工具服务时最容易忽略的地方。
如果你关注 Java 后端、Spring Boot、Spring AI、MCP/Agent 落地和线上问题排查,可以关注我的 CSDN 专栏,后面会继续拆更工程化的 Java AI 应用实践。