Java 实现 MCP Server 以及常用 MCP 服务分享:TaoToken 统一 Key 接入 Spring AI 实战 1. 为什么 Java 开发者需要自己写一个 MCP ServerMCP 全称 Model Context Protocol是 Anthropic 开源的一套模型上下文协议说白了就是给大模型装了一个标准化的“工具箱接口”。以前大模型想调用外部能力每家 Function Calling 的格式都不一样接一个模型写一套适配接三个模型写三套维护起来非常痛苦。MCP 出现之后相当于给这件事定了一个类似 JDBC 的标准客户端按统一协议连服务端按统一协议暴露工具模型换不换、客户端换不换工具层基本不用动。对 Java 开发者来说这件事的意义在于你手上已有的 Spring Boot 服务、内部 RPC、数据库查询逻辑都可以通过一个 MCP Server 包装成模型能直接调用的工具而不需要把业务重写成 Python。尤其是团队里已经有大量 Java 存量系统的时候用 Spring AI 起一个 MCP Server比重新搭一套 Python 工具链要顺手得多。这篇内容聚焦三件事用 Spring AI 搭一个能跑的 MCP Server、把工具通过 SSE 暴露出来、再用 TaoToken 的统一 Key 和 API 通道去接常用 MCP 服务让模型侧和工具侧的接入都收敛到一套配置里。适合已经会 Spring Boot、想快速把内部能力接给 AI 客户端的 Java 开发者。下面所有步骤都可以直接复制跟做我会把踩过的坑和排查动作一起写清楚。2. TaoToken 前置准备统一 Key 与 API 通道在动手写 Server 之前先把模型侧的通道准备好。MCP Server 本身只负责暴露工具真正去调用模型、做 Function Calling 决策的是客户端那一侧所以你需要一个能稳定调用支持 Function Calling 模型的 API 通道。TaoToken 在这里的作用就是把模型调用收敛成一套 Key 和一套 API 地址不用在多个平台之间来回切换配置。你需要先拿到一个 API Key。进入控制台创建即可地址是 https://taotoken.net/api-keys 创建后复制保存后面客户端配置里会用到。注意 Key 只在创建时完整显示一次丢了就重新建一个。拿到 Key 之后模型调用的基础地址统一用 https://taotoken.net/api 这个地址不加任何多余参数直接作为 OpenAI 兼容风格的 base_url 使用。如果你用的是 Claude Code 这类走 Anthropic 协议的客户端接入地址参考 https://taotoken.net/doc 里的说明协议路径会有区别别混用。注意MCP 工具要能被模型正确调用前提是所选模型支持 Function Calling。选模型时优先挑带工具调用能力的否则客户端能连上 MCP Server但模型不会主动去调工具。如果你后面打算长期跑编码类 Agent或者让 MCP 工具在 IDE 里持续工作可以了解下 Coding Plan地址是 https://taotoken.net/coding-plan 它更适合高频、长时间的编码场景。只是想先验证模型能不能正常对话用模型对话页 https://taotoken.net/models 快速试一下就行。3. 可复制配置Spring AI 搭建 MCP Server 全流程3.1 环境与依赖先把版本卡死这一步最容易出问题。JDK 用 17 或 21Spring Boot 必须 3.4.x 及以上Spring AI 用 1.0.0-M6 之后的里程碑版本。低于这个组合MCP 的 starter 根本拉不起来。pom.xml 里核心就一个依赖加上 Spring AI 的 BOM 和里程碑仓库properties java.version17/java.version spring-ai.version1.0.0-M7/spring-ai.version /properties dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server-webflux/artifactId /dependency /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshotsenabledfalse/enabled/snapshots /repository /repositories这里选 webflux 版本是因为它用 SSE 暴露端点调试起来最直观浏览器和客户端都好接。如果你更习惯阻塞式可以换成spring-ai-starter-mcp-server-webmvc配置项基本一致。3.2 写一个工具服务工具就是一个普通 Spring Bean方法上打Tool注解参数上打ToolParam。description 写清楚模型靠它判断什么时候该调这个工具。import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Service; Service public class WeatherService { Tool(description 根据城市名称获取天气预报信息) public String getWeather(ToolParam(description 城市名称) String cityName) { return cityName 今天晴气温 22 度; } Tool(description 根据城市名称获取空气质量) public String getAirQuality(ToolParam(description 城市名称) String cityName) { return cityName 空气质量优AQI 35; } }实际项目里把方法体换成你的真实业务调用即可比如查内部订单、读配置中心、调 RPC。工具方法返回值建议是字符串或简单结构太复杂的对象模型解析容易出偏差。3.3 把工具注册成 Provider光有Tool还不够得通过一个ToolCallbackProvider把工具对象暴露给 MCP 框架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 ToolConfig { Bean public ToolCallbackProvider myTools(WeatherService weatherService) { return MethodToolCallbackProvider.builder() .toolObjects(weatherService) .build(); } }多个 Service 就.toolObjects(a, b, c)一起塞进去框架会自动扫描里面的Tool方法。3.4 application.yml 配置SSE 模式下关键配置如下spring: application: name: java-mcp-demo ai: mcp: server: name: webflux-mcp-server version: 1.0.0 type: ASYNC sse-endpoint: /sse sse-message-endpoint: /mcp/messagestype在 webflux 下用 ASYNCsse-endpoint是客户端建立连接的入口sse-message-endpoint是后续消息回传的路径。这两个路径别写反写反了客户端能连上但调不动工具。3.5 客户端侧 settings.json 示例如果你用 Claude Desktop 或 Cline 这类客户端本地 stdio 模式的配置长这样先把项目打成 jarmvn clean package -Dmaven.test.skiptrue然后在客户端的 MCP 配置里写{ mcpServers: { weather-java-server: { command: java, args: [ -Dspring.ai.mcp.server.stdiotrue, -Dspring.main.web-application-typenone, -Dlogging.pattern.console, -jar, /your/absolute/path/target/java-mcp-demo-0.0.1-SNAPSHOT.jar ] } } }stdio 模式下必须把 web 应用类型关掉、控制台日志清空否则日志会混进标准输出把协议通信冲乱这是最常见的连不上原因。4. 验证请求与成功结果4.1 启动服务端直接mvn spring-boot:run或跑 main 方法。启动日志里出现类似Registered tools: 2的字样说明工具注册成功。如果只看到 0回去检查ToolCallbackProvider有没有被 Spring 扫到。4.2 用 Java 客户端验证 SSE写一个简单的测试类连本地 SSE 端点import io.modelcontextprotocol.client.McpClient; import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport; import io.modelcontextprotocol.spec.McpSchema; import org.springframework.web.reactive.function.client.WebClient; import java.util.Map; public class ClientSse { public static void main(String[] args) { var transport new WebFluxSseClientTransport( WebClient.builder().baseUrl(http://localhost:8080)); var client McpClient.sync(transport).build(); client.initialize(); client.ping(); McpSchema.ListToolsResult tools client.listTools(); System.out.println(可用工具 tools); McpSchema.CallToolResult result client.callTool( new McpSchema.CallToolRequest(getWeather, Map.of(cityName, 成都))); System.out.println(返回结果: result.content()); client.closeGracefully(); } }跑通后你会看到工具列表里有两个工具调用getWeather返回类似[TextContent[text成都 今天晴气温 22 度]]的内容。到这一步Server 侧就算通了。4.3 在客户端里接模型验证把 SSE 地址填进支持 MCP 的客户端启用后能看到工具列表。然后在对话里问“成都天气怎么样”模型应该会自动触发getWeather调用。如果模型不调工具先确认它支持 Function Calling再确认工具 description 写得够清楚。模型侧的 Key 和地址就用前面 TaoToken 那套base_url 填 https://taotoken.net/api Key 填你在控制台创建的那个。这样模型调用和 MCP 工具调用就是两条独立的通道互不干扰排查问题时也好定位是哪一侧出的错。5. 本篇常见错误排查启动报找不到 MCP starter九成是 Spring Boot 版本低于 3.4或者 BOM 版本没对齐。把 parent 版本和spring-ai.version一起升上去。客户端连上但工具列表为空检查ToolCallbackProvider是否被Configuration或Component标注以及工具方法是不是 public。私有方法不会被扫描。stdio 模式启动后客户端立刻断开日志污染了标准输出。确认加了-Dspring.main.web-application-typenone和-Dlogging.pattern.consolebanner 也要关。SSE 端点 404sse-endpoint和sse-message-endpoint配错或者用了 webmvc 的 starter 却按 webflux 的路径去连。两个 starter 的默认路径不一样对照官方文档确认。模型不调用工具模型不支持 Function Calling或者工具 description 太模糊。把 description 写成一句完整的功能描述参数说明也补全。调用工具报参数解析失败ToolParam的 description 没写或者参数类型用了复杂对象。先用 String、int 这类基础类型跑通再考虑复杂结构。TaoToken 侧返回 401Key 复制时带了空格或者用了错误的 base_url。确认地址是 https://taotoken.net/api Key 重新从控制台复制一次。6. 常用 MCP 服务与统一接入建议自己写的 Server 跑通之后日常更常见的是直接接现成的 MCP 服务。文件操作、网页抓取、数据库查询、代码仓库检索这几类基本都有开源实现不用重复造。找服务的聚合入口可以看 MCP 市场类站点按分类挑注意看最近更新时间太久没维护的慎用。接入这些服务时模型侧的 Key 和地址统一走 TaoToken 那一套工具侧各自按服务文档配。这样你的配置里只有一处模型凭证换模型、换客户端都不用动工具配置。需要长期跑编码 Agent 的话Coding Plan 那套更适合持续调用只是临时验证某个 MCP 服务能不能用用模型对话页快速试一次就够了。安全上多提一句MCP 工具能操作本地文件和数据库接入前一定限制可访问目录范围别把整个磁盘或者生产库直接暴露给模型。工具 description 里也别写敏感路径和凭证这些信息会进模型上下文。