拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

solon ai mcp简单使用:把 MCP 服务端接入 TaoToken 统一 Key 通道

solon ai mcp简单使用:把 MCP 服务端接入 TaoToken 统一 Key 通道

1. Solon AI MCP 接入统一 Key 通道:从多模型调用混乱到一次配置搞定

如果你正在用 Solon(OpenSolon)写 Java 后端,又想让项目里的 AI 能力支持 MCP 工具调用,大概率会遇到一个很现实的问题:模型来源太杂。本地 Ollama 跑一个 qwen,线上又想接 Claude 或 GPT 系列,每个模型一套 Key、一套 Base URL、一套鉴权方式,代码里到处散落着配置,改一次环境要翻好几个文件。

Solon AI 本身对 MCP 的支持是完整的,solon-ai-mcp可以让你把普通 Java 方法通过@ToolMapping注解发布成 Tool 服务,客户端再用McpClientWrapper把多个 MCP 服务端的工具聚合起来交给ChatModel调用。问题出在ChatModel这一层:它需要一个具体的模型服务地址和凭证。当你想在多个模型之间切换,或者团队里多人共用一套额度时,Key 的管理就成了麻烦事。

TaoToken 在这里扮演的角色,是一个统一的模型调用通道。你只需要拿到一个 Key,把ChatModel的请求地址指向 TaoToken 的 API 端点,模型 ID 按需填写,剩下的路由和鉴权由通道处理。这样 Solon 项目里的 MCP 工具调用逻辑完全不用动,只改模型接入这一处配置,就能在 Ollama、Claude、GPT 等模型之间灵活切换。

这篇文章面向的是已经有 Solon 项目、并且已经跑通过solon-ai-mcp基础示例的开发者。我会从 MCP 服务端的注册讲起,给出可复制的pom.xml依赖片段、Tool 服务代码、MCP 客户端配置,然后重点演示如何把ChatModel接到 TaoToken 通道,最后用一次完整的工具调用请求验证链路是否通畅。整个过程你可以跟着一步步操作,遇到报错也有对照排查。

适合谁看:写过 Solon 的@Component和@Mapping,对 MCP 的 Tool 发布有基本概念,但还没把模型调用统一管理起来的 Java 开发者。如果你还没接触过 Solon AI,建议先跑通官方的最小示例再回来,这篇的重点在“接入统一通道”而不是“从零学 Solon”。

2. TaoToken 前置准备:拿到统一 Key 和 API 地址

在改 Solon 代码之前,先把 TaoToken 这边的准备工作做完。这一步不复杂,但顺序别搞反,否则后面调试时会分不清是 Key 的问题还是代码的问题。

首先访问 TaoToken 官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册并登录。登录后进入控制台,找到 API Keys 管理页面,创建一个新的 Key。这个 Key 就是你后面在 Solon 配置里填的凭证,格式通常是一串以特定前缀开头的字符串。创建时建议给它起一个能识别用途的名字,比如solon-mcp-dev,方便以后在多个项目之间区分。

拿到 Key 之后,你需要确认两件事:Base URL 和可用的 Model ID。TaoToken 的 API 端点是 https://taotoken.net/api ,注意这个地址后面不加 UTM 参数,直接作为ChatModel的请求前缀使用。Model ID 则取决于你想调用哪个模型,控制台的模型列表里会列出当前可用的标识符,比如claude-sonnet-4-20250514、gpt-4o这类。你不需要记住所有模型,先选一个你打算在 Solon 项目里用的,记下它的 ID 就行。

这里有一个容易踩的坑:TaoToken 的 API 地址和模型对话页面的地址不是同一个。模型对话页面是给你在浏览器里直接测试用的,而 API 地址是给代码调用的。如果你把浏览器地址填进ChatModel.of(),请求会返回 404 或者重定向错误。正确的做法是只使用https://taotoken.net/api作为基础地址,具体的路径由 Solon AI 的 provider 实现去拼接。

另外,如果你打算在团队里共用这个 Key,建议在控制台里设置好额度限制或者按项目拆分多个 Key。Solon 项目里可以通过环境变量或者配置文件来读取 Key,不要硬编码在 Java 代码里。后面我会给出具体的配置方式。

