拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从原理到示例:Java开发玩转MCP——Spring AI Alibaba生态实战指南(TaoToken统一Key接入篇)

从原理到示例:Java开发玩转MCP——Spring AI Alibaba生态实战指南(TaoToken统一Key接入篇)

1. 为什么 Java 后端需要 MCP:从接口方言到统一工具协议

如果你写过几年 Spring Boot,大概率经历过这种场景:业务系统里已经有一堆现成的 Service,比如订单查询、库存校验、天气接口、内部工单系统。现在产品说想接个大模型,让用户用自然语言就能查数据、触发操作。你第一反应是写 Function Calling,结果发现每个模型厂商的 JSON Schema 格式略有差异,工具描述要重复写好几遍,换一个模型就得改一轮适配层。

MCP(Model Context Protocol)想解决的就是这件事。你可以把它理解成 AI 世界的 USB-C 接口:工具提供方按统一协议暴露能力,模型侧按统一协议发现和调用工具,中间不需要为每个模型单独写胶水代码。对 Java 开发者来说,它的价值不在于“又一个新协议”,而在于你现有的 Spring Bean 可以很低成本地变成大模型可调用的工具。

这篇面向的是已经有 Java 后端经验、想把本地工具能力暴露给大模型的开发者。我会用 Spring Boot + Spring AI Alibaba 走一遍完整链路:先写一个 MCP Server 把天气查询工具注册出去,再写一个 MCP Client 让对话请求自动触发工具调用,最后把模型 endpoint 切到 TaoToken 统一 Key 通道,用一次真实请求验证工具调用是否成功返回。全程给出可复制的 pom 依赖、application.yml 配置和示例代码,你跟着敲就能跑通。

先说清楚 MCP 在 Java 侧的两个角色。MCP Server负责暴露工具,它可以是 Stdio 模式(本地进程通信,适合开发调试),也可以是 SSE 模式(HTTP 长连接,适合远程服务)。MCP Client负责连接 Server、拉取工具列表,并把工具注册进 ChatClient,这样用户提问时模型就能自动决定调哪个工具。Spring AI Alibaba 在这两层都提供了 starter,省掉了手写协议解析的活。

我试过把公司内部一个工单查询接口包成 MCP 工具,从写代码到对话验证成功,大概花了四十分钟,其中一半时间在调依赖版本。所以下面我会把版本对齐这件事讲细一点,避免你在NoSuchMethodError上卡住。

核心检索词先摆出来:Java 接入 MCP、Spring AI Alibaba MCP Server、Spring Boot MCP Client、TaoToken 统一 Key。这几个词贯穿全文,你搜资料时也可以按这个方向找。

2. TaoToken 前置:统一 Key 通道与模型 endpoint 准备

在写代码之前,先把模型侧的通路准备好。Spring AI Alibaba 默认会去连各家模型的官方 endpoint,但如果你想像我一样,用一套 Key 管理多个模型、并且方便在 Claude、Qwen、DeepSeek 之间切换做工具调用测试,可以把 endpoint 指到 TaoToken 的统一通道。

TaoToken 在这里扮演的是统一 API 入口:你拿到一个 Key,就能通过兼容 OpenAI 协议的接口访问多个模型。对 Spring AI 来说,这意味着你只需要配base-url和api-key,模型名换成对应的 Model ID 即可,代码层几乎不用动。

具体操作路径是这样的:先到官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册账号,然后进控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。创建完记得复制保存,页面刷新后就不再完整显示了。

这里有个细节要注意:Spring AI Alibaba 的 OpenAI 兼容配置里,base-url一般填到/v1这一层,而 TaoToken 的 API 根地址是 https://taotoken.net/api 。所以你在 yml 里写的完整 base-url 应该是https://taotoken.net/api,具体路径拼接由框架的 OpenAI 客户端处理。如果你用的是其他框架,记得确认它是否会自动补/v1/chat/completions。

模型选择上,工具调用能力比较稳的可以选 Claude 系列或 Qwen 系列。你可以在模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 先手动测一下模型能不能正常返回,确认 Key 有效再进代码环节。这一步别省,我见过太多人代码写完报 401,最后发现是 Key 复制时带了空格。

如果你后面打算长期做编码类 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/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,遇到协议细节可以对照查。

前置准备清单:一个可用的 API Key、确认 base-url 为https://taotoken.net/api、选好一个支持工具调用的 Model ID。这三样齐了,下面直接进配置。

