
1. 为什么 SpringAI 里 MCP 和 Tools 总让人绕晕刚接触 SpringAI 的时候我一度以为 MCP 和 Tools 是二选一的关系要么用Tool注解写本地工具要么接一个 MCP Server。直到在本地联调一个「查订单 查天气」的 Agent 时才发现这俩根本不是竞争关系而是同一套工具调用能力在不同部署粒度上的两种形态。先把结论摆出来Tools 是 Spring 应用进程内的工具方法MCP 是把这类通用工具抽出来单独部署成一个服务再通过 MCP Client 远程调用。换句话说MCP 解决的是「工具复用和跨进程共享」Tools 解决的是「当前应用内的能力暴露」。两者最终都会被 SpringAI 的ChatClient当成可调用工具来编排。这个场景特别适合本地开发联调你有一个 Spring Boot 服务里面既有业务专属的 Tools又想复用别人已经写好的 MCP Server 能力。如果 Key 和 API 通道各配一套联调时改来改去非常痛苦。这篇就围绕「用 TaoToken 统一 Key 跑通配置骨架」来写给出application.yml和config.toml的可复制骨架再附上启动日志和调用返回的验证动作帮你把 MCP 与 Tools 的边界和配合方式彻底厘清。适合谁看正在用 SpringAI 写 Agent、手里有本地 Tools 又想接 MCP Server、并且希望模型调用通道统一管理的后端同学。下面所有配置都可以直接抄改掉 Key 就能跑。2. TaoToken 前置统一 Key 与 API 通道的接入位置在讲配置之前先把 TaoToken 在这套骨架里的角色说清楚。它提供的是统一的模型 API 通道和 Key 管理SpringAI 里无论是本地 Tools 触发的模型调用还是 MCP Client 转发过来的请求最终都要走同一个模型入口。把 Key 收敛到一处联调时就不用每个模块单独维护凭证。你需要先拿到一个可用的 API Key。进入控制台创建即可控制台入口https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteAPI Key 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite创建完 Key 之后模型调用的 Base URL 统一用https://taotoken.net/api这个地址不加 UTM直接作为base-url写进配置。如果你后面要跑长期编码或 Agent 任务可以顺带了解 Coding PlanCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite这里有个容易踩的坑很多人把 MCP Server 的地址和模型 API 的地址混在一起配。记住分工——MCP Server 地址是工具服务的地址TaoToken 的base-url是模型推理的地址两者是不同层的东西。MCP Client 负责把工具描述传给模型模型决定调用哪个工具真正执行工具的是 MCP Server 或本地 Tools。提示Key 建议通过环境变量注入不要硬编码进application.yml提交到仓库。下面骨架里我用${TAOTOKEN_API_KEY}占位。3. 可复制配置application.yml 与 config.toml 骨架这一节是全文的核心直接给两份可复制的配置骨架。先看 Spring Boot 侧的application.yml它负责模型通道、本地 Tools 开关和 MCP Client 的连接信息。spring: ai: openai: # TaoToken 统一模型通道 base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.3 mcp: client: enabled: true # 本地联调stdio 方式启动的 MCP Server stdio: connections: local-tools: command: npx args: - -y - modelcontextprotocol/server-filesystem - /tmp/mcp-workspace # 如果 MCP Server 是独立 HTTP 服务用下面这段 sse: connections: remote-tools: url: http://localhost:8081/mcp/sse # 本地 Tools 扫描包 app: tools: scan-package: com.example.springai.tools几个关键点解释一下。spring.ai.openai.base-url指向 TaoToken 的 API 地址这是所有模型调用的唯一出口。spring.ai.mcp.client下面同时给了stdio和sse两种连接方式本地联调时按你的 MCP Server 启动方式二选一即可不需要的都注释掉。app.tools.scan-package是我自定义的本地 Tools 扫描包配合Tool注解使用。再看 MCP Server 侧的config.toml骨架。如果你用的是支持 TOML 配置的 MCP Server比如某些自研或社区实现可以这样写[server] name local-tools-server version 0.1.0 transport stdio [model] # MCP Server 内部若需调用模型同样走 TaoToken 统一通道 base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY default_model gpt-4o-mini [tools] # 声明本 Server 对外暴露的工具 enabled [read_file, write_file, list_dir] [tools.read_file] description 读取指定路径的文件内容 input_schema { path string } [tools.write_file] description 向指定路径写入内容 input_schema { path string, content string } [tools.list_dir] description 列出目录下的文件 input_schema { path string }这份config.toml的语义是MCP Server 自己声明暴露哪些工具以及它内部如果需要模型能力也复用同一个 TaoToken 通道。这样整个链路里模型 Key 只有一份联调时改一处即可。本地 Tools 的写法保持 SpringAI 原生风格方便和 MCP 工具对照Component public class OrderTools { Tool(description 根据订单号查询订单状态) public String queryOrderStatus(String orderId) { // 本地业务逻辑直接查库 return 订单 orderId 状态已发货; } }到这里MCP 与 Tools 的边界就很清楚了OrderTools是进程内方法随应用启动即注册config.toml里声明的工具是独立进程/服务通过 MCP Client 连接后动态注册。两者最终都进入同一个工具注册表交给ChatClient编排。4. 验证请求启动日志与调用返回配置写完怎么确认 MCP 和 Tools 都挂上了先看启动日志。应用起来后控制台应该能看到类似下面的输出INFO o.s.a.mcp.client.McpClientAutoConfiguration : Registered MCP client: local-tools INFO o.s.a.mcp.client.McpClientAutoConfiguration : Discovered 3 tools from MCP server local-tools INFO c.e.s.tools.OrderTools : Registered local tool: queryOrderStatus INFO o.s.a.chat.client.DefaultChatClient : ChatClient initialized with base-urlhttps://taotoken.net/api重点看两行Discovered 3 tools from MCP server说明 MCP Server 的工具被成功拉取Registered local tool说明本地 Tools 也注册成功。如果 MCP 那行没出现八成是连接配置或 Server 没起来。接着写一个最小的验证接口把本地 Tool 和 MCP Tool 一起交给模型RestController public class AgentController { private final ChatClient chatClient; public AgentController(ChatClient.Builder builder, OrderTools orderTools) { this.chatClient builder .defaultTools(orderTools) .build(); } GetMapping(/agent/ask) public String ask(RequestParam String q) { return chatClient.prompt() .user(q) .call() .content(); } }请求http://localhost:8080/agent/ask?q帮我查下订单 A100 的状态并列出 /tmp/mcp-workspace 里的文件预期返回会同时体现两类工具的调用结果订单 A100 状态已发货。 /tmp/mcp-workspace 目录下有readme.md、data.json。如果返回里只出现了订单状态没有目录列表说明 MCP 工具没被模型选中或没注册成功。这时候打开模型对话页面手动验证一下工具描述是否正常模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite在对话里直接问「你有哪些可用工具」能直观看到 MCP 工具和本地 Tools 是否都在列表里。5. 本篇常见错排查联调阶段最容易卡在几个固定位置我按出现频率排一下。第一个是base-url写错。有人把 TaoToken 的地址写成带/v1或带具体路径的形式导致 404。正确写法就是https://taotoken.net/apiSpringAI 会自己拼接后续路径。如果报Connection refused先确认网络能通到这个域名。第二个是 MCP Server 启动方式不匹配。application.yml里配了stdio但你的 Server 实际是 HTTP 服务日志里就不会有Discovered tools。反过来配了sse但 Server 是命令行启动的同样连不上。本地联调建议先用stdio因为不占端口、随应用生命周期管理。第三个是工具重名。本地 Tools 里有个read_fileMCP Server 也暴露了read_file注册时可能互相覆盖。排查方法是看启动日志里工具总数对不对或者给本地工具加前缀比如local_read_file。第四个是 Key 没注入。${TAOTOKEN_API_KEY}如果环境变量没设启动时不会报错但第一次调用会返回 401。验证方式是启动后直接打一次/agent/ask看返回是不是鉴权失败。Key 的创建和查看都在 API Keys 页面API Keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite第五个是模型选错。有些模型对工具调用的支持不完整返回里不会触发 tool call。联调阶段先用支持 function calling 的模型确认链路通了再换。注意MCP Client 拉取工具是启动时一次性完成的改了 MCP Server 的工具声明后要重启 Spring 应用热更新不会重新拉取。6. 把统一 Key 用在长期编码与 Agent 任务上本地联调跑通之后如果你打算把这套骨架用到长期的编码助手或 Agent 任务里建议把模型通道和工具通道彻底解耦模型走 TaoToken 统一 Key工具走 MCP Server 独立部署。这样换模型不用动工具配置加工具也不用改模型凭证。接入文档里有更完整的参数说明和示例遇到配置项不确定的时候可以直接查接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite如果你跑的是长时间编码或 Agent 循环任务Coding Plan 那条通道会更合适Key 和额度管理都在同一处Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite回到 MCP 和 Tools 的关系最后用一句话收束Tools 是「当前进程能做什么」MCP 是「把能做的事抽出去给别人用」而 TaoToken 统一 Key 解决的是「不管谁调用模型入口只有一个」。三者配合起来本地联调时你只需要维护一份 Key、一份模型地址剩下的就是工具声明和业务逻辑。