首页/新闻资讯/正文详情

用 Spring Boot 写一个真正能跑的 MCP Server:接入 Open-Meteo 天气查询(TaoToken 统一 Key 配置版)

发布时间:2026/9/26 16:21:19 来源:云帆数科 栏目:资讯中心
用 Spring Boot 写一个真正能跑的 MCP Server:接入 Open-Meteo 天气查询(TaoToken 统一 Key 配置版)
1. 从一次「连不上」的调试说起Spring Boot MCP Server 到底难在哪如果你最近在搜 Spring Boot、MCP Server、Open-Meteo、Spring AI、STDIO 这几个词大概率是想干一件事让本地跑起来的 Java 服务被 Claude Desktop、Cursor 或者 MCP Inspector 当成一个标准工具来调用。听起来不难但真正动手时很多人卡在第一步——服务明明启动了客户端却一直连不上或者连上了但工具列表是空的。我自己第一次搭的时候也踩了这个坑。JAR 包能跑日志看着一切正常可 Inspector 就是秒断。后来才发现问题出在 STDIO 模式下标准输出被 Spring 的启动日志污染了。MCP 的 JSON-RPC 消息走的就是 stdin/stdout你往里塞一行 Banner客户端解析就崩了。这篇就围绕这个真实场景来写用 Spring Boot 3.5 Spring AI 2.0 搭一个基于 STDIO 的 MCP Server接入免费的 Open-Meteo 天气 API暴露一个get_weather工具。整条链路不需要大模型 API Key也不需要申请天气服务密钥打包后就能通过 MCP Inspector 验证。适合谁适合已经会写 Spring Boot、想快速把 MCP 跑通、不想在概念上绕圈的 Java 开发者。链路其实只有一条MCP Client 通过 STDIO 发 JSON-RPC 请求 → Spring Boot MCP Server 收到调用 → 工具内部先调 Open-Meteo Geocoding API 把城市名转成经纬度 → 再调 Forecast API 拿实时天气 → 返回结构化结果。MCP 负责「怎么被发现和调用」Open-Meteo 负责「数据从哪来」两件事别混在一起。2. TaoToken 前置统一 Key 与 API 通道怎么接在讲代码之前先把 Key 和通道这件事说清楚。MCP Server 本身不强制依赖大模型但一旦你要把它接到 Claude Code、Cursor 这类客户端里或者后续想用统一的 API 通道管理模型调用就需要一个稳定的入口。我这边用的是 TaoToken 的统一 Key 方案官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。它的作用是把模型调用和工具调用的入口收敛到一个 Key 上省得你在多个平台之间来回切换配置。对于这个天气 MCP Server 来说TaoToken 不是必需的——Open-Meteo 完全免费且无需鉴权——但如果你打算把这个 Server 接到一个真实的 Agent 工作流里统一 Key 会让配置干净很多。具体操作上你需要先去控制台创建一个 API Key。控制台入口在这里https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建完之后Key 的管理页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 可以随时查看和轮换。拿到 Key 之后客户端侧的配置通常写在一个settings.json或者类似的配置文件里。以 Claude Code 风格的配置为例片段大概长这样{ mcpServers: { weather-mcp-server: { command: java, args: [ -jar, /absolute/path/weather-mcp-server-0.0.1-SNAPSHOT.jar ], env: { TAOTOKEN_API_KEY: sk-你的统一Key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }这里TAOTOKEN_API_KEY和TAOTOKEN_BASE_URL是给后续扩展用的。当前这个天气 Demo 不会真的去调大模型但把环境变量预留好等你以后想在这个 Server 里加一个「用模型总结天气」的工具就不用再改配置结构了。注意不要把 Key 硬编码进 Java 代码或者提交到 Git。用环境变量注入是最省事的做法Spring Boot 里直接Value(${TAOTOKEN_API_KEY:})就能读到。如果你只是想先验证模型对话通道是否正常可以走这个入口https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。长期做编码和 Agent 的话Coding Plan 的入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。Claude Code 相关的配置可以参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。3. 可复制配置pom、application.yml 与 MCP 工具骨架3.1 Maven 依赖先看pom.xml。核心是引入 Spring AI BOM 和 MCP Server Starter。注意 STDIO 场景用的是spring-ai-starter-mcp-server不是 WebFlux 或 WebMVC 那个版本。properties java.version21/java.version spring-ai.version2.0.0/spring-ai.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdorg.springframework/groupId artifactIdspring-web/artifactId /dependency /dependencies这里有个细节我没有引入spring-boot-starter-web。因为 STDIO 模式下不需要 Web 容器少启动一个 Tomcat 能让边界更清晰也避免端口占用之类的干扰。spring-web只是为了用RestClient去调 Open-Meteo。3.2 application.yml配置文件是第二个容易翻车的地方。看下面这段spring: application: name: weather-mcp-server main: web-application-type: none banner-mode: off ai: mcp: server: name: weather-mcp-server version: 1.0.0 type: SYNC stdio: true annotation-scanner: enabled: true logging: level: root: OFFweb-application-type: none告诉 Spring 不要起 Web 容器。banner-mode: off关掉启动 Banner。logging.level.root: OFF把日志压到最低。这三条都是为了保护 stdout 的纯净度。为什么要这么狠因为 STDIO 模式下stdout 承载的是 MCP 的 JSON-RPC 消息。你往里混一行Started WeatherMcpServerApplication in 2.3 seconds客户端读到的就不是合法 JSON直接解析失败。我第一次调试时遇到的现象就是 Inspector 一直连接失败单独运行程序看起来毫无异常问题恰恰出在「正常输出」上。处理原则不要在 STDIO Server 里用System.out.println()日志写到 stderr 或文件工具返回值通过 MCP 框架返回不要自己打印。3.3 Open-Meteo 客户端Open-Meteo 的天气接口接收经纬度但用户只会说「杭州天气怎么样」。所以要先调 Geocoding API 把城市名转成坐标再调 Forecast API。先定义返回给 Agent 的结果对象public record WeatherResult( String city, String country, double latitude, double longitude, double temperature, double apparentTemperature, int humidity, double windSpeed, int weatherCode, String observedAt) { }然后是客户端封装。为了让你看清核心逻辑这里只保留必要字段Service public class OpenMeteoClient { private final RestClient restClient RestClient.create(); public WeatherResult getCurrentWeather(String city) { GeoResponse geo restClient.get() .uri(uriBuilder - uriBuilder .scheme(https) .host(geocoding-api.open-meteo.com) .path(/v1/search) .queryParam(name, city) .queryParam(count, 1) .queryParam(language, zh) .queryParam(format, json) .build()) .retrieve() .body(GeoResponse.class); if (geo null || geo.results() null || geo.results().isEmpty()) { throw new IllegalArgumentException(没有找到城市 city); } GeoLocation location geo.results().getFirst(); ForecastResponse forecast restClient.get() .uri(uriBuilder - uriBuilder .scheme(https) .host(api.open-meteo.com) .path(/v1/forecast) .queryParam(latitude, location.latitude()) .queryParam(longitude, location.longitude()) .queryParam(current, temperature_2m,apparent_temperature, relative_humidity_2m,weather_code,wind_speed_10m) .queryParam(timezone, auto) .build()) .retrieve() .body(ForecastResponse.class); if (forecast null || forecast.current() null) { throw new IllegalStateException(天气服务暂时没有返回有效数据); } CurrentWeather current forecast.current(); return new WeatherResult( location.name(), location.country(), location.latitude(), location.longitude(), current.temperature_2m(), current.apparent_temperature(), current.relative_humidity_2m(), current.wind_speed_10m(), current.weather_code(), current.time()); } }响应对象用 Record 表达简洁且不可变record GeoResponse(ListGeoLocation results) {} record GeoLocation(String name, String country, double latitude, double longitude) {} record ForecastResponse(CurrentWeather current) {} record CurrentWeather( String time, double temperature_2m, double apparent_temperature, int relative_humidity_2m, double wind_speed_10m, int weather_code) {}这里我没有直接返回 Open-Meteo 的原始 JSON。外部接口字段经常很多而 Agent 真正需要的是稳定、清晰、语义明确的工具结果。自己定义 DTO也能隔离第三方接口变化。3.4 MCP 工具注册Spring AI 2.0 可以扫描 Spring Bean 上的 MCP 注解。工具类这样写Component public class WeatherTools { private final OpenMeteoClient openMeteoClient; public WeatherTools(OpenMeteoClient openMeteoClient) { this.openMeteoClient openMeteoClient; } McpTool( name get_weather, description 查询指定城市的实时天气包括温度、体感温度、湿度和风速) public WeatherResult getWeather( McpToolParam( description 城市名称例如杭州、北京或 Chicago, required true) String city) { if (city null || city.isBlank()) { throw new IllegalArgumentException(城市名称不能为空); } return openMeteoClient.getCurrentWeather(city.trim()); } }工具描述不是可有可无的注释。MCP 客户端会把工具名称、描述和参数 Schema 提供给模型模型据此决定何时调用以及传什么参数。所以描述至少要回答三个问题这个工具能做什么、什么时候该调用、参数格式是什么。像query、execute这种过于宽泛的名称工具一多就很容易让模型选错。4. 验证请求打包、Inspector 调用与成功结果配置写完了接下来是验证。先打包./mvnw clean package然后通过 MCP Inspector 启动 JARnpx modelcontextprotocol/inspector \ java \ -jar \ /absolute/path/weather-mcp-server-0.0.1-SNAPSHOT.jarWindows PowerShell 可以写成一行npx modelcontextprotocol/inspector java -jar D:\project\weather-mcp-server\target\weather-mcp-server-0.0.1-SNAPSHOT.jar打开 Inspector 后按这个顺序操作连接类型选 STDIO查看 Tools 列表选择get_weather输入「杭州」点击运行。如果一切正常你会看到类似这样的返回{ city: 杭州, country: 中国, latitude: 30.29365, longitude: 120.16142, temperature: 18.4, apparentTemperature: 17.9, humidity: 72, windSpeed: 8.3, weatherCode: 3, observedAt: 2025-01-15T14:00 }看到城市、温度、湿度和风速都出来了说明从 MCP 协议到外部天气 API 的整条链路已经跑通。这一步是整个 Demo 的关键验证点过了这里剩下的就是把它接到真实客户端里。如果你用的是 Claude Desktop 或 Cursor把第 2 节里的settings.json片段填进去重启客户端工具列表里就会出现get_weather。调用方式和 Inspector 里一样输入城市名即可。5. 本篇常见错排查5.1 Inspector 秒断或一直连不上这是最高频的问题九成以上是 stdout 被污染。检查三件事banner-mode是否设为offlogging.level.root是否设为OFF代码里有没有System.out.println()。Spring 的启动日志默认走 stdout在 STDIO 模式下必须关掉。5.2 工具列表为空如果 Inspector 能连上但 Tools 列表是空的先确认annotation-scanner.enabled是否为true再确认WeatherTools类上有没有Component。Spring AI 只扫描 Spring Bean 上的McpTool注解普通类不会被识别。5.3 城市名解析失败Open-Meteo 的 Geocoding API 对中文城市名支持还行但偶尔会有歧义。如果返回「没有找到城市」可以试试用拼音或者英文名。另外count参数设成 1 只取第一个结果如果第一个结果不准确可以调大这个值再手动筛选。5.4 天气接口超时Open-Meteo 是免费服务偶尔会有网络抖动。生产环境里应该给RestClient配上连接超时和读取超时比如RestClient restClient RestClient.builder() .requestFactory(new SimpleClientHttpRequestFactory() {{ setConnectTimeout(5000); setReadTimeout(10000); }}) .build();天气查询属于只读操作可以对网络抖动做少量重试。但如果以后换成「创建订单」「发送邮件」这类有副作用的工具就必须先考虑幂等不能无脑重试。5.5 返回体字段为 nullOpen-Meteo 的current字段在某些参数组合下可能返回空。检查queryParam(current, ...)里的字段名是否拼写正确比如temperature_2m不能写成temperature。字段名错了不会报错只会返回 null。6. 把这条链路接到你的工作流里天气 Demo 跑通之后你会发现 MCP Server 的骨架其实很通用一个 Spring Boot 应用一个McpTool注解的方法一个外部 API 客户端。换掉 Open-Meteo换成你自己的业务接口就是一个能用的 Agent 工具。如果你打算长期做编码和 Agent 相关的事情建议把 Key 和通道统一管理起来。API Key 的创建和管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 模型对话验证在 https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 长期编码和 Agent 场景可以看 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。Claude Code 的配置参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。最后留一个实用技巧调试 STDIO MCP Server 时把日志重定向到文件比如java -jar app.jar 2server.log这样既不影响协议通道又能保留排查线索。这个习惯能帮你省下不少「明明启动了却连不上」的时间。

相关推荐

用 Claude Code 从零构建 GitHub Star 管理器:TaoToken 统一 Key 配置与 Chrome 扩展 Manifest V3 骨架
用 Claude Code 从零构建 GitHub Star 管理器:TaoToken 统一 Key 配置与 Chrome 扩展 Manifest V3 骨架

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

Python校园消费行为分析:从模拟数据到聚类建模的完整流程
Python校园消费行为分析:从模拟数据到聚类建模的完整流程

简介:用于 Python 毕业设计的学生校园消费行为分析项目包,面向需要完成数据挖掘、数据可视化或个人消费场景调研题目的高校学生及开发者。项目围绕校园消费场景,覆盖数据清洗、学生表与消费记录关联、食堂就餐人数与时间分布、不同性别与专业… · 2026/9/26 16:21:13

前三季度漏洞披露突破7万条:2026软件供应链安全形势持续严峻
前三季度漏洞披露突破7万条:2026软件供应链安全形势持续严峻

数据统计来源:信盾数据源数字不会说谎:漏洞增长正在加速 截至 2026 年 9 月,信盾数据源构建的安全漏洞知识库已收录漏洞 422,707 条,覆盖 CVE、CNNVD、CNVD 三大主流漏洞编号体系(分别收录 39.8 万、29.0 万、13.1 万条… · 2026/9/26 16:21:13

[Ai Agent] 11 MCP进阶:手写客户端,让MCP连接万物(Client)——TaoToken 统一 Key 接入 Stdio 与 Streamable HTTP
[Ai Agent] 11 MCP进阶:手写客户端,让MCP连接万物(Client)——TaoToken 统一 Key 接入 Stdio 与 Streamable HTTP

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

SpringBoot+Vue3相亲网站全栈项目实战:数据库设计、匹配算法与部署避坑指南
SpringBoot+Vue3相亲网站全栈项目实战:数据库设计、匹配算法与部署避坑指南

最近有个相亲网站的源码项目收尾了,前后端分离,Java SpringBootVue3MyBatisMySQL这套组合,从零搭到能跑通核心业务,整个过程踩坑无数,但也把很多网上讲得含糊的地方彻底搞明白了。今天不聊虚的,直接把这套系… · 2026/9/26 16:53:16

SpringBoot+MyBatis+JSP图书管理系统实战:避坑与进阶技巧
SpringBoot+MyBatis+JSP图书管理系统实战:避坑与进阶技巧

简介:这是一套基于SpringBoot、MyBatis与JSP构建的图书管理系统完整项目源码,面向具备Java Web基础、希望深入理解企业级开发流程的开发者与在校学生。系统覆盖图书增删改查、分类管理、借阅归还、分页查询等核心业务,采用MVC架构&#xff0c… · 2026/9/26 16:53:16

腾讯TokenHub上线之后:大厂验证聚合分发赛道,开发者如何选平台
腾讯TokenHub上线之后:大厂验证聚合分发赛道,开发者如何选平台

2026年5月,腾讯云TokenHub的上线在圈内引发热议:用一个API Key聚合自研混元与DeepSeek、Kimi、MiniMax、智谱GLM等第三方模型,大厂亲自下场做Token聚合分发生意。这对开发者意味着什么?第三方聚合平台还有多少空间?本文展开聊聊,并把第一个推荐的第三方平台给到词元之河(Toke… · 2026/9/26 16:53:16

NAT路由访问过程中源目端口源目IP的变化:用 TaoToken 统一 Key 抓包验证与配置骨架
NAT路由访问过程中源目端口源目IP的变化:用 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/9/26 16:53:16

SpringBoot+MyBatis+JSP图书管理系统实战:从搭建到部署避坑
SpringBoot+MyBatis+JSP图书管理系统实战:从搭建到部署避坑

简介:这是一套面向Java Web初学者与课程设计者的图书管理系统完整源码,采用SpringBoot整合MyBatis持久层与JSP视图,覆盖图书增删改查、分类管理、借阅归还及分页展示等核心业务,适合作为毕业设计、实训项目或框架入门练手。压缩包… · 2026/9/26 16:53:09

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

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

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码