3. 可复制配置:pom 依赖、application.yml 与 MCP Server 代码

这一节是全文的技术核心,我给的都是能直接粘贴的片段。先对齐版本:Spring Boot 用 3.3.x,Spring AI Alibaba 用 1.0.0-M6.1 附近的版本,Spring AI 用 1.0.0-M6。版本错位是 MCP starter 报错的头号原因,建议你先在 pom 里统一管理。

先看 pom.xml 的依赖部分。MCP Server 和 Client 的 starter 要分开引,另外加上 OpenAI 兼容的模型 starter:

<properties> <java.version>17</java.version> <spring-boot.version>3.3.5</spring-boot.version> <spring-ai.version>1.0.0-M6</spring-ai.version> <spring-ai-alibaba.version>1.0.0-M6.1</spring-ai-alibaba.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MCP Server:把本地工具暴露出去 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- MCP Client:连接 Server 并注册工具 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- OpenAI 兼容模型接入 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> </dependencies>

接下来是 application.yml。这里同时配了模型通道和 MCP Server 的 SSE 模式。注意base-url指向 TaoToken,api-key换成你自己的:

server: port: 8080 spring: application: name: mcp-demo ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-3-5-sonnet-20241022 temperature: 0.7 mcp: server: name: java-mcp-server version: 1.0.0 type: SYNC sse-endpoint: /mcp

把 Key 放环境变量是个好习惯,别硬编码进 yml。启动前export TAOTOKEN_API_KEY=你的Key即可。

然后是 MCP Server 的工具实现。Spring AI 用@Tool注解标记方法,@ToolParam描述参数,框架会自动生成 JSON Schema:

import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Service; @Service public class WeatherToolService { @Tool(name = "get_weather", description = "查询指定城市的实时天气") public String getWeather( @ToolParam(description = "城市名称,例如:北京、杭州") String city) { // 实际项目替换为真实气象 API 调用 return String.format("%s:晴,25℃,湿度 40%%", city); } @Tool(name = "query_order", description = "根据订单号查询订单状态") public String queryOrder( @ToolParam(description = "订单号,纯数字") String orderId) { return String.format("订单 %s 状态:已发货,预计明天送达", orderId); } }

工具注册需要一个配置类,把 ToolCallbackProvider 暴露成 Bean:

import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class McpServerConfig { @Bean public ToolCallbackProvider weatherTools(WeatherToolService service) { return MethodToolCallbackProvider.builder() .toolObjects(service) .build(); } }

到这里 Server 侧就完成了。启动后访问http://localhost:8080/mcp应该能看到 SSE 连接建立。如果你只想本地调试,可以把sse-endpoint去掉,改用 Stdio 模式启动,但 SSE 更适合后面 Client 远程连接。

4. 验证请求:MCP Client 触发工具调用并确认返回

Server 跑起来后,写 Client 来验证整条链路。Client 的职责是连接 MCP Server、拉取工具列表、注入 ChatClient,然后你发一句自然语言,看模型是否自动调用了工具。

先写 MCP Client 的连接配置。Spring AI 的 MCP Client 支持 SSE 和 Stdio 两种 transport,这里用 SSE 连本地 8080:

import org.springframework.ai.mcp.client.McpClient; import org.springframework.ai.mcp.client.transport.SseClientTransport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class McpClientConfig { @Bean public McpClient mcpClient() { SseClientTransport transport = new SseClientTransport("http://localhost:8080/mcp"); return McpClient.builder() .transport(transport) .clientInfo("java-mcp-client", "1.0.0") .build(); } }

然后把它和 ChatClient 串起来。关键点是defaultTools接收 ToolCallbackProvider,MCP Client 拉到的工具会通过它注册进去:

import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.mcp.client.McpClient; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ChatClientConfig { @Bean public ChatClient chatClient(ChatClient.Builder builder, McpClient mcpClient) { ToolCallbackProvider tools = mcpClient.toolCallbackProvider(); return builder .defaultTools(tools) .build(); } }

写一个 Controller 做验证入口:

import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient = chatClient; } @GetMapping("/ask") public String ask(@RequestParam String q) { return chatClient.prompt(q).call().content(); } }

启动两个服务(或者同一个进程里先起 Server 再起 Client),然后发请求:

curl "http://localhost:8080/ask?q=帮我查一下杭州现在的天气"

