1. 为什么 Java 开发者搭 MCP 服务总在第一步卡住
MCP 服务这件事,Java 开发者上手时最容易卡住的不是协议本身,而是模型通道。Spring AI 的 MCP Server Starter 已经把 SSE 端点、工具注册、@Tool注解扫描这些活干得差不多了,真正让人反复重启项目的是鉴权配置:base-url填哪个、api-key从哪来、模型名写gpt-4o还是gpt-4o-mini、为什么客户端连上了却调不出工具。
我一开始也是照着文档把spring-ai-mcp-server-webmvc-spring-boot-starter加进pom.xml,写了个带@Tool的方法,mvn spring-boot:run起来看到/sse端点通了,结果客户端一发请求就 401。排查半天发现是模型侧的 Key 没配,Spring AI 默认会去读OPENAI_API_KEY环境变量,本地没设就直接抛鉴权异常。后来换成 TaoToken 的统一 Key 通道,把base-url和api-key一次性写进application.yml,服务端和客户端共用同一套凭证,这类问题才彻底消失。
这篇就按我实际跑通的顺序来:先讲清楚 MCP 服务在 Spring AI 里是什么形态,再把 TaoToken 的 Key 和通道配好,然后给出可复制的application.yml骨架、MCP 服务端启动配置,最后用一次端到端调用验证通道连通。适合已经会 Spring Boot、想快速把第一个 MCP 服务跑起来的 Java 开发者。
2. TaoToken 前置:统一 Key 与 API 通道准备
MCP 服务本身不产生模型能力,它只是把本地方法暴露成工具,真正干活的是背后的大模型。所以第一步不是写代码,而是把模型通道准备好。TaoToken 在这里的角色是统一入口:一个 Key 覆盖多种模型,base-url固定,省得你在 OpenAI、Claude、国产模型之间来回换配置。
操作路径很直接。打开官网 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= 。生成后先复制保存,页面刷新就不再完整显示。
API 通道的基础地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接作为base-url使用。Spring AI 的 OpenAI 兼容客户端会在这个地址后面拼/v1/chat/completions之类的路径,所以你在配置里只写根地址就行,不要自己加/v1。
注意:Key 只放在本地
application.yml或环境变量里,不要提交到 Git。团队协作时用环境变量注入,配置文件里写${TAOTOKEN_API_KEY}占位。
如果你只是想先确认模型能不能通,可以先用模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 发一条消息,确认 Key 有效再进代码环节。这一步能省掉后面「到底是 Key 错还是代码错」的扯皮。
3. 可复制配置:application.yml 与 MCP 服务端骨架
先给依赖。Spring AI 的 MCP Server 目前用 WebMVC 版本最省事,SSE 传输开箱即用:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency>版本号按你项目里 Spring AI 的 BOM 对齐,M6 是我实测能跑通 MCP Server 的版本。接下来是application.yml,这是整篇最该直接抄的部分:
server: port: 8080 spring: application: name: gzh-mcp-server ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.7 mcp: server: name: gzh-mcp-server version: 1.0.0 sse-endpoint: /sse sse-message-endpoint: /mcp/message几个关键点解释一下。base-url写 TaoToken 的 API 根地址,Spring AI 会自动补全路径;api-key用环境变量注入,本地跑之前在终端export TAOTOKEN_API_KEY=你的Key;model先填gpt-4o-mini验证通道,跑通后再换更强的模型。sse-endpoint和sse-message-endpoint是 MCP 客户端要连的两个地址,默认值就是这两个,写出来是为了后面客户端配置对得上。
然后是服务端主类和工具类。工具类用@Tool注解暴露方法,Spring AI 会自动扫描并注册到 MCP 服务:
@SpringBootApplication public class McpServerApplication { public static void main(String[] args) { SpringApplication.run(McpServerApplication.class, args); } } @Component public class GzhTools { @Tool(description = "根据城市名查询当前天气,返回温度和天气状况") public String getWeather(@ToolParam(description = "城市名称,例如 杭州") String city) { // 这里替换成真实调用,示例先返回固定结构 return city + " 当前 22 摄氏度,多云"; } @Tool(description = "把一段中文翻译成英文") public String translate(@ToolParam(description = "待翻译的中文文本") String text) { return "translated: " + text; } }@Tool的description很重要,模型靠它决定什么时候调用这个工具。写得太模糊,模型就不会触发;写清楚输入输出,命中率明显提升。@ToolParam同理,参数说明会进到模型的上下文里。
4. 启动与端到端验证:一次调用确认通道连通
配置齐了就可以启动。终端里先设 Key,再跑:
export TAOTOKEN_API_KEY=sk-你的实际Key mvn spring-boot:run看到日志里出现Registered tools: [getWeather, translate]和SSE endpoint: /sse就说明服务端起来了。这时候别急着写客户端,先用 curl 确认 SSE 端点活着:
curl -N http://localhost:8080/sse正常会返回一行event: endpoint加一个data: /mcp/message?sessionId=xxx。这个sessionId是本次连接的会话标识,客户端后续发消息要带上它。-N是关掉缓冲,不然你看不到流式输出。
接下来配客户端。如果你用支持 MCP 的编辑器插件,配置就是一段 JSON:
{ "mcpServers": { "gzh-mcp-server": { "url": "http://localhost:8080/sse" } } }配好之后,客户端会连上 SSE 端点,拉取工具列表。成功的话你能在工具面板里看到getWeather和translate两个方法,参数说明也一并解析出来。这时候在对话里问「杭州天气怎么样」,模型会触发getWeather,服务端日志打印调用记录,客户端返回「杭州 当前 22 摄氏度,多云」。这一条链路走通,就说明 TaoToken 的模型通道、Spring AI 的 MCP 服务端、客户端三者全部连通。
如果你想在代码里做一次自动化验证,可以写个简单的测试,直接调 MCP 客户端的工具列表接口,断言返回里包含你注册的工具名。这样每次改配置后跑一遍,比手动点客户端快。
5. 本篇常见错排查
401 鉴权失败:九成是api-key没读到。检查环境变量名和application.yml里的${TAOTOKEN_API_KEY}是否一致,echo $TAOTOKEN_API_KEY确认有值。另一个可能是base-url多写了/v1,TaoToken 的根地址就是https://taotoken.net/api,不要自己加路径。
SSE 连不上或一直 pending:先确认端口没被占,lsof -i:8080看一下。如果服务端日志显示端点注册了但 curl 没反应,检查是不是被安全框架拦了,Spring Security 默认会拦/sse,需要在配置里放行。
工具列表为空:@Tool注解的类必须是 Spring Bean,加@Component或@Service。另外确认spring-ai-mcp-server-webmvc-spring-boot-starter的版本和 Spring AI BOM 一致,版本错配会导致扫描不到注解。
模型不触发工具调用:description写得太泛,或者模型选的太弱。先把model换成能力更强的型号试一次,确认是描述问题还是模型问题。temperature调低一点也有帮助,工具调用场景不需要发散。
客户端解析出工具但调用报错:看服务端日志的异常栈,多半是工具方法内部抛了未捕获异常。MCP 协议会把异常包装成错误响应返回,客户端只显示「调用失败」,真实原因在服务端日志里。
6. 后续怎么把这套通道用顺
第一个 MCP 服务跑通之后,你会发现真正花时间的不是写工具方法,而是反复调description和参数说明,让模型稳定命中。我的做法是每加一个工具,先在模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 手动问几句,看模型会不会主动调、调的时候参数对不对,确认没问题再进代码。
如果你打算长期做编码类 Agent,把 MCP 服务和 Coding Plan 配合起来会更顺,套餐页在 https://taotoken.net/coding-plan?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= ,遇到路径或参数问题先翻这里,比搜索引擎快。
Claude Code 相关的接入配置可以参考 https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,思路和 Spring AI 这边一致,都是把base-url指向统一通道、Key 走环境变量。把这一套配置模板固化下来,下一个 MCP 服务基本就是复制粘贴加改工具方法的事。