准备工作做完后,你手里应该有三样东西:一个有效的 API Key、Base URLhttps://taotoken.net/api、一个你打算使用的 Model ID。接下来进入 Solon 项目的配置环节。

3. 可复制配置:pom 依赖、MCP 服务端与 ChatModel 接入

这一节是整篇文章的核心操作部分。我会按照“依赖 → MCP 服务端 → MCP 客户端 → ChatModel 接入 TaoToken”的顺序给出完整代码,你可以直接复制到自己的项目里,只需要替换 Key 和 Model ID。

3.1 pom.xml 依赖配置

先确认你的pom.xml里有以下依赖。这里的关键是solon-ai-mcp和它依赖的 MCP SDK 版本要对齐,否则运行时会报NoSuchMethodError或者类找不到。

<dependencies> <dependency> <groupId>org.noear</groupId> <artifactId>solon-web</artifactId> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon-boot-undertow</artifactId> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon-logging-logback</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.31</version> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon-ai</artifactId> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon-ai-mcp</artifactId> <exclusions> <exclusion> <groupId>io.modelcontextprotocol.sdk</groupId> <artifactId>mcp</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.noear.mcp.sdk</groupId> <artifactId>mcp</artifactId> <version>0.9.0-M1</version> </dependency> </dependencies>

注意solon-ai-mcp里排除了io.modelcontextprotocol.sdk:mcp,换成了org.noear.mcp.sdk:mcp:0.9.0-M1。这是 Solon 对 MCP 协议的一个适配版本,如果你不排除原版 SDK,可能会出现协议不兼容的情况。这个细节在官方文档里没有特别强调,但实际跑的时候如果遇到 SSE 连接建立后立刻断开,大概率就是这里没对齐。

3.2 MCP 服务端:发布 Tool 服务

MCP 服务端的职责是把普通的 Java 方法暴露成 AI 可以调用的工具。Solon 用@ToolMapping和@ToolParam两个注解来完成这件事。下面是一个查询天气的 Tool 服务示例:

package com.wht.mcp.server; import org.noear.solon.ai.chat.annotation.ToolMapping; import org.noear.solon.ai.chat.annotation.ToolParam; import org.noear.solon.annotation.Component; @Component public class McpServerTool { @ToolMapping(description = "查询天气预报") public String getWeather(@ToolParam(description = "城市位置") String location) { System.err.println("location:" + location); return location + "晴,30度"; } }

这里有一个编译参数需要注意:@ToolParam的description在运行时需要读取参数名,如果你没有开启-parameters编译参数,Solon 会拿不到参数名,导致工具调用时参数映射失败。解决办法是在pom.xml的maven-compiler-plugin里加上<compilerArgs><arg>-parameters</arg></compilerArgs>,或者在注解里显式指定name属性。

再来看第二个 Tool 服务,它根据天气推荐游玩地点:

package com.wht.mcp; import org.noear.solon.ai.chat.annotation.ToolMapping; import org.noear.solon.ai.chat.annotation.ToolParam; import org.noear.solon.annotation.Component; @Component public class McpServerTool { @ToolMapping(description = "查询游玩地方") public String getSpot(@ToolParam(description = "天气") String weather) { System.err.println("weather:" + weather); return weather.contains("雨") ? "图书馆或者海洋馆" : "动物园或者植物园"; } }

这两个服务分别部署在两个不同的 Solon 工程里,端口分别是 8080 和 8081。这样做的目的是模拟多个 MCP 服务端的场景,客户端需要同时连接两个服务端并聚合它们的工具列表。

3.3 MCP 客户端:聚合多个服务端

客户端的核心是McpClientWrapper,它负责连接 MCP 服务端并获取工具列表。下面是完整的客户端代码:

package com.wht.mcp; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import lombok.SneakyThrows; import org.noear.solon.ai.chat.ChatModel; import org.noear.solon.ai.chat.ChatResponse; import org.noear.solon.ai.chat.message.ChatMessage; import org.noear.solon.ai.mcp.client.McpClientWrapper; import java.util.List; import java.util.Scanner; public class McpClient { @SneakyThrows public static void main(String[] args) { McpClientWrapper mcpClient1 = new McpClientWrapper("http://localhost:8080", "/mcp/sse"); McpClientWrapper mcpClient2 = new McpClientWrapper("http://localhost:8081", "/mcp/sse"); ChatModel chatModel = ChatModel.of("https://taotoken.net/api") .provider("openai") .model("claude-sonnet-4-20250514") .apiKey(System.getenv("TAOTOKEN_API_KEY")) .defaultToolsAdd(mcpClient1.toTools()) .defaultToolsAdd(mcpClient2.toTools()) .build(); System.err.println("请开始向 AI 提问!"); Scanner scanner = new Scanner(System.in); String userInput = scanner.nextLine(); while (StrUtil.isNotEmpty(userInput)) { ChatMessage system = ChatMessage.ofSystem("您是一名精通问题总结的助手,我的问题中可能只包含一个请求,也可能包含多个问题,请帮我总结并抽取出来,保证不会丢失关键信息."); ChatMessage user = ChatMessage.ofUser(StrUtil.format("我现在的问题是“{}” 请仔细理解并总结,直接返回答案,不要进行其他额外的赘述。答案的模板必须遵循下面的形式:\n" + " 问题一###问题二###问题三\n" + " 例如:我的问题是“查询凤起路地铁站附近100米范围内的管线信息,同时帮我查一下西湖区面积大于100的地下停车场信息”,经过解析结果使用模板输出:\n\n" + " 查询凤起路地铁站附近100米范围内的管线信息###查询西湖区面积大于100的地下停车场信息。\n", userInput)); ChatResponse systemChatResponse = chatModel.prompt( CollUtil.newArrayList(user, system)).call(); System.err.println("用户的问题:" + systemChatResponse.getMessage()); List<ChatMessage> messageList = CollUtil.newArrayList(); CollUtil.newArrayList(systemChatResponse.getMessage().getContent().split("###")) .forEach(question -> { try { messageList.add(ChatMessage.ofUser(question)); ChatResponse response = chatModel.prompt(messageList).call(); messageList.add(response.getMessage()); } catch (Exception e) { e.printStackTrace(); } }); System.err.println("AI的回答:" + CollUtil.getLast(messageList).getContent()); if ("q".equals(userInput)) { break; } userInput = scanner.nextLine(); } System.err.println("对话结束!"); } }

这段代码里有几个关键点需要说明。第一,ChatModel.of("https://taotoken.net/api")里的地址是 TaoToken 的 API 端点,不是模型对话页面的地址。第二,.provider("openai")表示使用 OpenAI 兼容的协议格式,TaoToken 的 API 兼容这种格式,所以这里填openai即可。第三,.apiKey(System.getenv("TAOTOKEN_API_KEY"))从环境变量读取 Key,避免硬编码。第四,.defaultToolsAdd()把两个 MCP 服务端的工具都注册到同一个 ChatModel 上,这样模型在回答时可以根据需要调用任意一个工具。

如果你用的是配置文件而不是环境变量,可以在app.yml里这样写:

taotoken: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api model: claude-sonnet-4-20250514

然后在 Java 代码里用@Inject或者Solon.cfg().get()读取。这样切换环境时只需要改配置文件,不用动代码。

4. 验证请求:一次完整的工具调用链路

配置写完之后,必须实际跑一次请求来确认链路是通的。这一节我会给出具体的验证步骤和预期结果,你可以对照着检查自己的运行情况。

4.1 启动 MCP 服务端

先启动两个 MCP 服务端工程。如果你用的是 Solon 的Solon.start()方式,确保两个工程的端口分别是 8080 和 8081,并且都注册了/mcp/sse这个端点。启动成功后,控制台应该能看到类似Undertow started on port 8080的日志。

你可以先用浏览器或者 curl 访问一下http://localhost:8080/mcp/sse,如果返回一个 SSE 事件流(内容可能是event: endpoint之类的),说明 MCP 服务端已经正常工作了。如果返回 404,检查一下 Solon 的 MCP 插件是否已经启用,通常需要在app.yml里加上solon.ai.mcp.server.enable: true。

