1. Spring AI MCP 接入到底卡在哪
Spring AI 的 MCP 接入本身并不复杂,真正让人卡住的地方往往不是代码,而是配置。MCP 协议让 Spring AI 项目可以调用外部工具,比如文件系统操作、地图查询、数据库读取等,原理是通过ToolCallbackProvider统一管理所有外部工具。项目启动时,Spring 会把 properties 里配置的 MCP 工具封装进SyncMcpToolCallbackProvider,你只需要依赖注入就能拿到全部工具。
听起来很顺,但实际动手时会遇到几个典型问题:base-url 填什么、api-key 放哪里、stdio 和 SSE 两种连接方式怎么选、工具调用链路怎么验证。尤其是当你想用一个统一的 Key 来打通多个模型和工具调用时,配置项散落在不同地方,排查起来很费劲。
这篇就聚焦这个场景:在 Spring AI 项目里通过 MCP 协议接入外部工具,用 TaoToken 统一 Key 和 API 通道,把 base-url 与 api-key 的配置骨架写清楚,再给一次完整的工具调用验证动作和预期返回。适合已经在写 Spring Boot + Spring AI、准备接 MCP 工具的开发者,也适合想先跑通链路再深入原理的人。
2. TaoToken 前置准备:统一 Key 与通道
在配置 MCP 之前,先把模型侧的通道准备好。Spring AI 的 ChatClient 需要一个可用的模型服务地址和 Key,TaoToken 在这里扮演的是统一入口的角色:一个 Key 同时覆盖模型对话和后续工具调用链路,不用为每个服务单独维护一套凭证。
你需要做两件事:
第一,拿到 API Key。访问https://taotoken.net/api-keys(带 utm 的完整链接是https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite),在控制台里创建一个 Key,复制保存。这个 Key 后面会同时用在模型配置和 MCP 相关请求里。
第二,确认 base-url。TaoToken 的 API 入口是https://taotoken.net/api,注意这个地址不加 UTM 参数,直接作为 Spring AI 的 base-url 使用。如果你用的是 OpenAI 兼容模式,Spring AI 的spring.ai.openai.base-url就填这个值。
提示:Key 只显示一次,建议创建后立刻存到环境变量或配置中心,不要硬编码进代码仓库。
如果你还想先验证模型通道是否通,可以打开模型对话页面https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite发一条消息,确认 Key 和通道正常,再往下配 MCP。长期做编码或 Agent 场景的话,Coding Plan 页面https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite里有更细的额度说明,可以按需看。
3. 可复制配置:pom 依赖与 application.properties 骨架
先把依赖补齐。除了 Spring AI 的基础 starter,MCP Client 的依赖必须单独加:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency>然后是模型侧配置,用 TaoToken 的 base-url 和 Key:
spring.ai.openai.base-url=https://taotoken.net/api spring.ai.openai.api-key=${TAOTOKEN_API_KEY} spring.ai.openai.chat.options.model=gpt-4o-mini这里TAOTOKEN_API_KEY建议通过环境变量注入,避免明文。接下来是 MCP 的两种连接方式配置。
stdio 方式适合本地工具,比如文件系统 MCP 服务器,通过子进程启动并交互:
spring.ai.mcp.client.stdio.connections.server1.command=npx spring.ai.mcp.client.stdio.connections.server1.args[0]=-y spring.ai.mcp.client.stdio.connections.server1.args[1]=@modelcontextprotocol/server-filesystem spring.ai.mcp.client.stdio.connections.server1.args[2]=/Users/yourname/Pictures spring.ai.mcp.client.stdio.connections.server1.env=SSE 方式适合远程 MCP 服务器,通过 URL 连接:
spring.ai.mcp.client.sse.connections.server1.url=https://your-mcp-server.example.com spring.ai.mcp.client.sse.connections.server1.sse-endpoint=/sse?key=YOUR_MCP_KEY注意 SSE 的sse-endpoint里如果带 key,替换成你自己的。两种方式在 Spring AI 里的使用方式完全一致,区别只在配置。
控制器侧,把SyncMcpToolCallbackProvider注入进来,挂到 ChatClient 上:
@RestController @RequestMapping("/mcp") public class McpClientController { private final ChatClient chatClient; private final SyncMcpToolCallbackProvider toolCallbackProvider; McpClientController(ChatClient.Builder chatClientBuilder, SyncMcpToolCallbackProvider toolCallbackProvider) { this.chatClient = chatClientBuilder.build(); this.toolCallbackProvider = toolCallbackProvider; } @RequestMapping(value = "/stdio/file", produces = MediaType.TEXT_HTML_VALUE + ";charset=UTF-8") public String stdio(String userInput) { return this.chatClient.prompt() .toolCallbacks(toolCallbackProvider) .user(userInput) .call() .content(); } }这段骨架就是 MCP 工具调用的核心:toolCallbacks(toolCallbackProvider)把配置里所有 MCP 工具一次性挂上,模型在需要时会自动选择调用。
4. 验证请求:一次工具调用的完整动作与预期返回
配置写完后,启动 Spring Boot 项目。启动日志里会看到 MCP 客户端初始化的信息,如果 stdio 配置正确,会看到子进程启动;SSE 配置正确则会看到连接建立。
先验证工具列表是否被加载。加一个简单的 Advisor 打印工具名:
public class SimpleLoggerAdvisor implements CallAdvisor { private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class); @Override public String getName() { return this.getClass().getSimpleName(); } @Override public int getOrder() { return 99; } @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { OpenAiChatOptions options = (OpenAiChatOptions) request.prompt().getOptions(); options.getToolCallbacks().stream().forEach( toolCallback -> logger.info("tool-toolName: {}", toolCallback.getToolDefinition().name()) ); return chain.nextCall(request); } }把它加到调用链里,启动后发一次请求,控制台会打印出所有已注册的 MCP 工具名。如果这里为空,说明 MCP 配置没生效,回到第 5 节排查。
然后发一次真实的工具调用请求。以 stdio 文件系统为例,浏览器访问:
http://localhost:8080/mcp/stdio/file?userInput=列出当前文件夹下的所有文件预期返回是模型根据工具调用结果生成的文本,比如列出目录下的文件名列表。同时控制台会打印工具调用的名称、参数和结果。如果你加了可观测性依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>再实现一个ObservationHandler,就能看到更详细的调用详情:
@Component public class ToolCallingObservationHandler implements ObservationHandler<ToolCallingObservationContext> { private static final Logger logger = LoggerFactory.getLogger(ToolCallingObservationHandler.class); @Override public void onStop(ToolCallingObservationContext context) { logger.info("tool calling completion: \ntool calling name: \n{} \ntool calling arguments:\n{} \ntool calling result: \n{}", context.getToolDefinition().name(), context.getToolCallArguments(), context.getToolCallResult()); } @Override public boolean supportsContext(Observation.Context context) { return context instanceof ToolCallingObservationContext; } }看到tool calling name、arguments、result三段都打印出来,就说明整条链路通了:模型识别意图 → 选择 MCP 工具 → 执行 → 返回结果 → 模型组织语言输出。
5. 本篇常见错排查
配置过程中最容易踩的坑集中在几个地方,按出现频率排一下。
工具列表为空。最常见的原因是 MCP 依赖没加,或者 properties 里的连接名写错。stdio 的connections.server1和 SSE 的connections.server1是两套独立配置,如果你同时配了两种,注意不要重名。另外 stdio 的command如果填npx,确保本机 Node.js 环境可用,否则子进程起不来。
base-url 或 Key 报 401/403。检查spring.ai.openai.base-url是否填了https://taotoken.net/api,注意不要多加路径。Key 是否通过环境变量正确注入,可以在启动日志里确认配置加载。如果 Key 有空格或换行,也会导致鉴权失败。
SSE 连接超时。SSE 需要保持长连接,如果网络环境不稳定或服务端不支持,会一直重连。可以先用 curl 测一下sse-endpoint是否可达。另外 Spring AI 1.0.0 版本对 Streamable Http 支持还不完整,如果你用的是新协议,暂时只能等版本更新或改用 SSE。
工具被调用但结果不对。这通常是工具参数传递问题。看ToolCallingObservationHandler打印的arguments,确认模型传的参数是否符合工具定义。比如文件路径参数如果传了相对路径,而 MCP 服务器要求绝对路径,就会失败。
中文乱码。控制器上加了produces = MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"基本能解决。如果还有问题,检查请求头里的Accept-Charset。
6. 接入文档与后续动作
MCP 接入的骨架就是这些:依赖、base-url、api-key、连接配置、控制器注入、验证请求。stdio 用于本地工具,SSE 用于远程工具,Spring AI 里使用方式一致,按需配置即可。
如果你在排障或接入过程中卡住,建议先看接入文档https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,里面有 base-url 和 Key 的详细说明。需要重新生成或管理 Key 的话,API Keys 页面https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite可以直接操作。验证模型通道是否正常,用模型对话https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite发一条消息最快。长期做编码或 Agent 场景,Coding Planhttps://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite里有额度说明可以参考。
最后提醒一句:MCP 工具调用链路跑通后,建议先把SimpleLoggerAdvisor和ToolCallingObservationHandler留着,调试期能省很多时间。等稳定了再按需精简。