1. 从 Function Call 到 MCP:工具规模化后的管理难题
如果你正在学 AI Agent,大概率已经写过 Function Call 的 demo:定义几个 JSON Schema,模型返回tool_calls,代码里 if-else 路由到对应函数。两个工具的时候很爽,二十个工具的时候就开始难受了。
我试过在一个 Spring Boot 项目里手写十几个工具的 Schema,改一个参数名要同步改三处:Schema 定义、路由分支、执行方法。更麻烦的是工具发现——新同事接手时根本不知道系统里有哪些工具可用,只能翻代码。这就是 Function Call 的边界:它解决的是"模型能不能调用工具",但不解决"工具多了怎么管"。
MCP(Model Context Protocol,模型上下文协议)要解决的就是后者。你可以把它理解成大模型世界的 USB 接口:工具只要实现 MCP Server,任何支持 MCP 的 Host(Claude Desktop、Cursor、你自己的 Spring AI 应用)都能调用它,不用为每个工具单独写适配代码。
MCP 的架构分三层。Host 是用户直接使用的 AI 应用,负责接收输入、调用模型、协调工具调用;Client 是 Host 内部的通信组件,负责和 Server 建立连接、获取工具列表、发送调用请求;Server 是真正提供工具的一方,声明自己有哪些工具、执行逻辑、返回结果。一个 Host 可以挂多个 Client,每个 Client 连一个 Server,这样就能把 HR 系统、订单系统、知识库的能力组合起来。
MCP 定义了三种核心能力:Tools(工具调用,最常用)、Resources(资源访问,偏读取资料)、Prompts(提示词模板,可复用)。实际项目里 Tools 占九成以上,Resources 和 Prompts 了解即可。
传输方式有两种。Stdio 走本地进程标准输入输出,适合个人开发和本地工具,不暴露端口、安全性高;Streamable HTTP 走远程 HTTP 服务,适合团队共享和生产环境,能利用 HTTP 生态做认证、监控、负载均衡。个人本地用 Stdio,团队和生产用 Streamable HTTP,这是基本判断。
MCP 和 Function Call 不是替代关系。Function Call 是发动机,让模型能输出调用意图;MCP 是整车,负责工具定义、发现、通信、复用。Spring AI 则是 MCP 协议的一种 Java 实现方式——MCP 是标准,Spring AI 是方便你写 MCP Server 的框架,类比 HTTP 协议和 Spring MVC 的关系。
这一篇的目标很具体:把 Spring AI 的 endpoint 从默认地址改到 TaoToken 统一通道,完成一次 MCP 工具调用闭环。你会拿到可复制的application.yml配置、curl 验证命令,以及常见报错的排查路径。适合正在学 AI Agent、手上有 Java 项目、想把工具调用跑通的开发者。
2. TaoToken 前置准备:统一 Key 与 API 通道配置
在动手改 Spring AI 配置之前,先把 TaoToken 这边的准备工作做完。这一步不复杂,但顺序别搞反,否则后面调试会多花时间。
首先打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册账号。注册流程就是常规的邮箱加密码,不展开。登录后进入控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,在 API Keys 页面创建一个新的 Key。创建时建议给 Key 起个能认出来的名字,比如spring-ai-mcp-dev,方便后面区分环境。
Key 创建后只显示一次,复制下来存到安全的地方。如果你用.env文件管理,可以写成TAOTOKEN_API_KEY=sk-xxxx的形式;如果直接写进application.yml,注意别把配置文件提交到公开仓库。
接下来确认你要用的模型 ID。在模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 可以试跑一下,确认模型能正常返回。常用的模型 ID 比如claude-sonnet-4-20250514、gpt-4o这类,具体以控制台里列出的为准。MCP 工具调用对模型的 function calling 能力有要求,选支持工具调用的模型。
Base URL 统一用https://taotoken.net/api,注意这个地址不加 UTM 参数,直接写进配置即可。完整的接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,里面有各语言 SDK 的接入示例,Java 部分可以参考。
如果你打算长期跑编码类 Agent 任务,可以看一下 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,按套餐走比按量计费更划算。只是做 MCP 入门验证的话,按量付费就够了。
这里有个容易踩的坑:Key 的权限范围。创建 Key 时如果选了受限权限,可能只能调部分模型。MCP 工具调用需要模型支持 function calling,如果 Key 绑定的模型不支持,后面会报模型能力相关的错误。建议先用默认权限跑通,再按需收紧。
准备工作清单:
- 注册并登录 TaoToken
- 在 API Keys 页面创建 Key 并保存
- 在模型对话页面确认模型 ID 可用
- 记下 Base URL:
https://taotoken.net/api - 确认 Key 有调用目标模型的权限
这些做完,就可以进入 Spring AI 的配置环节了。
3. 可复制配置:application.yml 与 MCP Client 接入
这一节是核心操作部分。Spring AI 从 1.0 版本开始提供了 MCP Client 的 starter,我们用它来连接 MCP Server,同时把模型 endpoint 指向 TaoToken。
先看依赖。在pom.xml里加入 Spring AI 的 MCP Client starter 和 OpenAI 兼容的模型 starter:
<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里引入spring-ai-bom,版本选 1.0.0 或更高。MCP Client starter 会自动带上 MCP 协议实现和 Stdio/HTTP 两种传输的支持。
然后是application.yml。这是最关键的一段,路径和字段名要和 Spring AI 的约定一致:
spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-sonnet-4-20250514 temperature: 0.7 mcp: client: enabled: true name: spring-ai-mcp-client version: 1.0.0 type: SYNC request-timeout: 30s stdio: connections: hr-server: command: java args: - -jar - ./mcp-servers/hr-server.jar streamable-http: connections: order-server: url: http://localhost:8081 endpoint: /mcp几个字段说明一下。base-url指向 TaoToken 的 API 地址,注意结尾不要多加斜杠。api-key用环境变量注入,避免硬编码。model填你在控制台确认过的模型 ID。mcp.client.type选SYNC表示同步调用,入门阶段够用;如果要异步可以改ASYNC。
stdio.connections下面配本地 MCP Server,command是启动命令,args是参数。这里假设你有一个打包好的hr-server.jar,它实现了 MCP 协议、暴露了getUserAnnualLeave等工具。streamable-http.connections配远程 MCP Server,url加endpoint拼出完整地址。
如果你用的是 Claude Code 或 Cline 这类工具,配置格式会不一样。Claude Code 的 MCP 配置在~/.claude/claude_desktop_config.json或项目级.mcp.json里,格式是:
{ "mcpServers": { "hr-server": { "command": "java", "args": ["-jar", "./mcp-servers/hr-server.jar"] }, "order-server": { "url": "http://localhost:8081/mcp" } } }Cline 的 MCP 配置在 VS Code 的settings.json里,走cline.mcpServers字段,结构类似。Codex 的auth.json则是另一套,主要配 API Key 和 Base URL,MCP 部分在config.toml里。这三件套的核心都是 Base URL、Key、Model ID 三个要素,只是载体不同。
回到 Spring AI。配置写完后,写一个配置类把ChatClient和 MCP 工具注册进去:
@Configuration public class McpConfig { @Bean public ChatClient chatClient(ChatClient.Builder builder, ToolCallbackProvider toolCallbackProvider) { return builder .defaultToolCallbacks(toolCallbackProvider) .build(); } }ToolCallbackProvider是 Spring AI 自动装配的,它会从 MCP Client 拉取所有已连接 Server 的工具列表,转成 Spring AI 的ToolCallback。这样模型在对话时就能看到这些工具,并按需调用。
注意defaultToolCallbacks这个方法名,不同小版本可能有差异,以你引入的 Spring AI 版本为准。如果编译报错找不到方法,去接入文档里核对一下当前版本的 API。
配置阶段最容易出问题的地方是 YAML 缩进。spring.ai.mcp.client下面的层级比较深,缩进错了启动时会报绑定失败。建议用 IDE 的 YAML 插件校验,或者启动后看日志里有没有McpClientAutoConfiguration相关的报错。
4. 验证请求:curl 与 MCP 工具调用闭环
配置写好了,怎么确认真的通了?分两步:先用 curl 验证 TaoToken 通道本身能通,再跑 Spring AI 应用验证 MCP 工具调用闭环。
第一步,curl 验证模型通道。这条命令直接打 TaoToken 的 chat completions 接口:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "user", "content": "你好,请回复 OK"} ] }'正常返回是一个 JSON,choices[0].message.content里会有模型回复。如果返回 401,说明 Key 不对或没带上;如果返回 404,检查 base-url 后面有没有多写/v1——TaoToken 的 base-url 是https://taotoken.net/api,SDK 会自动拼/v1/chat/completions,curl 手动拼的时候要写全。
第二步,验证带工具调用的请求。这条 curl 模拟模型看到工具定义后返回 tool_calls 的场景:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "user", "content": "帮我查一下 user_123 还剩几天年假"} ], "tools": [ { "type": "function", "function": { "name": "getUserAnnualLeave", "description": "查询用户年假余额", "parameters": { "type": "object", "properties": { "userId": {"type": "string", "description": "用户ID"} }, "required": ["userId"] } } } ] }'如果模型支持 function calling,返回的choices[0].message里会有tool_calls字段,function.name是getUserAnnualLeave,arguments里是{"userId": "user_123"}。这说明模型正确识别了工具并输出了调用意图。
第三步,跑 Spring AI 应用。启动你的 Spring Boot 项目,日志里应该能看到 MCP Client 连接 Server 的记录,类似Connected to MCP server: hr-server,以及拉取到的工具列表。然后写一个简单的 Controller 或 CommandLineRunner 触发对话:
@RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient = chatClient; } @GetMapping("/chat") public String chat(@RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } }访问http://localhost:8080/chat?message=帮我查一下user_123还剩几天年假,观察返回。如果 MCP 工具调用成功,返回内容里会包含年假天数,同时日志里能看到 MCP Client 向 Server 发送tools/call请求、Server 返回结果的记录。
整个闭环是:用户提问 → Spring AI 把工具列表发给模型 → 模型返回 tool_calls → Spring AI 通过 MCP Client 调用 Server → Server 执行工具返回结果 → Spring AI 把结果回传给模型 → 模型生成最终回答。这条链路跑通,MCP 入门就算完成了。
验证阶段建议开 DEBUG 日志,在application.yml里加:
logging: level: org.springframework.ai: DEBUG io.modelcontextprotocol: DEBUG这样能看到 MCP 协议的完整交互过程,对理解协议细节很有帮助。
5. 常见报错排查:401、local proxy failed 与 OAuth 问题
跑不通的时候,报错信息往往不够直白。这一节把几个高频错误和排查路径列出来。
401 Unauthorized。最常见的原因是 Key 没传对。检查三处:环境变量TAOTOKEN_API_KEY有没有导出、application.yml里${TAOTOKEN_API_KEY}有没有被正确解析、Key 有没有多余空格。如果是 curl 报 401,确认Authorization头是Bearer sk-xxx格式,Bearer 后面有一个空格。还有一种情况是 Key 被禁用或过期,去控制台 API Keys 页面确认状态。
local proxy failed / connection refused。这个报错通常出现在 MCP Client 连本地 Server 的时候。如果是 Stdio 传输,检查command和args能不能在命令行里手动跑通——先java -jar ./mcp-servers/hr-server.jar看能不能启动。如果是 Streamable HTTP,检查url和endpoint拼出来的地址能不能 curl 通。端口被占用也会报这个错,换个端口试试。
reading choices 相关报错。比如Error reading choices from response或choices is null。这通常是响应体格式和 SDK 预期不一致。先看 curl 返回的原始 JSON 结构,确认choices字段存在且是数组。如果 curl 正常但 SDK 报错,检查 Spring AI 版本和 OpenAI 兼容层的匹配度。TaoToken 的接口是 OpenAI 兼容格式,Spring AI 的 OpenAI starter 应该能直接解析。如果用的是其他 starter,可能需要调整。
OAuth 相关报错。比如OAuth token endpoint returned 400或invalid_client。MCP 的 Streamable HTTP 传输支持 OAuth 认证,如果你的 Server 配了 OAuth 但 Client 没配对应的 client_id/secret,就会报这个。入门阶段建议先用无认证的本地 Server 跑通,再逐步加认证。如果 Server 端不需要 OAuth,检查 Client 配置里有没有误开 OAuth 相关选项。
工具列表为空。MCP Client 连上了 Server,但ToolCallbackProvider拉不到工具。检查 Server 端有没有正确声明工具——MCP 协议要求 Server 在初始化时返回tools/list的结果。如果 Server 是用 Spring AI 写的,确认@Tool注解的方法所在的类被注册成了 Bean。另外,spring.ai.mcp.client.enabled要设为true,否则自动装配不生效。
模型不返回 tool_calls。curl 带 tools 参数但模型只返回普通文本。这通常是模型不支持 function calling,或者 tools 参数格式不对。确认你选的模型在 TaoToken 控制台里标注了支持工具调用。另外,有些模型对 tools 的 JSON Schema 有要求,parameters必须是合法的 JSON Schema,type必须是object。
排查通用思路:先 curl 验证通道,再单独验证 MCP Server,最后合起来跑。分层排查比一上来就调整个链路快得多。日志级别开到 DEBUG,MCP 协议的交互过程会打出来,能看到具体是哪一步断了。
如果 401 和连接问题都排除了,但工具调用还是不通,去接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 核对一下当前版本的配置示例,有时候是 starter 版本和配置字段对不上。
6. 继续深入:从 MCP 入门到 Agent 实战
跑通一次 MCP 工具调用闭环之后,下一步可以往几个方向深入。
一是多 Server 组合。把 HR Server、Order Server、Knowledge Server 都挂上,让模型根据问题自动选择调用哪个。这时候你会遇到工具命名冲突、权限隔离、调用链路追踪的问题,这些正是 MCP 相比裸 Function Call 的优势所在——Server 端可以独立管理自己的工具和权限,Client 端只负责发现和调用。
二是传输方式切换。本地开发用 Stdio 很方便,但团队共享要换成 Streamable HTTP。把 Server 部署成一个 HTTP 服务,加上认证和监控,Client 端改一下streamable-http.connections的配置就行。这个切换过程能帮你理解 MCP 协议的分层设计。
三是和 RAG 结合。用户问"我的订单 12345 能不能退",需要同时查订单状态和退货政策。订单状态走 Order MCP Server,退货政策走 Knowledge MCP Server,模型综合两个结果生成回答。这就是从单纯 RAG 升级到 Agent 的路径。
四是可观测性。MCP 调用次数、耗时、失败率、调用链路,这些在生产环境很重要。可以在 Client 端加拦截器埋点,或者用 MCP 协议本身的日志能力。Spring AI 的 MCP Client 支持自定义McpClientCustomizer,可以在里面加监控逻辑。
如果你打算长期做 Agent 开发,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 按套餐走比按量计费省心,尤其是频繁调试工具调用的时候。模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 可以快速试不同模型的工具调用表现,找到最适合你场景的那个。
最后说一个实际经验:MCP 入门阶段别追求一次配齐所有 Server。先用一个最简单的本地 Stdio Server 跑通闭环,确认模型能正确返回 tool_calls、Client 能正确调用、结果能正确回传。这个最小闭环跑通后,再加第二个 Server、再换 HTTP 传输、再加认证。每加一个变量就验证一次,比一次性配一堆然后对着报错猜要快得多。
工具调用的本质是让模型从"只会说"变成"能办事"。MCP 把这个能力标准化了,Spring AI 把它 Java 化了,TaoToken 把模型通道统一了。三者串起来,你手上的 Java 项目就有了接入 Agent 生态的基础。