4.2 运行 MCP 客户端

在 IDE 里直接运行McpClient的main方法。运行之前,确保环境变量TAOTOKEN_API_KEY已经设置好了。在 Linux 或 macOS 上可以用export TAOTOKEN_API_KEY=你的Key,在 Windows 上可以用set TAOTOKEN_API_KEY=你的Key,或者在 IDE 的 Run Configuration 里配置环境变量。

程序启动后,控制台会输出请开始向 AI 提问!。这时候输入一个测试问题,比如:

杭州今天天气怎么样,适合去哪里玩?

4.3 预期结果与链路分析

输入问题后,程序会先调用一次 ChatModel,让模型把问题拆解成多个子问题。然后对每个子问题分别调用 ChatModel,这时候模型会根据注册的工具列表决定是否调用 MCP 工具。

如果一切正常,你会看到类似这样的输出:

用户的问题:杭州今天天气怎么样###适合去哪里玩 location:杭州 weather:杭州晴,30度 AI的回答:杭州今天晴,30度,适合去动物园或者植物园。

这个过程中发生了三次模型调用和两次工具调用。第一次模型调用负责拆解问题,第二次模型调用触发了getWeather工具,第三次模型调用触发了getSpot工具。所有的模型请求都经过了 TaoToken 的 API 端点,你可以在 TaoToken 控制台的请求日志里看到对应的调用记录。

如果你在控制台日志里看到了location:杭州和weather:杭州晴,30度这两行输出,说明 MCP 工具确实被调用了,而且参数传递是正确的。如果只看到了模型回答但没有工具调用日志,说明模型没有触发工具,可能是工具描述不够清晰,或者模型本身不支持 function calling。

4.4 验证 TaoToken 通道是否生效

要确认请求确实走了 TaoToken 通道,最直接的方法是查看 TaoToken 控制台的用量统计。登录控制台后,在请求日志或用量页面应该能看到刚才的几次调用记录,包括模型 ID、请求时间、Token 消耗量。如果这里没有记录,说明请求没有到达 TaoToken,需要检查ChatModel.of()里的地址是否正确。

另一个验证方法是临时把apiKey改成一个错误的值,重新运行程序。如果请求返回 401 错误,说明鉴权环节确实经过了 TaoToken。如果返回的是连接超时或者 404,说明地址配置有问题。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

这一节整理几个实际接入过程中最容易遇到的报错,以及对应的排查思路。这些报错信息你可能会在控制台或者日志里看到,对照着检查能省不少时间。

5.1 401 Unauthorized

这是最常见的鉴权失败报错。可能的原因有三个:Key 没有正确设置、Key 已经失效、请求头里的鉴权格式不对。

先检查环境变量TAOTOKEN_API_KEY是否真的被程序读到了。可以在代码里加一行System.err.println(System.getenv("TAOTOKEN_API_KEY"))来确认。如果输出是null,说明环境变量没设置成功,检查一下 IDE 的 Run Configuration 或者 shell 的 export 语句。

如果 Key 读到了但还是 401,去 TaoToken 控制台确认这个 Key 是否还在有效期内,有没有被禁用。另外注意,有些 HTTP 客户端会自动在 Key 前面加Bearer前缀,有些不会。Solon AI 的apiKey()方法通常会自动处理,但如果你手动构造请求头,需要确认格式是Authorization: Bearer <你的Key>。

5.2 local proxy failed 或 connection refused

这个报错通常出现在 MCP 客户端连接 MCP 服务端的时候。如果你看到local proxy failed或者Connection refused,先确认两个 MCP 服务端是否已经启动,端口是否和代码里写的一致。

McpClientWrapper的构造函数第一个参数是基础地址,第二个参数是路径。如果你写的是new McpClientWrapper("http://localhost:8080", "/mcp/sse"),那么实际请求的地址是http://localhost:8080/mcp/sse。检查一下服务端是否真的在这个路径上暴露了 SSE 端点。

还有一种情况是防火墙或者安全组拦截了本地端口。如果你在容器里运行,确认端口映射是否正确。本地开发一般不会有这个问题,但如果你用了 Docker,需要把 8080 和 8081 端口暴露出来。

