Solon AI + MCP实战:5行代码搞定天气查询,LLM从此告别数据孤岛|TaoToken统一Key接入
1. 当 LLM 被问到天气它为什么只会说“请自行查询”你问 ChatGPT 或本地部署的 Qwen“杭州明天适合跑步吗”它大概率回你一句“我无法获取实时天气建议你打开天气 App 查看”。这不是模型笨而是它被关在训练数据的笼子里——知识截止到某个时间点之后发生的、实时变化的数据它一概不知。天气、股价、库存、订单状态、本地文件内容这些都属于“模型看不见”的外部世界。传统做法是给每个数据源单独写一套 Function Calling 适配层天气接口写一遍、地图接口写一遍、数据库查询再写一遍。每接一个工具从定义 JSON Schema、处理参数校验、拼装 HTTP 请求到解析返回熟练工也得花上大半天。更麻烦的是这套适配代码和具体模型强绑定换个模型厂商就得重写一遍。十个工具就是十份重复劳动维护成本随工具数量线性上涨。MCPModel Context Protocol想解决的就是这件事。你可以把它理解成 AI 世界的 USB-C 接口工具提供方按统一协议暴露能力模型侧按统一协议发现和调用双方不用再为“你用什么格式、我用什么格式”扯皮。Solon AI 作为国内 Java 生态里较早落地 MCP 的框架把服务端注册和客户端调用都封装成了注解和 Builder天气查询这种典型场景服务端核心逻辑确实能压到 5 行左右。这篇面向的是已经会用 Solon 或至少写过 Java Web 的开发者目标很明确跑通一条“LLM 提问 → 自动调用天气 MCP 工具 → 返回真实预报”的完整链路并且用 TaoToken 的统一 Key 把模型调用这一环也收口避免在多个厂商的 Key 之间来回切换。全程可复制命令和配置都给你备好。2. 前置准备TaoToken 统一 Key 与 Solon AI 依赖在写天气工具之前先把模型调用这条线理顺。Solon AI 本身不绑定具体模型厂商它通过 OpenAI 兼容协议对接后端。TaoToken 提供的就是一个 OpenAI 兼容的统一入口你拿一个 Key 就能调用多家模型省去为每个厂商单独申请、单独配置的麻烦。先到 TaoToken 控制台创建一个 API Key。地址是 https://taotoken.net/api-keys 登录后点“新建密钥”复制那串以sk-开头的字符串存到环境变量里别硬编码进代码export TAOTOKEN_API_KEYsk-你的密钥模型对话的调试入口在 https://taotoken.net/models 你可以先在网页上确认目标模型比如gpt-4o-mini或claude-3-5-sonnet是否可用再落到代码里。接下来建一个 Maven 项目pom.xml里引入 Solon AI 和 MCP 相关依赖。版本以你本地仓库能拉到的最新稳定版为准这里给一个可用的坐标组合dependencies dependency groupIdorg.noear/groupId artifactIdsolon-ai/artifactId version3.3.0/version /dependency dependency groupIdorg.noear/groupId artifactIdsolon-ai-mcp/artifactId version3.3.0/version /dependency dependency groupIdorg.noear/groupId artifactIdsolon-web/artifactId version3.3.0/version /dependency /dependenciesSolon AI 的 MCP 模块分服务端和客户端两部分服务端负责把 Java 方法暴露成工具客户端负责连接服务端并把工具注册给模型。天气查询这个场景服务端和客户端可以放在同一个进程里跑也可以拆成两个服务先跑通单进程版本最省事。如果你打算长期做编码类 Agent把 MCP 工具链和模型调用都固定下来可以顺带看一下 Coding Plan 的说明https://taotoken.net/coding-plan 它把常用模型和额度打包适合反复调试工具调用的场景。3. 可复制配置天气 MCP Server 骨架与 settings.json先写服务端。新建WeatherMcpServer.java核心就是两个注解McpServerEndpoint声明这是一个 MCP 服务端点ToolMapping把方法标记为可被模型调用的工具。方法体里调用真实天气 API这里用伪代码占位你替换成高德、和风或任意天气服务的 HTTP 调用即可。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; McpServerEndpoint(name weather-server, sseEndpoint /mcp/weather) public class WeatherMcpServer { ToolMapping(description 获取指定城市未来三天的天气预报返回温度区间和天气状况) public String getWeather(Param(description 城市名称例如杭州) String city) { return WeatherApi.getForecast(city); } }McpServerEndpoint里的sseEndpoint指定了 SSE 流式端点路径客户端会连这个地址。ToolMapping的description很关键模型靠这段文字判断什么时候该调用这个工具写清楚“未来三天”“温度区间”比只写“查天气”命中率高得多。启动类里把 Solon 跑起来监听 8080import org.noear.solon.Solon; public class App { public static void main(String[] args) { Solon.start(App.class, args); } }服务端跑起来后客户端这边需要一份配置告诉 Solon AI 去哪里连 MCP 服务、用哪个模型。如果你用 Solon AI 的配置文件方式可以在app.yml里写solon.ai: chat: apiUrl: https://taotoken.net/api/v1/chat/completions apiKey: ${TAOTOKEN_API_KEY} model: gpt-4o-mini mcp: client: weather: apiUrl: http://localhost:8080/mcp/weather注意apiUrl指向的是 TaoToken 的 API 地址https://taotoken.net/api下的 OpenAI 兼容路径apiKey从环境变量读。这样模型调用和 MCP 工具连接就都在一份配置里了。如果你更习惯用settings.json这种结构化配置比如在 IDE 插件或独立 Agent 里等价写法是{ llm: { baseUrl: https://taotoken.net/api/v1, apiKey: ${TAOTOKEN_API_KEY}, model: gpt-4o-mini }, mcpServers: { weather: { url: http://localhost:8080/mcp/weather, transport: sse } } }transport填sse对应服务端的 SSE 端点。如果你的 MCP 服务用的是 Streamable HTTP这里改成http即可Solon AI 客户端会自动协商。4. 验证请求从 curl 到模型自动调用天气工具配置写完别急着上模型先用 curl 确认 MCP 服务端本身是活的。Solon AI 的 MCP 端点支持标准的 SSE 握手你可以用 curl 发一个初始化请求看返回curl -N http://localhost:8080/mcp/weather \ -H Accept: text/event-stream正常的话你会看到一串event: endpoint和data:开头的流式输出说明 SSE 通道建立成功。如果连接被拒绝检查 8080 端口是否被占用、Solon 是否真的启动完成。服务端确认后写客户端调用代码。核心是McpClientProvider构建工具提供者然后把它塞进模型的toolsAddimport org.noear.solon.ai.chat.ChatModel; import org.noear.solon.ai.chat.ChatResponse; import org.noear.solon.ai.mcp.client.McpClientProvider; public class WeatherChatDemo { public static void main(String[] args) { McpClientProvider toolProvider McpClientProvider.builder() .apiUrl(http://localhost:8080/mcp/weather) .build(); ChatModel chatModel ChatModel.of(https://taotoken.net/api/v1/chat/completions) .apiKey(System.getenv(TAOTOKEN_API_KEY)) .model(gpt-4o-mini) .build(); ChatResponse response chatModel.prompt(杭州明天适合户外活动吗) .options(o - o.toolsAdd(toolProvider)) .call(); System.out.println(response.getMessage().getContent()); } }跑起来后你会看到模型先输出一段“正在查询杭州天气”之类的思考然后返回类似“杭州明天多云气温 18 到 26 度适合户外活动建议带一件薄外套”的完整回答。这中间模型自动完成了工具发现、参数填充city杭州、调用服务端、拿回结果再组织语言的全过程你一行 Function Calling 的胶水代码都没写。想更直观地看模型对话效果可以到 https://taotoken.net/models 的对话界面里把 MCP 工具挂上去手动问几句观察工具调用日志。接入文档在 https://taotoken.net/doc 里面有 OpenAI 兼容接口的完整参数说明遇到字段对不上时翻一下很快能定位。5. 本篇常见错排查报错一Connection refused: localhost:8080/mcp/weather九成是服务端没启动或者sseEndpoint路径写错。先curl一下确认端口通不通再检查McpServerEndpoint的sseEndpoint值和客户端apiUrl是否完全一致注意结尾不要多斜杠。报错二模型返回“我没有天气查询能力”说明工具没注册成功。检查toolsAdd(toolProvider)是否真的执行了以及McpClientProvider的apiUrl是否指向正确的 SSE 端点。另一个常见原因是ToolMapping的description太模糊模型没识别出该调用它把描述写具体些。报错三401 Unauthorized来自 TaoTokenAPI Key 没读到或已失效。确认TAOTOKEN_API_KEY环境变量在当前 shell 里echo得出来代码里用System.getenv读取时注意大小写。如果是在 IDE 里跑IDE 的环境变量配置可能和终端不共享需要在 Run Configuration 里单独加。报错四工具调用返回空字符串天气 API 本身的问题。WeatherApi.getForecast(city)里打印一下原始 HTTP 响应确认城市名编码正确、API Key 有效、返回 JSON 结构和你解析的字段对得上。MCP 层只负责传递不负责修数据。报错五SSE 连接建立后立刻断开检查 Solon 的依赖里有没有引入冲突的 Web 容器。Solon AI 的 MCP 服务端依赖solon-web的 SSE 支持如果项目里同时引了其他 Servlet 容器可能出现流被提前关闭。排除掉多余容器依赖即可。6. 把天气工具换成你自己的数据源天气查询只是个引子。你完全可以把WeatherApi.getForecast换成查数据库、查内部工单系统、查 Git 仓库提交记录ToolMapping的注解结构不变模型侧一行代码都不用改。这就是 MCP 的价值工具的实现和模型的调用解耦了。如果你要接多个 MCP 服务在客户端配置里加多个mcpServers条目toolsAdd时把多个 provider 一起传进去模型会自动根据用户问题选择该调哪个工具。长期跑编码类 Agent 的话把模型调用固定到 Coding Plan 上配合 MCP 工具链能省掉不少切换 Key 和额度管理的琐事。最后留一个实用习惯每次新增 MCP 工具后先用 curl 单独验证服务端再挂到模型上测。服务端和模型侧分开排查定位问题的速度会快很多。