ARTICLE DETAIL

资讯详情

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

Java 基于 Spring-AI 构建 MCP Server 与 MCP Client:TaoToken 统一 Key 接入配置实战

Java 基于 Spring-AI 构建 MCP Server 与 MCP Client:TaoToken 统一 Key 接入配置实战 1. 为什么 Java 团队需要自己搭 MCP Server 和 MCP Client如果你在用 Spring-AI 做 Java 侧的 AI 应用大概率会遇到一个很具体的分叉口模型能聊天但没法直接读你项目里的数据库、调你的内部接口、操作你的业务工具。MCPModel Context Protocol就是来解决这件事的——它把「模型能调用的工具」标准化成 Server把「发起调用的那一端」标准化成 Client。Spring-AI 从 1.0 开始提供了spring-ai-mcp-server和spring-ai-mcp-client两个 starterJava 开发者不用切语言就能把这条链路搭起来。但真正落地时痛点往往不在 MCP 协议本身而在 Key 管理。一个稍微像样的项目MCP Server 里可能要调 Claude 做工具描述生成MCP Client 里可能要调 GPT 做意图识别再加上本地调试用的模型通道三四个 Key 散落在不同 yml、不同环境变量里改一次配置要翻五个文件。这篇就聚焦这个场景用 TaoToken 的统一 Key 和 API 通道把 Spring-AI 的 MCP Server 与 MCP Client 双端配置收敛到一处并给出可复制的application.yml骨架和一次本地启动 工具调用的完整验证动作。适合谁看已经能跑通 Spring-AI 基础对话、想进一步做工具调用Function Calling / Tool的 Java 后端或者手上有一堆模型 Key、想统一收口的工程负责人。下面所有配置我都按「复制就能改」的粒度写你只需要替换自己的 Key 和端口。2. TaoToken 前置统一 Key 与 API 通道准备TaoToken 在这里扮演的角色是「一个 Key 走多个模型通道」。你不需要为每个模型厂商单独维护 base_url 和 api_keySpring-AI 侧只认一个 OpenAI 兼容的 endpoint剩下的模型路由交给 TaoToken 处理。这对 MCP 场景特别友好因为 MCP Server 和 MCP Client 往往要用不同模型但配置结构可以完全一致。第一步去控制台创建 API Key。地址是 https://taotoken.net/api-keys 登录后新建一个 Key复制出来先存到本地临时文件后面要填进 yml。注意这个 Key 只在创建时完整显示一次丢了就重新建。第二步确认 API 通道地址。TaoToken 的 API 根地址是 https://taotoken.net/api Spring-AI 的 OpenAI starter 需要的是兼容/v1的 base-url所以配置里写https://taotoken.net/api即可starter 会自动拼/v1/chat/completions。如果你用的是 Claude 系列模型走的是 Anthropic 兼容通道base-url 同样是这个根地址模型名按 TaoToken 文档里的命名填。第三步想清楚模型分配。我的建议是MCP Server 侧用便宜、响应快的模型做工具描述和参数校验MCP Client 侧用理解能力强的模型做意图路由。两边都指向同一个 TaoToken Key但model字段不同。这样你只维护一个 Key模型切换只改一行。提示不要把 Key 硬编码进代码或提交到 Git。下面 yml 里我用${TAOTOKEN_API_KEY}占位本地用环境变量注入生产用配置中心。如果你还没决定用哪个模型可以先到模型对话页面 https://taotoken.net/model-chat 试一下各模型的工具调用表现再回来填配置。这一步能省掉后面反复改 model 字段的时间。3. 可复制配置application.yml 与 MCP 双端骨架这一节是全文的核心。我按「一个 Spring Boot 项目同时跑 MCP Server 和 MCP Client」的结构来写实际你也可以拆成两个服务配置逻辑一样。先看依赖。pom.xml里需要三个东西Spring-AI 的 OpenAI starter、MCP Server starter、MCP Client starter。版本用 Spring-AI 1.0.0 及以上MCP 的 artifact 在 1.0 之后已经进入正式仓库。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server-webmvc/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency然后是application.yml。这里的关键是OpenAI 的 base-url 和 api-key 只写一份MCP Server 和 MCP Client 共用同一个ChatModelBean模型差异通过options.model在调用时覆盖。server: port: 8080 spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-3-5-sonnet-20241022 temperature: 0.3 mcp: server: name: java-mcp-server version: 1.0.0 protocol: STREAMABLE type: SYNC client: enabled: true name: java-mcp-client version: 1.0.0 request-timeout: 30s type: SYNC sse: connections: local-server: url: http://localhost:8080几个参数说明一下。spring.ai.openai.base-url指向 TaoToken 的 API 根地址starter 会自动补全路径。api-key用环境变量注入本地启动前export TAOTOKEN_API_KEY你的Key。spring.ai.mcp.server.protocol用STREAMABLE这是 MCP 较新的传输方式比纯 SSE 更稳。spring.ai.mcp.client.sse.connections里配了一个指向本机 8080 的连接这样 Client 和 Server 在同一个进程里也能联调。接下来是 MCP Server 的工具定义。这里要处理一个上一篇踩过的坑String 类型返回值会被框架二次封装成 JSON 字符串导致 Client 收到\aaaaaaa\这种带转义的结果。解决办法是自定义ToolCallResultConverter。package com.example.mcp.util; import org.springframework.ai.tool.execution.ToolCallResultConverter; import org.springframework.ai.util.json.JsonParser; import org.springframework.lang.Nullable; import java.lang.reflect.Type; public class StringResultConverter implements ToolCallResultConverter { Override public String convert(Nullable Object result, Nullable Type returnType) { if (result instanceof String) { return (String) result; } else if (returnType Void.TYPE) { return Done; } else { return JsonParser.toJson(result); } } }然后在工具方法上用resultConverter指定它。注意Tool注解的resultConverter属性在 Spring-AI 1.0 里已经可用。package com.example.mcp.tool; import com.example.mcp.util.StringResultConverter; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; Component public class ToolComponent { Tool(description 查询订单状态, resultConverter StringResultConverter.class) public String queryOrder( ToolParam(required true, description 订单号) String orderId) { return 订单 orderId 状态已发货; } }MCP Client 侧的初始化Spring-AI 会自动扫描spring.ai.mcp.client配置并创建McpSyncClientBean。你只需要在业务代码里注入McpSyncClient或McpSyncClientCustomizer然后调用listTools和callTool。package com.example.mcp.client; import org.springframework.ai.mcp.client.McpSyncClient; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; Component public class McpClientRunner implements CommandLineRunner { private final McpSyncClient mcpClient; public McpClientRunner(McpSyncClient mcpClient) { this.mcpClient mcpClient; } Override public void run(String... args) { var tools mcpClient.listTools(); System.out.println(可用工具数量 tools.tools().size()); tools.tools().forEach(t - System.out.println( - t.name())); } }到这里配置骨架就齐了。Server 暴露工具Client 发现工具两边共用 TaoToken 的 Key 和 base-url。4. 本地启动与工具调用验证配置写完跑起来才算数。按下面顺序操作每一步都有预期输出。第一步设置环境变量并启动。在项目根目录执行export TAOTOKEN_API_KEY你的TaoTokenKey mvn spring-boot:run预期看到日志里出现Registered tools: 1和MCP client connected to local-server。如果只看到 Server 注册、没看到 Client 连接检查spring.ai.mcp.client.sse.connections.local-server.url是否指向了正确的端口。第二步验证工具列表。启动完成后McpClientRunner会打印可用工具。正常输出类似可用工具数量1 - queryOrder如果数量是 0说明 Client 没连上 Server或者 Server 的工具没被扫描到。先确认ToolComponent在SpringBootApplication的扫描路径下。第三步实际调用一次工具。在McpClientRunner里追加一段调用逻辑var result mcpClient.callTool( new McpSchema.CallToolRequest(queryOrder, Map.of(orderId, SO-20241022-001))); System.out.println(工具返回 result.content());重启后预期输出工具返回[TextContent[text订单 SO-20241022-001 状态已发货]]注意这里没有出现\订单...\这种二次封装说明StringResultConverter生效了。如果你看到的是带转义的字符串检查Tool注解里resultConverter是否写对以及StringResultConverter是否被 Spring 管理加Component或手动 new 都行但注解方式更省事。第四步验证模型通道。工具调用本身不经过模型但 MCP Client 在实际业务里往往要先让模型决定调哪个工具。你可以加一个简单的ChatClient调用确认 TaoToken 通道通Autowired ChatClient chatClient; String reply chatClient.prompt() .user(帮我查一下订单 SO-20241022-001 的状态) .call() .content(); System.out.println(模型回复 reply);如果这一步报 401说明TAOTOKEN_API_KEY没注入成功如果报 404检查base-url是否写成了https://taotoken.net/api/v1多写了/v1会 404starter 自己会拼。5. 本篇常见错排查报错一No tool named queryOrder found。这是 Client 连上了 Server 但工具名对不上。MCP 工具名默认取方法名但 Spring-AI 可能会加前缀。用mcpClient.listTools()打印实际名字再按实际名字调用。另外确认Tool注解的name属性没被显式改过。报错二String 返回值带转义引号。就是上一篇遇到的二次封装问题。根因是默认的ToolCallResultConverter会把 String 当对象序列化。解决办法就是本文第 3 节的StringResultConverter在Tool上指定resultConverter。注意returnType Void.TYPE的分支要保留否则 void 方法会返回 null 导致 Client 解析异常。报错三Connection refused: localhost:8080。Client 启动比 Server 快SSE 连接建立时 Server 还没就绪。两个办法一是给 Client 加request-timeout和重试二是把 Client 和 Server 拆成两个服务用启动顺序控制。本地联调推荐第一种配置里加spring.ai.mcp.client.sse.connections.local-server.url后加?retrytrue不一定生效更稳的是在McpClientRunner里加Thread.sleep(3000)做临时规避。报错四TaoToken 返回 401 或 403。先确认环境变量TAOTOKEN_API_KEY在当前 shell 里echo $TAOTOKEN_API_KEY有值。如果是在 IDE 里跑检查 Run Configuration 的 Environment variables 有没有配。403 通常是 Key 权限或模型名不对去 https://taotoken.net/api-keys 确认 Key 状态再去模型对话页面确认模型名拼写。报错五MCP Server 启动报protocol STREAMABLE not supported。这是 Spring-AI 版本问题。STREAMABLE在 1.0.0-M6 之后才支持如果你用的是更早的 milestone改成SSE或升级版本。升级后注意spring-ai-starter-mcp-server-webmvc的 artifact 名在正式版里可能变成spring-ai-starter-mcp-server-webmvc以官方 BOM 为准。6. 把 Key 收口到一处之后跑通上面这套之后你手上应该有一个能同时跑 MCP Server 和 MCP Client 的 Spring Boot 项目两边共用 TaoToken 的一个 Key 和一个 base-url。后续要加新工具只需要在ToolComponent里加方法要换模型只改spring.ai.openai.chat.options.model一行要加新的 MCP 连接在spring.ai.mcp.client.sse.connections下加一段。如果你打算把这个项目往生产推下一步建议是把 MCP Client 的 SSE 连接改成走内网地址Key 从环境变量换成配置中心工具方法里加参数校验和超时控制避免模型传错参数导致业务异常。这些都不需要改 MCP 协议层属于常规 Spring 工程实践。需要继续查接入细节的话接入文档在 https://taotoken.net/doc API Key 管理在 https://taotoken.net/api-keys 模型试用在 https://taotoken.net/model-chat 。如果你后面要做长期编码或 Agent 场景可以看下 Coding Plan https://taotoken.net/coding-plan 它和本文的 MCP 双端配置是互补的。
返回列表