预期返回类似:杭州:晴,25℃,湿度 40%。这说明模型识别到用户意图,自动选择了get_weather工具,传入了city=杭州,并拿到了工具返回值。你可以再试一句订单 123456 到哪了,看它是否切换到query_order工具。

如果返回的是模型自己编的天气,而不是工具返回的固定格式,说明工具没注册成功。这时候去看启动日志里有没有Registered tools: [get_weather, query_order]这类输出。没有的话,检查 ToolCallbackProvider 的 Bean 是否被扫描到,以及 MCP Client 是否成功连上了 SSE endpoint。

验证模型本身是否正常,可以先用模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 发一句普通对话,确认 Key 和模型没问题,再排查 MCP 层。这个顺序能帮你快速定位是模型通道问题还是工具注册问题。

5. 常见报错排查:401、local proxy failed 与 reading choices

这一节按真实报错来。我把踩过的坑按错误信息分类,你对照日志找。

401 Unauthorized。最常见的原因是 Key 无效或没带上。检查三处:环境变量TAOTOKEN_API_KEY是否真的 export 了(echo $TAOTOKEN_API_KEY看有没有值);yml 里api-key的占位符拼写是否一致;Key 前后有没有多余空格或换行。还有一种情况是 base-url 写成了https://taotoken.net少了/api,导致请求打到了错误路径返回 401。正确写法是https://taotoken.net/api。

local proxy failed / connection refused。这个通常出现在 MCP Client 连 Server 时。如果你用 SSE 模式,确认 Server 已经启动并且 8080 端口在监听(lsof -i:8080)。如果 Server 和 Client 在不同机器,把http://localhost:8080/mcp换成实际 IP。另外注意 SSE endpoint 路径要和 yml 里的sse-endpoint完全一致,大小写都别错。

reading choices 相关报错,比如Cannot read field "choices" because response is null或choices解析失败。这多半是模型返回体不符合 OpenAI 格式,或者模型名写错了。检查spring.ai.openai.chat.options.model是否是你账号下可用的 Model ID。有些模型不支持工具调用,返回结构会不一样,换一个支持 function calling 的模型再试。如果用的是 TaoToken 通道,确认模型名和文档里列的一致。

NoSuchMethodError / ClassNotFoundException。这是版本冲突的典型症状。Spring AI 和 Spring AI Alibaba 的 milestone 版本之间依赖关系比较敏感,建议用mvn dependency:tree看下spring-ai-core是不是被拉了两个版本。统一在 properties 里管理版本号,别在单个 dependency 里写死。

工具被调用但参数为空。检查@ToolParam的 description 是否写清楚,模型靠这个理解参数含义。参数类型尽量用 String、int 这类简单类型,复杂对象容易生成错误的 JSON Schema。

OAuth / token 过期类报错。如果你用的是需要 OAuth 的模型通道,注意 token 有效期。TaoToken 的 Key 是长期有效的,但如果你在代码里又套了一层自己的鉴权,记得把两层分开排查。接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 里有协议细节,遇到不确定的字段可以去对。

排查顺序建议:先确认模型通道通(用 chat 页面测),再确认 MCP Server 起没起,再确认 Client 连没连上,最后看工具注册日志。一层层往下,别一上来就改代码。

6. 语义一致 CTA:把统一 Key 通道用进你的 Java Agent

走到这里,你已经跑通了 Spring Boot + Spring AI Alibaba 的 MCP 全链路:Server 暴露工具、Client 注册工具、模型自动调用、结果返回。接下来可以做的扩展方向有几个:把内部工单、库存、审批这些 Service 陆续加上@Tool注解,让它们变成模型可调用的能力;把 SSE 模式部署到内网,让多个 Client 共享同一套工具;或者把 endpoint 固定到 TaoToken 统一通道,用一套 Key 管理多个模型的切换测试。

如果你在排障或接入阶段卡住了,优先看 API Keys 管理页 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 确认 Key 状态,再对照接入文档 https://taotoken.net/doc?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= 最快。如果你打算长期做编码类 Agent、高频调用工具,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 会更合适。

最后留一个实用技巧:把get_weather这类高频工具的返回值加一层本地缓存(比如 Caffeine),模型连续问同一个城市时就不用重复打外部 API。工具方法本身不用改,在 Service 里加@Cacheable就行。这个优化在真实项目里能省不少调用量。

返回列表