Spring AI 2.0 MCP 协议实战:Model Context Protocol SDK 与多服务器编排
1. 从三套适配代码到一套协议Spring AI 2.0 MCP 到底解决什么问题如果你正在做 AI 应用落地大概率踩过这个坑OpenAI 有 function callingClaude 有 tool useGemini 又是另一套 declarative tools。接三个模型就得写三套工具适配代码。更麻烦的是企业内部那一堆能力孤岛——财务的智能报表、CRM 的客户洞察、知识库的语义检索每个都是独立服务想串给大模型调用全靠手写胶水代码。Model Context ProtocolMCP就是冲着这件事来的。它定义了一套标准化协议让 AI 模型以统一方式与外部工具和资源交互。Spring AI 2.0 把底层 MCP Java SDK 升级到了 0.17.2并引入McpSyncServerCustomizer、McpAsyncServerCustomizer等接口同时修复了非 Web 上下文下的自动配置问题。这篇聚焦一个具体目标用 Spring AI 2.0 的 Model Context Protocol SDK 搭一套多服务器编排骨架覆盖服务注册、工具发现与调用路由。我会给出可复制的依赖与配置片段演示本地启动多个 MCP 服务、验证工具列表与调用链路的完整动作。适合已经写过 Spring Boot、想快速把 MCP 跑起来的同学。MCP 的核心能力有四类Tools模型可调用的函数、Resources模型可读取的数据、Prompts预定义提示模板、Sampling服务器反调模型。多服务器编排主要围绕 Tools 展开因为工具发现和调用路由是编排的核心。MCP Java SDK 分三层Client/Server 层、Session 层、Transport 层。Transport 层负责 JSON-RPC 消息的序列化与传输Spring AI 目前支持 Stdio、SSE、Streamable HTTP 三种方式。Stdio 适合本地进程SSE 适合远程长连接Streamable HTTP 是 0.17.2 引入的新传输方式在保持无状态的同时提供流式响应能力。多服务器编排要解决三个问题服务注册每个 MCP Server 怎么被客户端发现、工具发现不同 Server 的工具如何统一暴露、调用路由LLM 决定调用某个工具时请求怎么落到正确的 Server。Spring AI 通过配置化的多连接 工具名前缀 ToolCallback 桥接把这三件事串了起来。2. TaoToken 前置给多服务器编排准备一个稳定的模型入口多服务器编排跑起来之后最终还是要让 LLM 来决定调用哪个工具。这时候模型入口的稳定性就很关键——如果模型侧频繁超时或鉴权失败你根本分不清是 MCP 编排的问题还是模型接入的问题。我试过用 TaoToken 作为模型接入层它的 API 地址是https://taotoken.net/api兼容 OpenAI 风格的调用方式。对于 Spring AI 来说你只需要把base-url指向它再配一个 API Key 就能用。这样做的好处是MCP 编排层和模型接入层解耦排查问题时可以分别验证。先拿到 API Key。访问https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_springai创建密钥复制出来备用。注意不要把它硬编码进代码用环境变量注入。然后在 Spring AI 的配置里指定 base-url 和 api-key。Spring AI 2.0 的 OpenAI starter 支持自定义 base-url这样就能把请求转发到 TaoToken 的兼容端点。模型 ID 按你实际使用的填写比如gpt-4o或claude-3-5-sonnet这类。如果你需要长期跑编码类 Agent 任务可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_springai。它更适合持续性的编码场景和本篇的多服务器编排可以配合使用。这里要强调一点TaoToken 在这里的角色是模型接入层不是 MCP Server 本身。MCP Server 是你自己写的、暴露内部能力的服务TaoToken 提供的是 LLM 的调用入口。两者职责不同不要混淆。配置好模型入口后你就可以专注于 MCP 编排本身了。接下来看具体的依赖和配置。3. 可复制配置依赖、application.yml 与多服务器连接片段这一节给出可以直接复制的配置。先看 Maven 依赖。Spring AI 的 MCP 集成分 Client 和 Server 两个维度各自有对应的 starter。dependencies !-- MCP 客户端连接外部 MCP 服务器 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency !-- MCP 服务器暴露本应用的工具能力 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server-webmvc/artifactId /dependency /dependencies版本管理通过 BOM 引入避免手动对齐版本号dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version2.0.0-M2/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagementSpring AI 的里程碑版本需要单独添加仓库repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories接下来是application.yml。这是多服务器编排的核心我把它拆成模型接入和 MCP 客户端两部分。模型接入指向 TaoTokenspring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o temperature: 0.7MCP 客户端部分同时连接本地 Stdio 服务和远程 SSE 服务演示混合编排spring: ai: mcp: client: enabled: true type: SYNC request-timeout: 30s toolcallback: enabled: true stdio: connections: local-fs: command: npx args: - -y - modelcontextprotocol/server-filesystem - /data/docs sse: connections: order-service: url: http://localhost:8081 sse-endpoint: /mcp/sse crm-service: url: http://localhost:8082 sse-endpoint: /mcp/sse如果你在 Windows 下跑 Stdionpx需要用cmd.exe /c包装stdio: connections: local-fs: command: cmd.exe args: - /c - npx.cmd - -y - modelcontextprotocol/server-filesystem - D:\\data\\docstoolcallback.enabled: true是关键配置。它让所有从 MCP Server 发现的工具自动注册为 Spring AI 的ToolCallback这样在ChatClient里就能直接调用不用手动桥接。如果你需要自定义客户端行为比如针对某个慢服务单独设超时可以实现McpSyncClientCustomizerComponent public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer { Override public void customize(String serverConfigurationName, McpClient.SyncSpec spec) { if (slow-external-api.equals(serverConfigurationName)) { spec.requestTimeout(Duration.ofSeconds(60)); } spec.toolsChangeConsumer(tools - System.out.println(Tools changed for [ serverConfigurationName ]: tools.stream().map(McpSchema.Tool::name).toList())); } }工具过滤也是多服务器编排里常用的。当某个 Server 暴露了太多工具你只想放行一部分可以实现McpToolFilterComponent public class SecurityToolFilter implements McpToolFilter { private static final SetString ALLOWED Set.of( query_orders, get_order_detail, search_products, read_file); Override public boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool) { if (tool.name().contains(write) || tool.name().contains(delete)) { return false; } return ALLOWED.contains(tool.name()); } }注意McpToolFilter在应用上下文里只能定义一个 Bean它会作用于所有 MCP 连接。命名冲突由McpToolNamePrefixGenerator处理默认会给工具名加上服务器前缀比如order-service__query_orders这样不同 Server 的同名工具不会打架。4. 验证请求本地启动多 MCP 服务并检查工具列表与调用链路配置写完了接下来验证。分三步启动 MCP Server、检查工具注册、发起一次真实调用。先写一个 MCP Server暴露订单查询工具。用注解方式最直观Component public class OrderMcpTools { private final OrderService orderService; public OrderMcpTools(OrderService orderService) { this.orderService orderService; } McpTool(name query_orders, description 根据客户ID查询订单列表) public ListOrderSummary queryOrders( McpToolParam(description 客户ID, required true) String customerId, McpToolParam(description 订单状态) String status) { return orderService.findByCustomerAndStatus(customerId, status) .stream() .map(o - new OrderSummary(o.getId(), o.getStatus(), o.getAmount())) .toList(); } McpTool(name get_order_detail, description 获取订单详细信息) public OrderDetail getOrderDetail( McpToolParam(description 订单ID, required true) String orderId) { Order order orderService.findById(orderId); return new OrderDetail(order.getId(), order.getCustomerName(), order.getItems(), order.getTotalAmount()); } }Server 侧的配置spring: ai: mcp: server: type: SYNC protocol: SSE server-info: name: order-mcp-server version: 1.0.0 annotation-scanner: enabled: true启动这个 Server它会在 8081 端口暴露 SSE 端点。再启动一个 CRM Server 在 8082暴露get_customer_info工具。两个 Server 都起来后回到客户端应用。客户端启动时Spring AI 会自动连接所有配置的 MCP Server并把工具注册到SyncMcpToolCallbackProvider。写一个CommandLineRunner打印注册结果Component public class ToolRegistryInspector implements CommandLineRunner { private final SyncMcpToolCallbackProvider toolProvider; public ToolRegistryInspector(SyncMcpToolCallbackProvider toolProvider) { this.toolProvider toolProvider; } Override public void run(String... args) { ToolCallback[] callbacks toolProvider.getToolCallbacks(); System.out.println( Registered MCP Tools ); for (ToolCallback cb : callbacks) { System.out.printf( - %s: %s%n, cb.getName(), cb.getDescription()); } } }预期输出类似 Registered MCP Tools - local-fs__read_file: Read the contents of a file - local-fs__list_directory: List directory contents - order-service__query_orders: 根据客户ID查询订单列表 - order-service__get_order_detail: 获取订单详细信息 - crm-service__get_customer_info: 获取客户信息看到前缀了吗local-fs__、order-service__、crm-service__这是默认前缀生成器自动加的跨服务器命名冲突就这样解决了。最后验证调用链路。把工具桥接进ChatClientConfiguration public class ChatClientConfig { Bean public ChatClient chatClient(ChatModel chatModel, SyncMcpToolCallbackProvider toolCallbackProvider) { return ChatClient.builder(chatModel) .defaultToolCallbacks(toolCallbackProvider.getToolCallbacks()) .build(); } }发起一次真实请求Service public class AssistantService { private final ChatClient chatClient; public AssistantService(ChatClient chatClient) { this.chatClient chatClient; } public String ask(String message) { return chatClient.prompt() .system(你是客服助手可以使用订单查询和客户信息工具。) .user(message) .call() .content(); } }调用ask(帮我查一下客户 C1001 的订单)LLM 会自动决定调用order-service__query_orders请求通过 MCP 协议落到订单 Server返回结果后再由模型组织成自然语言。整条链路用户输入 → ChatClient → LLM 决策 → ToolCallback → MCP Client → MCP Server → 业务服务 → 原路返回。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth多服务器编排跑起来的过程中报错基本集中在几个地方。这一节按真实报错对照排查。401 Unauthorized。这个最常见分两种来源。一种是模型侧TAOTOKEN_API_KEY没注入或写错检查环境变量是否生效可以在启动日志里确认 base-url 和 key 是否被正确读取。另一种是 MCP Server 侧如果 Server 配了鉴权客户端连接时没带 token也会 401。排查方法先用 curl 单独测模型端点再单独测 MCP Server 的 SSE 端点定位是哪一层。local proxy failed。这个报错通常出现在 Stdio 连接场景。原因一般是command或args写错子进程根本没起来。检查npx是否在 PATH 里Windows 下是否用了cmd.exe /c包装。还有一个容易忽略的点args里的路径如果不存在子进程会启动失败但报错信息不直观。先把命令复制到终端手动跑一遍确认能起来再放进配置。reading choices 相关报错。这类报错一般来自模型响应解析阶段说明请求发出去了但返回体不符合预期。常见原因是 base-url 配错比如漏了/api或者多了斜杠。TaoToken 的 API 地址是https://taotoken.net/apiSpring AI 会在后面拼接/v1/chat/completions这类路径所以 base-url 不要自己再加/v1。另外模型 ID 写错也会导致返回体异常确认你填的模型名在服务端是存在的。OAuth 相关报错。如果 MCP Server 用了 OAuth 鉴权客户端需要配置对应的认证信息。Spring AI 的 MCP Client 支持在连接配置里加 header。排查时先确认 token 是否过期再看 header 名称是否和服务端要求一致。有些服务端要求Authorization: Bearer xxx有些要求自定义 header对不上就会报 OAuth 错误。工具列表为空。配置看起来都对但getToolCallbacks()返回空数组。检查三点toolcallback.enabled是否为 trueannotation-scanner.enabled是否为 trueMCP Server 是否真的启动成功。可以在客户端启动日志里搜索连接相关的 INFO 日志确认每个 Server 的连接状态。工具名冲突。如果看到两个工具同名说明前缀生成器没生效。默认情况下 Spring AI 会加前缀但如果你自定义了McpToolNamePrefixGenerator并返回了空字符串就会冲突。检查是否有自定义的前缀生成器 Bean。排查的核心思路是分层验证模型层、MCP 连接层、工具注册层、调用层一层一层单独测不要一上来就端到端跑。6. 把编排跑稳之后模型入口与长期编码的配合多服务器编排骨架搭起来之后你会发现真正影响体验的往往是模型侧的稳定性。MCP 负责把工具串起来但决策调用哪个工具的是 LLM。如果模型入口不稳定编排再优雅也白搭。TaoToken 在这里的价值是提供一个统一的模型接入点。你可以在application.yml里把 base-url 指向https://taotoken.net/api用同一个 Key 切换不同模型不用改 MCP 编排层的代码。模型对话入口在https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_springai可以用来快速验证某个模型对工具调用的支持情况。如果你在搭的是长期运行的编码 AgentMCP 编排会涉及大量工具调用和上下文管理这时候 Coding Plan 更合适https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_springai。它针对持续性编码场景做了优化和本篇的多服务器编排可以配合。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_springai里面有完整的参数说明和示例。控制台在https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_springai可以查看调用量和余额。最后给一个实操建议先把 MCP 编排跑通用本地 Stdio 服务验证工具发现和调用链路确认没问题后再接入远程 SSE 服务。模型入口先用一个稳定的端点等编排稳定了再考虑多模型切换。这样排查问题时变量最少定位最快。

