资讯动态

MCP 都用上 Spring AI MCP!从零搭建可复用的工具调用链路

发布时间:2026/10/18 6:18:10 来源:尧图企业网站定制
1. 为什么你的 Spring AI 项目需要一个 MCP 工具调用链路如果你正在用 Spring AI 做 AI 应用大概率遇到过这个尴尬模型能聊天但一让它「读一下我桌面的文件」「查一下本机配置」它就开始一本正经地胡说。原因很简单——大模型本身没有手脚它只能生成文本真正去执行文件读写、系统查询、调用外部接口的得靠工具Tool / Function Calling。MCPModel Context Protocol就是把这个「给模型装手脚」的过程标准化了。它定义了一套服务端和客户端之间的通信协议服务端负责暴露工具能力客户端负责把工具注册给模型模型在对话中自主决定调哪个工具、传什么参数。Spring AI 从 1.0 版本开始对 MCP 做了比较完整的支持提供了spring-ai-mcp-server-spring-boot-starter和spring-ai-mcp-client-webflux-spring-boot-starter两个核心依赖让你用注解和配置就能把链路搭起来。这篇文章面向的是想真正把 MCP 跑通的 Java 开发者。我会用一个本地 Java 项目做场景拆成两条线一条是「消费别人的 MCP 服务」比如 filesystem 这种现成的一条是「自己写一个 MCP 服务」获取电脑配置最后把两条线接在一起做一次端到端验证。全程给出可复制的 pom、JSON 配置、Tool注册代码和测试类你跟着敲就能跑。适合谁看有 Spring Boot 基础、用过 Spring AI 的 ChatClient、但还没把 MCP 从概念落到工程里的人。如果你连 ChatClient 都没用过建议先把官方 quickstart 跑一遍再回来不然配置部分会有点懵。先说清楚一个容易混淆的点MCP 服务端和客户端是两个独立进程。服务端可以是 Java、Python、Node.js 写的只要遵循 MCP 协议客户端你的 Spring AI 应用通过 stdio 或 SSE 跟它通信。所以「搭建可复用链路」的本质是把服务端的工具能力通过一份配置稳定地注入到客户端的 ChatClient 里。下面分步来。2. TaoToken 前置准备给 ChatClient 配一个稳定的模型入口在写 MCP 代码之前得先解决模型从哪来的问题。Spring AI 的 ChatClient 需要一个ChatModel实现你可以接官方 OpenAI也可以接兼容 OpenAI 协议的网关。我这边习惯用 TaoToken 做统一入口原因是它同时提供 OpenAI 兼容接口和 Claude Code 这类编码场景的接入方式切换模型只改一个 model 字段不用动代码结构。你需要准备三样东西我把它叫做「三件套」后面所有配置都围绕它展开Base URLhttps://taotoken.net/api这是 OpenAI 兼容协议的根地址Spring AI 的OpenAiApi会往这个地址拼/v1/chat/completions。API Key去控制台创建一个格式通常是sk-开头。创建入口在 API Keys建议按项目建独立的 key方便后面排查是哪个应用在调。Model ID比如gpt-4o、claude-sonnet-4-5这类。具体支持哪些可以在 模型对话 页面直接试确认能出结果再写进配置。把这三个值写进application.ymlSpring AI 的 OpenAI starter 会自动读取spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o temperature: 0.7这里用${TAOTOKEN_API_KEY}从环境变量读别把 key 硬编码进仓库。本地跑的时候在 IDE 的 Run Configuration 里加环境变量或者用.env配合 spring-dotenv。如果你更习惯用 Claude Code 做编码辅助TaoToken 也提供了对应的接入方式配置逻辑和上面一致只是走的协议不同。文档在 接入文档里面有 ClaudeCodeAnthropic 的完整参数说明。我建议先把 OpenAI 兼容这条线跑通因为 Spring AI 对它的支持最成熟MCP 的工具调用也是基于这条线验证的。有一点要提醒MCP 的工具调用会显著增加 token 消耗因为每次对话都要把工具定义name、description、参数 schema塞进上下文。所以模型选择上优先选支持 function calling 且上下文够大的。如果你只是本地测试用便宜的小模型也能验证链路是否通等确认没问题再换大模型。3. 可复制配置MCP 客户端与服务端的最小闭环这一节是全文的核心我会给出两份配置一份是客户端引入外部 MCP 服务filesystem一份是服务端自己暴露工具电脑配置。两份配置的路径和字段都按实际项目写你直接复制改路径即可。3.1 客户端 pom 与 mcp-servers-config.json先建一个 Spring Boot 项目pom 里加两个依赖。注意 Spring AI 的版本要用 1.0.0 及以上MCP 的 starter 才稳定dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-webflux-spring-boot-starter/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency然后在src/main/resources下建mcp-servers-config.json内容如下。这个文件是 Spring AI 约定的 MCP 服务清单客户端启动时会读取并建立连接{ mcpServers: { filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, /Users/root/Desktop, /Users/root/Desktop ] } } }逐字段解释一下这几个参数配错了是最常见的坑filesystem服务名称客户端里用它区分不同 MCP 服务日志里也会打这个名字。command: npx用 Node.js 的 npx 来拉起这个 npm 包。前提是你本机装了 Node.jsnpx -v能出版本号。args里的-y表示包不存在时自动同意安装避免卡在交互式确认。后面两个路径分别是允许读和允许写的目录。Windows 用户必须写绝对路径比如D:\\workspace\\demo而且 JSON 里反斜杠要转义成\\。Mac/Linux 用/Users/xxx/Desktop这种。如果你不想用 filesystem想找更多现成的 MCP 服务可以去 smithery.ai 或 glama.ai/mcp/servers 逛上面有大量社区维护的服务端覆盖数据库、浏览器、Git 等场景。选的时候注意看它支持的传输方式stdio 还是 SSEstdio 的配置就是上面这种 command args 的形式。3.2 服务端 pom 与 Tool 注册现在写自己的 MCP 服务端。新建一个 Spring Boot 项目pom 加dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId /dependency服务实现类用Tool注解暴露方法。这个注解是 Spring AI 识别工具的关键description 会作为工具描述传给模型模型靠它判断什么时候调用Slf4j Service public class ComputerService { Tool(description 获取电脑配置) public ComputerFunctionResponse queryConfig(ComputerFunctionRequest request) { log.info(获取电脑配置信息 {}, request.getComputer()); Properties properties System.getProperties(); String osName properties.getProperty(os.name); String osVersion properties.getProperty(os.version); String osArch properties.getProperty(os.arch); String userName properties.getProperty(user.name); String userHome properties.getProperty(user.home); String userDir properties.getProperty(user.dir); String javaVersion properties.getProperty(java.version); ComputerFunctionResponse response new ComputerFunctionResponse(); response.setOsName(osName); response.setOsVersion(osVersion); response.setOsArch(osArch); response.setUserName(userName); response.setUserHome(userHome); response.setUserDir(userDir); response.setJavaVersion(javaVersion); return response; } }然后在启动类里把工具注册成ToolCallbackProvider这一步是把Tool方法真正挂到 MCP 服务端的工具列表上Slf4j SpringBootApplication public class McpServerComputerApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(McpServerComputerApplication.class, args); } Bean public ToolCallbackProvider computerTools(ComputerService computerService) { return MethodToolCallbackProvider.builder() .toolObjects(computerService) .build(); } Override public void run(String... args) { log.info(mcp server computer success!); } }toolObjects可以传多个 service比如ComputerService、FileService一起塞进去它们的所有Tool方法都会暴露。打包用mvn clean package产物是target/mcp-server-computer-1.0.0.jar。3.3 把自建服务接回客户端服务端 jar 打出来后回到客户端的mcp-servers-config.json追加一段{ mcpServers: { filesystem: { command: npx, args: [-y, modelcontextprotocol/server-filesystem, /Users/root/Desktop, /Users/root/Desktop] }, mcp-server-computer: { command: java, args: [ -Dspring.ai.mcp.server.stdiotrue, -jar, /Users/fuzhengwei/Applications/apache-maven-3.8.4/repository/cn/bugstack/mcp/mcp-server-computer/1.0.0/mcp-server-computer-1.0.0.jar ] } } }关键参数是-Dspring.ai.mcp.server.stdiotrue它告诉服务端用 stdio 传输而不是默认的 SSE。客户端通过标准输入输出跟这个 jar 进程通信所以 jar 路径必须是绝对路径且文件真实存在。到这里客户端同时挂了两个 MCP 服务filesystem 负责文件操作mcp-server-computer 负责系统信息查询。4. 验证请求一次端到端跑通工具调用配置写完得验证链路真的通了。写一个测试类注入ChatClient.Builder和ToolCallbackProvider把工具挂到 ChatClient 上Slf4j SpringBootTest public class MCPTest { Resource private ChatClient.Builder chatClientBuilder; Autowired private ToolCallbackProvider tools; Test public void test_tool() { String userInput 有哪些工具可以使用; var chatClient chatClientBuilder .defaultTools(tools) .defaultOptions(OpenAiChatOptions.builder() .model(gpt-4o) .build()) .build(); System.out.println( QUESTION: userInput); System.out.println( ASSISTANT: chatClient.prompt(userInput).call().content()); } }先跑test_tool问模型「有哪些工具可以使用」。如果链路正常模型会列出 filesystem 和 computer 相关的工具名。这一步验证的是工具注册是否成功——如果模型说「我没有工具」说明defaultTools(tools)没生效或者 MCP 服务没连上。接着跑工作流测试让模型组合两个工具完成一个任务Test public void test_workflow() { String userInput 获取电脑配置在 /Users/root/Desktop 文件夹下创建 电脑.txt把电脑配置写入 电脑.txt; var chatClient chatClientBuilder .defaultTools(tools) .defaultOptions(OpenAiChatOptions.builder() .model(gpt-4o) .build()) .build(); System.out.println( QUESTION: userInput); System.out.println( ASSISTANT: chatClient.prompt(userInput).call().content()); }这个请求会触发多轮工具调用模型先调queryConfig拿到系统信息再调 filesystem 的写文件工具创建电脑.txt并写入内容。跑完后去桌面看文件应该真实存在内容就是本机的 OS、Java 版本等信息。控制台日志里能看到获取电脑配置信息这行说明服务端被真正调用了。实测下来第一次跑最容易卡在 npx 下载 filesystem 包上网络慢的话会等十几秒。如果超时可以提前手动npx -y modelcontextprotocol/server-filesystem跑一次把包缓存下来。另外模型有时候会「偷懒」不调工具直接编答案这时候把 temperature 调低一点或者在 prompt 里明确说「必须调用工具获取真实数据」命中率会高很多。5. 本篇常见错排查401、local proxy failed 与 reading choicesMCP 链路涉及进程间通信和模型调用报错点比较分散。我把几个高频错误和对应解法列出来你对照日志定位。401 Unauthorized模型调用被拒。先检查spring.ai.openai.api-key是否读到了环境变量echo $TAOTOKEN_API_KEY确认非空。再确认 base-url 是https://taotoken.net/api末尾不要多加/v1Spring AI 会自己拼。如果 key 没问题还是 401去 API Keys 看这个 key 是否被禁用或额度耗尽。local proxy failed / Connection refused客户端连不上 MCP 服务端。stdio 模式下检查command指向的可执行文件在 PATH 里npx -v、java -version都能正常输出。jar 路径写错是最常见的日志里会明确说Cannot run program。Windows 用户特别注意 JSON 里的路径转义D:\a\b.jar要写成D:\\a\\b.jar。reading choices 相关报错通常是模型返回体解析失败。可能是模型 ID 写错或者该模型不支持 function calling。换一个明确支持工具调用的模型再试。也有可能是响应被截断检查max-tokens是否设得太小。OAuth / 认证失败如果你接的是需要 OAuth 的 MCP 服务部分远程 SSE 服务需要在配置里加 token 字段。stdio 的本地服务一般不需要。遇到这类报错先确认服务端的认证方式别盲目加参数。工具被调用但结果为空检查Tool方法的返回对象是否可序列化字段有没有 getter。Spring AI 会把返回值转成 JSON 塞回模型如果序列化失败模型收到的是空对象就会说「没拿到数据」。排查顺序建议先看模型调用是否通401 类再看 MCP 进程是否起来proxy 类最后看工具执行结果序列化类。日志级别调到 DEBUGlogging.level.org.springframework.aiDEBUG能看到完整的请求和响应体定位快很多。6. 把链路变成可复用资产从单次验证到长期编码跑通一次不代表能长期用。要让 MCP 链路变成可复用的资产有几个工程化动作值得做。第一把mcp-servers-config.json里的路径参数外置成环境变量。Spring AI 支持在 JSON 里用${VAR}占位这样同一份配置在 Mac、Windows、CI 上都能用不用改文件。第二给每个 MCP 服务加健康检查客户端启动时如果某个服务连不上日志要明确报出来而不是静默失败——静默失败会导致模型「假装」有工具排查起来很痛苦。第三工具描述Tool的 description要写清楚输入输出模型靠它决策描述模糊会导致误调用。如果你打算把 MCP 用在日常编码或 Agent 场景比如让模型自动读代码库、跑测试、查文档那模型调用量会比较大建议用 Coding Plan 这类面向长期编码的套餐比按次调用划算。配置方式跟前面一样只是 key 和 model 换成套餐对应的值。最后说个我踩过的坑MCP 服务端的 jar 更新后客户端不会自动重连得重启客户端进程。所以本地开发时改完服务端代码重新打包后记得把客户端也重启一遍否则你调的还是旧工具。这个坑不报错只是行为不符合预期很容易怀疑人生。链路搭好之后你可以继续往上叠把 filesystem 换成数据库 MCP让模型查真实数据或者把多个自建服务组合成一个工具集做成团队内部的 MCP 网关。核心结构不变——服务端暴露Tool客户端用 JSON 挂载ChatClient 用defaultTools注入。把这三步固化成模板下一个 MCP 服务接入就是十分钟的事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