尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
用 Solon AI 从零构建 MCP 工具服务:让 AI Agent 拥有真实世界的能力|TaoToken 统一 Key 接入实践
1. 从一次“AI 查不到实时数据”的尴尬说起你可能遇到过这种场景在 Claude Desktop 或者 Cursor 里问 AI“帮我查一下 Solon AI 最新的 MCP 支持情况”它一本正经地编了一段听起来很合理、但完全对不上的内容。原因不复杂——大模型本身只有训练时的那点静态知识它没法主动去读你的数据库、调你的内部接口、查你本地的文档目录。这时候就需要一个标准化的“工具调用协议”把模型和真实世界连起来MCPModel Context Protocol就是干这个的。MCP 由 Anthropic 提出你可以把它理解成 AI 工具生态里的 USB Type-C以前每个模型、每个 IDE 都要为每个工具单独写适配现在只要工具服务端按 MCP 协议暴露能力任何支持 MCP 的客户端都能即插即用。对 Java 开发者来说好消息是不用去啃 Python 或 Node 的 MCP SDKSolon AI 已经把服务端和客户端都封装好了核心注解就两个McpServerEndpoint声明这是一个 MCP 服务端点ToolMapping把普通 Java 方法注册成 AI 可调用的工具。这篇文章面向的是有 Java 基础、想给自己的 AI Agent 接上真实业务能力的开发者。我会带你从零搭一个可运行的 MCP 工具服务包含项目依赖、端点声明、工具/资源/提示词三类原语的注册代码然后通过 TaoToken 的统一 Key 通道完成一次端到端的模型调用验证——也就是说让模型真的去调用你写的 Java 方法而不是嘴上说说。整个过程你可以直接复制粘贴跑通踩过的坑我也会标出来。Solon AI 的 MCP 支持覆盖 Java 8 到 Java 25能嵌入 SpringBoot、jFinal、Vert.x 等框架传输通道有 Streamable、SSE、STDIO、Streamable Stateless 四种。下面先从环境准备开始。2. TaoToken 统一 Key 前置准备一次配置打通模型调用在写 MCP 服务之前得先解决“模型从哪来”的问题。MCP 服务端只负责暴露工具真正决定要不要调用工具、怎么组织参数的是大模型。所以你需要一个能稳定访问模型的通道。我这边用的是 TaoToken 的统一 Key 方案好处是一个 Key 就能切换不同模型不用为每个模型单独维护一套鉴权和 Base URL。2.1 获取 API Key 与确认 Base URL先到 TaoToken 控制台创建一个 API Key。地址是 https://taotoken.net/api-keys 登录后点新建复制那串以sk-开头的密钥先存到环境变量里别硬编码进代码export TAOTOKEN_API_KEYsk-你的实际密钥Base URL 统一用https://taotoken.net/api注意这个地址后面不要加 UTM 参数直接作为 OpenAI 兼容接口的前缀即可。模型 ID 按你需要的填比如claude-sonnet-4-5、gpt-4o这类具体以控制台模型列表为准。2.2 在 Solon AI 里配置 ChatModelSolon AI 的ChatModel支持 OpenAI 兼容协议所以接 TaoToken 很直接。下面这段配置你可以放在app.yml里也可以纯代码构建。先看配置文件方式solon: ai: chat: apiUrl: https://taotoken.net/api/v1/chat/completions apiKey: ${TAOTOKEN_API_KEY} model: claude-sonnet-4-5 provider: openai如果你更喜欢代码里显式构建这样写import org.noear.solon.ai.ChatModel; ChatModel chatModel ChatModel.of(https://taotoken.net/api/v1/chat/completions) .provider(openai) .apiKey(System.getenv(TAOTOKEN_API_KEY)) .model(claude-sonnet-4-5) .build();这里有个容易踩的点apiUrl要带上/v1/chat/completions完整路径只写https://taotoken.net/api会报 404。provider 写openai是因为走的是 OpenAI 兼容格式不是指模型厂商。2.3 为什么 MCP 场景下要统一 KeyMCP 工具服务一旦上线可能同时被 Claude Desktop、Cursor、你自己的 Agent 后端调用。如果每个客户端配一套模型鉴权密钥管理会非常乱。用 TaoToken 统一 Key 之后MCP 服务端只管暴露工具模型调用统一走一个通道换模型只改一个 model 字段。这对后面做端到端验证特别省事——你不需要为了测试不同模型去改 MCP 服务本身的代码。配置好之后先别急着写 MCP用一段最小代码确认模型通道是通的public class PingModel { public static void main(String[] args) { ChatModel model ChatModel.of(https://taotoken.net/api/v1/chat/completions) .provider(openai) .apiKey(System.getenv(TAOTOKEN_API_KEY)) .model(claude-sonnet-4-5) .build(); String reply model.prompt(只回复两个字通了).call().getMessage().getContent(); System.out.println(reply); } }跑出来打印“通了”说明 Key、Base URL、模型 ID 三件套都对。这一步过了再往下走能省掉后面一半的排障时间。3. 可复制配置Solon AI 项目依赖与 McpServerEndpoint 声明现在进入正题搭 MCP 服务端。这一节给的都是可以直接复制进项目的片段路径和原文保持一致。3.1 pom.xml 依赖Solon AI 的 MCP 能力在solon-ai-mcp这个 artifact 里。如果你用 Maven加这一块dependency groupIdorg.noear/groupId artifactIdsolon-ai-mcp/artifactId version3.0.0/version /dependency版本号以你项目里 Solon 主版本对齐为准Solon AI 通常跟 Solon 主框架版本同步。如果你用 Gradleimplementation org.noear:solon-ai-mcp:3.0.0这个依赖支持 Java 8、11、17、21、25不需要额外装什么运行时。3.2 声明 MCP 服务端点核心注解是McpServerEndpoint加在类上就表示这个类的方法可以被 MCP 客户端发现。看一个最小可运行的服务端import org.noear.solon.Solon; import org.noear.solon.ai.annotation.ToolMapping; import org.noear.solon.ai.mcp.McpChannel; import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint; import org.noear.solon.annotation.Param; McpServerEndpoint(channel McpChannel.STREAMABLE, mcpEndpoint /mcp) public class HelloTool { ToolMapping(description 打招呼) public String hello(Param(description 名字) String name) { return 你好 name 欢迎使用 Solon AI MCP; } } public class McpServerApp { public static void main(String[] args) { Solon.start(McpServerApp.class, args); } }channel McpChannel.STREAMABLE表示用 Streamable HTTP 传输mcpEndpoint /mcp是暴露的路径。启动后服务监听 8080MCP 端点就是http://localhost:8080/mcp。3.3 工具注册ToolMapping 与 ParamToolMapping的description非常关键模型就是靠这段文字判断“这个工具是干嘛的、要不要调用”。写得太模糊模型可能该调的时候不调。Param的 description 同理帮模型正确填参数。McpServerEndpoint(channel McpChannel.STREAMABLE, mcpEndpoint /mcp) public class WeatherTool { ToolMapping(description 查询指定城市的天气预报) public String getWeather( Param(description 城市名称) String city, Param(description 日期格式 yyyy-MM-dd默认今天) String date) { return String.format(%s %s晴14°C东风2级, city, date); } ToolMapping(description 查询空气质量指数) public String getAirQuality(Param(description 城市名称) String city) { return String.format(%s AQI52良PM2.535, city); } }建议开启编译参数-parameters这样参数名能被自动识别模型填参更准。3.4 资源与提示词注册除了工具MCP 还有 Resource 和 Prompt 两类原语。Resource 让 AI 读结构化数据用ResourceMappingimport org.noear.solon.ai.annotation.ResourceMapping; McpServerEndpoint(channel McpChannel.STREAMABLE, mcpEndpoint /mcp) public class DocResource { ResourceMapping(uri docs://catalog) public String getCatalog() { return # 知识库目录\n1. Solon 入门指南\n2. Solon AI 开发教程\n3. Solon Cloud 微服务实战; } ResourceMapping(uri docs://{category}/{id}) public String getDoc( Param(description 分类) String category, Param(description 文档ID) String id) { return String.format(文档内容[%s] #%s, category, id); } }Prompt 提供可复用的提示模板用PromptMappingimport org.noear.solon.ai.annotation.PromptMapping; McpServerEndpoint(channel McpChannel.STREAMABLE, mcpEndpoint /mcp) public class QaPrompt { PromptMapping(name code_review) public String codeReviewPrompt( Param(description 编程语言) String language, Param(description 代码内容) String code) { return String.format(你是一位资深的 %s 代码审查专家。请审查以下代码\n%s\n从代码质量、潜在Bug、性能优化、最佳实践四个维度分析。, language, code); } }3.5 四种传输通道怎么选Solon AI MCP 支持四种通道配置方式就是改channel参数// 生产环境首选Streamable HTTP McpServerEndpoint(channel McpChannel.STREAMABLE, mcpEndpoint /mcp) // 集群部署无状态模式支持负载均衡 McpServerEndpoint(channel McpChannel.STREAMABLE_STATELESS, mcpEndpoint /mcp) // 兼容旧客户端SSE McpServerEndpoint(channel McpChannel.SSE, mcpEndpoint /mcp/sse) // 本地进程通信STDIO McpServerEndpoint(channel McpChannel.STDIO)客户端必须和服务端通道匹配这个后面排障会重点讲。4. 验证请求端到端调用与成功结果配置写完了得证明它真的能跑。这一节分两步先用 MCP 客户端直接调工具确认服务端没问题再把 MCP 客户端挂到 ChatModel 上让模型自己决定调用工具走 TaoToken 通道完成端到端验证。4.1 直接调用工具验证服务端写一个客户端用McpClientProvider连上刚才的服务import org.noear.solon.ai.mcp.McpChannel; import org.noear.solon.ai.mcp.client.McpClientProvider; import java.util.Map; public class McpClientTest { public static void main(String[] args) { McpClientProvider client McpClientProvider.builder() .channel(McpChannel.STREAMABLE) .url(http://localhost:8080/mcp) .build(); String result client.callTool(hello, Map.of(name, 阿飞)).getContent(); System.out.println(result); } }预期输出你好阿飞欢迎使用 Solon AI MCP这一步通了说明McpServerEndpoint和ToolMapping都生效了。如果这里就报错先别往下走去第 5 节对照报错排查。4.2 挂到 ChatModel 上让模型调用真正体现 MCP 价值的是让模型自主决定调用工具。把 MCP 客户端通过defaultToolsAdd挂到 ChatModel 上import org.noear.solon.ai.ChatModel; import org.noear.solon.ai.mcp.McpChannel; import org.noear.solon.ai.mcp.client.McpClientProvider; public class AgentWithMcp { public static void main(String[] args) { McpClientProvider mcpClient McpClientProvider.builder() .channel(McpChannel.STREAMABLE) .url(http://localhost:8080/mcp) .build(); ChatModel chatModel ChatModel.of(https://taotoken.net/api/v1/chat/completions) .provider(openai) .apiKey(System.getenv(TAOTOKEN_API_KEY)) .model(claude-sonnet-4-5) .defaultToolsAdd(mcpClient) .build(); String answer chatModel.prompt(帮我查一下杭州今天的天气) .call() .getMessage() .getContent(); System.out.println(AI 回答\n answer); } }预期结果模型识别出“查天气”这个意图自动调用你写的getWeather工具拿到返回值后组织成自然语言回答类似AI 回答 杭州今天天气晴朗气温 14°C东风 2 级适合外出。4.3 验证资源读取和提示词资源读取用readResourceString catalog mcpClient.readResource(knowledge://catalog).getContent(); System.out.println(catalog);提示词获取用getPrompt拿到模板后可以再喂给模型。这两步能跑通说明三类原语都注册成功了。4.4 列出所有可用工具调试时经常需要确认服务端到底暴露了哪些工具mcpClient.getTools().forEach(tool - { System.out.println(tool.name() : tool.description()); });输出应该能看到hello: 打招呼、getWeather: 查询指定城市的天气预报等。如果某个工具没出现多半是注解没生效或者类没被 Solon 扫描到。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节按真实报错来每个都给出原因和修法。5.1 401 Unauthorized报错长这样HTTP 401 Unauthorized: {error:{message:Invalid API key}}原因基本是 TaoToken 的 Key 没传对。检查三处环境变量TAOTOKEN_API_KEY是否真的导出到了运行进程IDE 里跑的话IDE 可能没继承 shell 的环境变量建议直接在代码里临时打印确认apiKey字段有没有拼写错误Key 是不是复制时带了空格。修法String key System.getenv(TAOTOKEN_API_KEY); System.out.println(key length: (key null ? null : key.length()));长度对不上就是没读到。5.2 local proxy failed报错local proxy failed: connection refused这个通常出现在 MCP 客户端连服务端的时候说明http://localhost:8080/mcp这个地址连不上。先确认服务端真的启动了控制台有没有打印启动成功再确认端口没被占用如果服务端和客户端不在同一台机器localhost要换成实际 IP。还有一种情况是通道不匹配——服务端用 STREAMABLE客户端却用 SSE也会连不上。5.3 reading choices 相关报错报错Error reading choices: expected array, got null这是模型返回体解析失败多半是 Base URL 写错了。常见错误是只写了https://taotoken.net/api少了/v1/chat/completions。完整地址必须是https://taotoken.net/api/v1/chat/completions改完再跑如果还报检查 model 字段是不是控制台里真实存在的模型 ID。5.4 OAuth 相关报错报错OAuth authentication failed / invalid_client如果你在 Claude Desktop 或某些 IDE 里配置 MCP 服务端时看到这个通常是客户端把 MCP 端点当成了需要 OAuth 的远程服务。本地开发阶段MCP 服务端一般不需要 OAuth检查客户端配置里是不是多填了认证字段。如果是远程部署的 MCP 服务确认服务端没有强制开启认证拦截。5.5 工具没被调用模型回答里完全没提工具直接编了个答案。原因通常是ToolMapping的 description 写得太泛模型没意识到该调工具。把 description 写具体比如“查询指定城市的实时天气预报返回温度和天气状况”比“查天气”更容易触发调用。另外确认defaultToolsAdd(mcpClient)真的加上了没加的话模型根本看不到工具。5.6 三件套对照表出现配置类问题时对照这张表检查配置项正确值常见错误Base URLhttps://taotoken.net/api/v1/chat/completions少了/v1/chat/completionsAPI Keysk-开头的完整密钥带了空格或引号Model ID控制台模型列表里的 ID拼写错误或用了不存在的模型6. 继续深入把 MCP 服务接到你的编码工作流服务跑通之后接下来可以做的事不少。如果你想让 MCP 工具服务长期服务于编码和 Agent 场景可以考虑 TaoToken 的 Coding Plan它针对长期编码类调用做了通道优化地址是 https://taotoken.net/coding-plan 。日常调试模型行为、验证工具调用是否符合预期用模型对话页面就够了https://taotoken.net/model-chat 。需要管理多个 Key 或查看调用量去控制台https://taotoken.net/console 。接入文档在 https://taotoken.net/doc API Keys 管理在 https://taotoken.net/api-keys 。回到 Solon AI 本身还有几个方向值得试。一是把 MCP 服务端和 Web API 写在同一个类里一个方法同时暴露成 HTTP 接口和 MCP 工具Controller McpServerEndpoint(channel McpChannel.STREAMABLE, mcpEndpoint /mcp) public class UnifiedApi { Mapping(/api/weather) ToolMapping(description 查询天气预报) public String getWeather(Param(description 城市) String city) { return 晴14°C; } }二是异步返回工具方法返回PublisherString就能流式吐结果适合查大数据的场景。三是嵌入到现有 SpringBoot 项目里Solon AI MCP 不强制你换框架加个依赖、加个注解类就能用。最后说个实际经验MCP 工具的 description 值得反复打磨。我一开始把工具描述写得很技术化模型经常不调用改成贴近用户提问口吻的描述后触发率明显上来了。工具服务是给模型看的不是给人看的措辞要顺着模型的“理解习惯”来。
RELATED

相关推荐

解决QT安装包.run文件不能运行与QT安装完后无法启动问题:TaoToken环境下的依赖排查与启动修复

解决QT安装包.run文件不能运行与QT安装完后无法启动问题:TaoToken环境下的依赖排查与启动修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/2 17:00:44
【知识库部署】MacBook+RAG+大模型知识库 = 王炸!用 TaoToken 统一 Key 打通本地检索链路

【知识库部署】MacBook+RAG+大模型知识库 = 王炸!用 TaoToken 统一 Key 打通本地检索链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/2 17:00:44
DRV8818+STM32F722双芯片驱动方案:工业步进电机可靠运行详解

DRV8818+STM32F722双芯片驱动方案:工业步进电机可靠运行详解

如果说实验室里最常见的步进电机方案是 A4988、DRV8825 这类模块,那么在真正要跑几年甚至上十年的机器人关节和工业设备上,我更愿意用 DRV8818PWPR 这种完整的电机驱动芯片。它负责双极步进电机最核心的电流斩波和微步时序,而 STM32F722VE 负…

📅 2026/10/2 17:00:44
MORE NEWS

更多资讯

📰

辣知·化智69 西周青铜器何尊的宅兹中国

读文累的话,请点上方“耳机”或者“听”然后躺个舒服姿势,享受优质音频魅力《辣知化智》不是中国人不尊重知识产权—— 辣知君 著西周青铜器何尊上的宅兹中国一个概念的三千年演变"中国"这两个字,在今天是一个国家的简称。但当我们…

📰

没技术的普通人怎么自己做小程序?手机三步自助制作上线全流程

作为一个没什么技术的普通人,想做一个自己的小程序,第一反应就是:我行吗?会不会很难?会不会花很多钱?身边也没人懂这行,只能自己瞎琢磨,越想越觉得不现实,索性放弃了。 其…

📰

Nginx应用与运维——Nginx HTTP模块详解(动态赋值功能模块)

Nginx HTTP模块详解1、动态赋值功能模块1.1、根据浏览器动态赋值1.1.1、旧浏览器标识指令——ancient_browser1.1.2、设置旧浏览器变量值指令——ancient_browser_value1.1.3、新浏览器标识指令——modern_browser1.1.4、设置新浏览器变量值指令——modern_browser_value1.2、根…

📰

秋季眼睛过敏高发期,家里有娃的这份防护要点请收好/钟祥极博视科普

入秋之后,不少家长发现孩子开始频繁揉眼睛,眼睛红红的、眼泪汪汪。有人觉得是没睡好,有人说是看电视太多,也有人认为是“上火”。其实,秋季正是过敏性结膜炎的高发季节,家里有娃的,这件事值得花…

📰

AI-For-Beginners 词嵌入实战:用自定义数据集重跑 Embeddings 作业(PyTorch / TensorFlow 双版本)

教程人工智能机器学习深度学习 【免费下载链接】AI-For-Beginners 12 Weeks, 24 Lessons, AI for All! 项目地址: https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners 点击查看 免费下载 本文是 AI-For-Beginners 课程「5-NLP / 14-Embeddings」配套作业&am…

📰

欢迎大家能够多多关注我与我的合作者的github

alingalingling GitHub

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