相关新闻

Python网络设备自动巡检脚本:Paramiko+Netmiko实现结构化健康报告

Python网络设备自动巡检脚本:Paramiko+Netmiko实现结构化健康报告

简介:本资源是一套基于Python实现网络设备自动化巡检的轻量级脚本工具包,面向IT运维工程师、网络管理员及Python初学者,解决大型网络环境中人工巡检效率低、易出错的核心痛点。资源共7个文件,含5个INI配置文件(分别定义…

2026/10/17 19:19:17 阅读更多 →
Comsol仿真SPP波导EIT:双模型设计与参数优化详解

Comsol仿真SPP波导EIT:双模型设计与参数优化详解

1. 项目概述:从标题说起“Comsol 中的 SPP 波导 EIT:探索两个有趣模型”,这个标题我第一眼看到就觉得很对胃口。它把三个关键词压缩在了一起:Comsol(多物理场仿真软件)、SPP 波导(表面等离激元波…

2026/10/17 19:20:59 阅读更多 →
C#串口测试程序实战:从SerialPort收发到粘包处理与协议验证

C#串口测试程序实战:从SerialPort收发到粘包处理与协议验证

简介:这是一份面向C#初学者与工控上位机开发者的串口调试测试程序源码,围绕串口收发、数据展示与数据库落库展开,适合需要快速验证串口通信逻辑、学习WinForm跨线程更新UI的开发者参考。资源包共37个文件,约140KB,包含…

2026/10/17 18:08:12 阅读更多 →

最新新闻

加载结果数据的工程化指南:从异步分页到弱网优化

加载结果数据的工程化指南:从异步分页到弱网优化

1. 从一句“加载结果数据”说起的真实场景如果你在代码仓库里翻到一个只有“加载结果数据”六个字的提交记录,第一反应大概率是困惑。这也太模糊了。可真正做过数据处理或者后台管理系统的人都会心一笑——这六个字背后,往往藏着一整套“从数据源取数&am…

2026/10/17 23:29:26 阅读更多 →
Unity URP Bloom后处理全解析:从原理到手写RenderFeature

Unity URP Bloom后处理全解析:从原理到手写RenderFeature

做渲染效果的时候,Bloom(泛光)大概是最容易快速提升画面质感的一项。低强度模式下,它能让霓虹灯、屏幕反光、金属高光边缘柔化出一圈渐变光晕;高强度模式下,又能把普通灯具变成那种梦幻的“发光体”。在 Un…

2026/10/17 23:29:26 阅读更多 →
Cocos2d-x LuaJIT二进制库链接避坑指南

Cocos2d-x LuaJIT二进制库链接避坑指南

简介:本资源是专为Cocos2d-x iOS平台开发者提供的libluajit.a兼容性修复库,聚焦解决cocos2d-x在iPhone 5S及更高型号设备上因LuaJIT架构不匹配导致的lua_open()调用崩溃问题。适用于中高级移动游戏开发工程师,尤其在使用旧版cocos2d-x集成Lua…

2026/10/17 23:29:26 阅读更多 →
大数据技术原理与应用测试题考点全解析与三轮复习法

大数据技术原理与应用测试题考点全解析与三轮复习法

简介:这套《大数据技术原理应用》测试题以docx文档形式整理,面向高校大数据专业学生、课程备考者及自学爱好者,用于系统检验大数据基础理论、关键技术与应用框架的掌握程度。文档共58页,包内仅1个文件,整体大小76KB&am…

2026/10/17 23:29:26 阅读更多 →
mmdetection模型解析:从配置文件到检测框的完整拆解

mmdetection模型解析:从配置文件到检测框的完整拆解

1. 从配置文件到检测框:mmdetection模型解析到底在解析什么刚接触mmdetection的人,十有八九会被它那套配置文件体系绕晕。打开一个configs/目录下的py文件,里面嵌套着model、data、schedule、runtime好几层字典,每个字典里又套着b…

2026/10/17 23:29:26 阅读更多 →
Oracle基金交易平台实战:七张表设计+PL/SQL全流程开发

Oracle基金交易平台实战:七张表设计+PL/SQL全流程开发

简介:面向Oracle数据库学习者的项目实战文档,聚焦基金交易平台的数据库设计,适合正在准备项目实训或课程设计的开发人员。文档以招商银行某分行的开放式基金交易系统为业务背景,先梳理基金管理、客户账户管理、交易审核等核心功能…

2026/10/17 23:28:25 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →