ARTICLE DETAIL

资讯详情

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

像写 Controller 一样开发 Java MCP:TaoToken 统一 Key 接入 Solon-AI 的 Java 8 实践

像写 Controller 一样开发 Java MCP:TaoToken 统一 Key 接入 Solon-AI 的 Java 8 实践 1. Java 8 老项目接入 MCP 的真实困境MCP 全称 Model Context Protocol你可以把它理解成大模型世界的 USB-C 接口模型不再为每个工具单独写适配而是通过统一协议去发现和调用外部能力。对 Java 开发者来说MCP Server 就是那个插座把数据库查询、运维脚本、内部 API 包装成模型能识别的工具。适合谁适合手上跑着 Java 8 或 Java 11 老服务、又想快速给业务接上 AI Agent 能力的后端同学。问题在于官方 Java SDK 把门槛卡在了 Java 17。很多公司的核心服务还锁在 Java 8升级 JDK 牵一发动全身光回归测试就能排两周。于是出现一个尴尬局面AI 能力很想要但为了一个工具调用去动整个运行时成本高得离谱。Solon-AI 给出的思路不一样。它把 MCP Server 的开发抽象成类似 Spring MVC 的注解风格用McpServerEndpoint声明端点用ToolMapping把普通 Java 方法暴露成工具运行时兼容 Java 8 到 Java 25。换句话说你写 MCP 工具的手感和写 Controller 几乎一致不需要啃 JSON-RPC 的报文细节。我试过在一个 Java 8 的运维小服务里加 MCP 端点从加依赖到本地跑通调用大概二十分钟。真正花时间的不是写代码而是把模型通道的 Key 和 Base URL 配对。这篇就按写 Controller 式 MCP 服务 → 接入 TaoToken 统一 Key → 本地验证 → 排错的顺序把每一步的可复制配置都摊开讲。核心检索词先明确Java MCP 开发、Solon-AI 注解式 MCP Server、Java 8 兼容、TaoToken 统一 Key 接入。下面所有配置都围绕这几个点展开你可以直接照着改。2. TaoToken 统一 Key 与 Solon-AI 的对接准备在写 MCP 工具之前先把模型通道这件事理清楚。MCP Server 本身只负责暴露工具真正决定模型能不能调用、调用哪个模型的是 ChatModel 那一层。Solon-AI 里ChatModel.of(apiUrl)需要一个兼容 OpenAI 协议的服务地址和一把 Key。TaoToken 在这里扮演的角色是统一入口一个 Base URL、一把 Key就能访问多种模型省去为每个厂商维护不同域名和鉴权头的麻烦。对 Java 8 项目尤其友好因为不用引入各家 SDK只要一个 HTTP 兼容地址即可。你需要准备三样东西我把它叫三件套配置项取值来源说明Base URLhttps://taotoken.net/api兼容 OpenAI 协议的调用地址API Key控制台创建形如sk-开头的密钥Model ID模型列表选择例如对话模型的具体标识Base URL 这里要特别注意Solon-AI 的ChatModel.of()通常接收的是完整 chat completions 路径前缀实际拼接后应指向/api下的对话端点。如果你在别处看到带/v1的写法按 Solon-AI 的拼接规则调整避免出现双斜杠或路径重复。Key 的获取走控制台创建后只显示一次建议直接写进环境变量而不是硬编码。模型 ID 则根据你要用的能力选做工具调用建议选支持 function call 的对话模型。注意Base URL 用https://taotoken.net/api不要额外拼 UTM 参数到代码里那些只用于文档跳转统计。准备阶段还有一件事确认你的 Solon 版本。Solon-AI 的 MCP 模块对 Solon 主版本有要求Java 8 项目建议用较新的 Solon 2.x 系列。依赖管理如果用 Maven把版本号统一在dependencyManagement里避免传递依赖打架。到这里通道侧的东西就齐了。接下来进入正题用注解写一个 MCP Server再把它和 ChatModel 串起来。3. 可复制配置注解式 MCP Server 与 TaoToken 接入这一节是全文的核心所有片段都可以直接复制。先看依赖Maven 的pom.xml里加两项Solon 主框架和 Solon-AI 的 MCP 模块。dependencies dependency groupIdorg.noear/groupId artifactIdsolon-web/artifactId version2.9.0/version /dependency dependency groupIdorg.noear/groupId artifactIdsolon-ai-mcp/artifactId version2.9.0/version /dependency /dependencies版本号按你项目实际情况调整关键是solon-ai-mcp要和 Solon 主版本对齐。Java 8 编译目标在maven-compiler-plugin里保持1.8即可Solon-AI 不会强制你升 JDK。接着写 MCP Server。下面这个类把两个方法暴露成工具风格和写 Controller 一模一样import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint; import org.noear.solon.ai.mcp.server.annotation.ToolMapping; import org.noear.solon.ai.annotation.Param; import org.noear.solon.annotation.Header; McpServerEndpoint(name it-tools, channel McpChannel.STREAMABLE, mcpEndpoint /mcp) public class ItToolsMcpServer { ToolMapping(description 查询服务器负载) public String getServerLoad(Param(serverId) String serverId, Header(token) String token) { // 真实场景这里查监控系统示例直接返回 return Server serverId load is 15%; } ToolMapping(description 重启指定服务) public String restartService(Param(serviceName) String serviceName) { return service serviceName restarted; } }McpServerEndpoint声明这是一个 MCP 端点channel选STREAMABLE表示走 HTTP 流式传输mcpEndpoint /mcp是访问路径。ToolMapping把方法变成模型可调用的工具description会作为工具说明传给模型写清楚很重要模型靠它判断何时调用。然后是模型通道配置。把 TaoToken 的三件套写进一个配置类import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Configuration; import org.noear.solon.ai.chat.ChatModel; Configuration public class ChatModelConfig { Bean public ChatModel chatModel() { String apiUrl System.getenv(TAOTOKEN_BASE_URL); // https://taotoken.net/api String apiKey System.getenv(TAOTOKEN_API_KEY); // sk-xxxx String modelId System.getenv(TAOTOKEN_MODEL_ID); return ChatModel.of(apiUrl) .apiKey(apiKey) .model(modelId) .build(); } }环境变量在启动脚本里设置Linux 下export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYsk-你的密钥 export TAOTOKEN_MODEL_ID你的模型ID如果你更习惯用配置文件Solon 支持app.yml可以写成taotoken: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} model-id: your-model-id然后在配置类里用Inject(${taotoken.base-url})注入。两种方式都行环境变量更适合容器部署配置文件更适合本地调试。最后把 MCP 客户端和 ChatModel 串起来让模型能调用我们刚写的工具import org.noear.solon.ai.mcp.client.McpClientProvider; import org.noear.solon.ai.mcp.McpChannel; import org.noear.solon.ai.chat.ChatModel; import org.noear.solon.ai.agent.react.ReActAgent; public class AgentBootstrap { public ReActAgent buildAgent(ChatModel chatModel) { McpClientProvider mcpClient McpClientProvider.builder() .channel(McpChannel.STREAMABLE) .url(http://localhost:8080/mcp) .build(); return ReActAgent.of(chatModel) .defaultToolAdd(mcpClient) .build(); } }McpClientProvider连上本地 MCP 端点后defaultToolAdd会把服务端所有工具注入 Agent。这样模型在对话中就能自主决定调用getServerLoad还是restartService。整个链路是用户提问 → ChatModel 走 TaoToken 通道 → 模型返回工具调用意图 → Agent 通过 MCP 客户端执行本地工具 → 结果回填模型 → 生成最终回答。配置到这里就完整了。下一节讲怎么启动并验证它真的通了。4. 本地启动与调用验证确认 MCP 端点真的通了配置写完不代表能跑验证要分两步先确认 MCP 端点本身可用再确认模型能通过 TaoToken 通道调用工具。第一步启动 Solon 应用。主类很简单import org.noear.solon.Solon; public class App { public static void main(String[] args) { Solon.start(App.class, args); } }启动后看日志应该能看到 MCP 端点注册成功的提示类似mcp server endpoint registered: /mcp。如果没看到多半是McpServerEndpoint没被扫描到检查包路径是否在Solon.start的扫描范围内。第二步直接用 curl 验证 MCP 端点。MCP 的 STREAMABLE 通道走 HTTP可以先发一个初始化请求探活curl -X POST http://localhost:8080/mcp \ -H Content-Type: application/json \ -d {jsonrpc:2.0,id:1,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{},clientInfo:{name:curl-test,version:1.0}}}正常返回里会有serverInfo和capabilities说明端点活着。如果返回 404检查mcpEndpoint路径和实际请求路径是否一致。第三步验证工具列表。发tools/list请求curl -X POST http://localhost:8080/mcp \ -H Content-Type: application/json \ -d {jsonrpc:2.0,id:2,method:tools/list,params:{}}返回的tools数组里应该能看到getServerLoad和restartService每个都带description和inputSchema。这一步能过说明注解解析和工具注册都没问题。第四步验证模型通道。写一个最小的调用测试public class VerifyTest { public static void main(String[] args) { ChatModel chatModel ChatModel.of(System.getenv(TAOTOKEN_BASE_URL)) .apiKey(System.getenv(TAOTOKEN_API_KEY)) .model(System.getenv(TAOTOKEN_MODEL_ID)) .build(); String reply chatModel.prompt(你好请回复一句话).call().getContent(); System.out.println(reply); } }能打印出模型回复说明 TaoToken 的 Base URL 和 Key 配对正确。如果这里报 401直接跳到下一节排错。第五步端到端验证。用上一节的AgentBootstrap构建 Agent然后提问ReActAgent agent new AgentBootstrap().buildAgent(chatModel); String result agent.prompt(帮我查一下 server-01 的负载情况).call().getContent(); System.out.println(result);理想输出里模型会先表达要调用getServerLoadAgent 执行后返回Server server-01 load is 15%模型再组织成自然语言。看到工具被真实调用整条链路就算打通了。实测下来最容易卡住的是第四步和第五步之间的衔接模型通道通了但工具没被调用。这通常是模型不支持 function call或者工具description写得太模糊模型判断不出该用。把描述改具体比如查询服务器负载改成根据 serverId 查询该服务器当前 CPU 负载百分比命中率会明显提升。5. 常见报错排查401、local proxy failed 与工具不触发排错这节按真实报错来每个都给定位思路。401 Unauthorized。出现在模型调用阶段说明 Key 没被正确识别。先确认TAOTOKEN_API_KEY环境变量真的注入了echo $TAOTOKEN_API_KEY看有没有值。再确认 Base URL 是https://taotoken.net/api没有多余斜杠或路径。如果 Key 是从控制台复制的注意别把首尾空格带进去。还有一种情况是 Key 被禁用或额度耗尽去控制台确认状态。local proxy failed / connection refused。这个报错通常出现在 MCP 客户端连本地端点时。检查三件事Solon 应用是否真的启动了、端口是不是 8080、mcpEndpoint路径是不是/mcp。如果应用启动在别的端口McpClientProvider.builder().url()里的地址要同步改。容器里跑的话localhost可能指向容器自身换成宿主 IP 或服务名。reading choices 相关报错。这类错误一般出现在解析模型响应时choices字段读不到。常见原因是 Base URL 指向的端点返回格式不是标准 OpenAI 结构或者模型 ID 写错导致返回了错误对象。先单独用 curl 打一次对话端点看返回 JSON 里有没有choices数组。没有的话检查model参数是不是有效值。OAuth / 鉴权头冲突。如果你同时配了多个鉴权来源可能出现请求头里带了两个 Authorization。Solon-AI 的ChatModel只认一个 apiKey别在拦截器里再手动加。MCP 端点侧的Header(token)是工具参数和模型通道的 Key 是两回事别混用。工具不触发。模型回复正常但从不调用工具排查顺序模型是否支持 function call → 工具description是否清晰 →defaultToolAdd是否真的挂上了。可以在构建 Agent 后打印mcpClient.getTools()的数量为 0 说明客户端没连上或服务端没注册工具。Java 8 编译报错。如果出现Unsupported class file major version检查依赖里有没有混入 Java 17 编译的包。Solon-AI 主包兼容 Java 8但某些传递依赖可能不是用mvn dependency:tree排查必要时排除高版本传递依赖。提示排错时把日志级别调到 DEBUGSolon-AI 会打印 MCP 请求和响应的原始报文比猜快得多。6. 从本地跑通到长期编码把通道固定下来本地验证通过后下一步是把它变成日常能用的东西。如果你只是偶尔测一下模型用模型对话页面手动发请求就够了但如果你要长期写代码、跑 Agent 任务建议把 TaoToken 的通道配置固化到项目模板里。具体做法把TAOTOKEN_BASE_URL、TAOTOKEN_API_KEY、TAOTOKEN_MODEL_ID三件套写进项目的.env.example团队成员复制成.env填自己的 Key。CI 环境里用密钥管理注入别提交到仓库。Solon-AI 的ChatModel配置类保持不变换环境只换环境变量。对于需要长时间运行的 Agent 服务建议开 Coding Plan 这类按周期计费的方式比按次调用更可控。MCP 端点这边生产环境把channel从STREAMABLE换成STREAMABLE_STATELESS无状态更适合多实例部署配合负载均衡不会因为会话粘性出问题。工具数量多了以后别一股脑全挂给模型。用 Solon-AI 的 Skill 机制做分组比如运维类工具归一个 Skill数据查询归另一个通过isSupported关键词做路由。这样模型每次只看到相关工具幻觉和误调用都会下降。最后留一个实用技巧MCP 工具的description当成 API 文档来写把参数含义、返回格式、适用场景都写进去。模型选工具靠的就是这段文字写得越清楚Agent 越靠谱。这比事后调 prompt 有效得多。通道配置和工具描述都固定下来之后你会发现 Java 8 项目接 AI 能力这件事真正的成本不在 JDK 版本而在把接口描述清楚。Solon-AI 把协议复杂度吃掉了剩下的就是业务本身。
返回列表