5.3 reading choices 报错

reading choices这个报错通常出现在解析模型响应的时候。TaoToken 返回的响应格式是 OpenAI 兼容的,包含choices数组。如果 Solon AI 在解析时找不到choices字段,就会报这个错。

可能的原因是 Model ID 填错了,导致 TaoToken 返回了一个错误响应而不是正常的模型输出。去控制台确认你填的 Model ID 是否在可用列表里。另一个原因是 provider 设置不对,如果你填的是ollama但实际请求的是 TaoToken 的 OpenAI 兼容接口,响应格式会对不上。记住:接入 TaoToken 时 provider 填openai。

如果确认 Model ID 和 provider 都没问题,还是报reading choices,可以打开 debug 日志看看原始响应内容。在app.yml里加上solon.logging.level: debug,然后重新运行,日志里会打印出完整的 HTTP 响应体,方便定位问题。

5.4 OAuth 相关报错

如果你看到OAuth或者token refresh failed之类的报错,说明请求被路由到了一个需要 OAuth 鉴权的端点。这种情况通常是因为 Base URL 填成了模型对话页面的地址,而不是 API 地址。

TaoToken 的 API 地址是https://taotoken.net/api,这个地址走的是 Key 鉴权,不需要 OAuth。如果你填的是其他地址,可能会触发 OAuth 流程。检查ChatModel.of()里的地址,确保只使用 API 端点。

另外,如果你在代码里同时配置了apiKey和 OAuth 相关的参数,可能会冲突。Solon AI 的ChatModel默认使用apiKey鉴权,不需要额外配置 OAuth。把多余的配置去掉,只保留apiKey即可。

5.5 工具没有被调用

这个问题不算报错,但很常见。模型返回了回答,但没有触发任何 MCP 工具。原因通常是工具描述不够清晰,或者模型本身不支持 function calling。

先检查@ToolMapping的description是否准确描述了工具的用途。描述越具体,模型越容易判断什么时候该调用。比如“查询天气预报”就比“获取信息”要好得多。

然后确认你使用的 Model ID 支持 function calling。不是所有模型都支持工具调用,如果你选了一个不支持 tool use 的模型,它只会直接回答而不会调用工具。在 TaoToken 控制台查看模型列表时,注意看每个模型的能力标注,选择支持 function calling 的模型。

如果工具描述和模型都没问题,但工具还是没被调用,可以尝试在 system message 里明确告诉模型“你可以使用工具来获取实时信息”。有时候模型需要一点提示才会主动调用工具。

6. 把统一 Key 通道用起来:长期编码与 Agent 场景的接入建议

走到这里,你已经完成了 Solon AI MCP 服务端接入 TaoToken 统一 Key 通道的完整流程。从依赖配置、Tool 服务发布、MCP 客户端聚合,到 ChatModel 指向 TaoToken API 端点,再到实际验证和报错排查,整条链路是通的。

如果你打算把这个方案用在长期编码或者 Agent 场景里,有几个实践建议可以参考。第一,把 Key 和 Base URL 放在配置文件或环境变量里,不要硬编码。团队协作时,每个人用自己的 Key,或者用一个共享 Key 但设置好额度限制。第二,MCP 服务端的工具描述要持续优化,工具越多,模型判断调用哪个工具的难度越大,清晰的描述能显著提升调用准确率。第三,如果你需要频繁切换模型做对比测试,可以在ChatModel构建时把 Model ID 参数化,通过配置读取,这样不用改代码就能换模型。

对于需要长期运行的 Agent 服务,建议关注 TaoToken 的 Coding Plan 方案,它在额度和调用方式上更适合持续性的编码辅助场景。你可以在控制台里查看具体的套餐说明,根据自己的调用量选择合适的方案。

接入文档和 API Keys 管理都在控制台里可以找到。如果你在配置过程中遇到了这篇文章没覆盖的报错,先去文档里查一下错误码的含义,大部分常见问题都有说明。模型对话页面可以用来快速测试某个 Model ID 是否可用,确认没问题后再填到 Solon 代码里。

返回列表