
1. 项目概述为什么从MCP天气查询工具入手最近和几个做AI应用开发的朋友聊天发现大家不约而同地都在研究一个叫“模型上下文协议”的东西也就是MCP。这玩意儿听起来挺高大上但说白了它就像给大语言模型比如ChatGPT、Claude装上了一套标准化的“手”和“眼睛”。模型本身是个聪明的“大脑”但它没法直接操作电脑、读取文件、查询网络数据。MCP就是定义了一套标准方法让“大脑”可以安全、可控地指挥“手”去执行具体任务。那为什么选择从“天气查询”这个工具开始呢原因很简单它是一个绝佳的MCP入门练手项目。首先它的业务逻辑清晰——输入地点返回天气信息。其次它涉及了MCP最核心的几个概念工具Tools的定义、服务器Server的实现、以及客户端Client的调用。最后它需要与外部API天气服务交互这正好体现了MCP“连接模型与现实世界”的核心价值。通过亲手实现一个天气查询MCP工具你能把MCP的抽象概念迅速具象化理解数据是如何在模型、MCP服务器和外部服务之间流转的。这比你读十篇文档都管用。2. 核心概念与工具选型构建MCP的基石在动手写代码之前我们必须把几个关键概念和工具理清楚。这就像盖房子前得先认识砖瓦和图纸。2.1 MCP的三层架构Server, Client ToolsMCP的架构非常清晰主要包含三层MCP Server服务器这是你将要编写的核心部分。它对外暴露一组定义好的“工具”Tools。你可以把它想象成一个“技能提供者”。在我们的天气项目中这个服务器就提供了一个叫get_weather的工具。MCP Client客户端这是与大语言模型如Claude Desktop、Cursor等集成的部分。客户端负责与MCP Server建立连接获取可用的工具列表并在模型需要时代表模型去调用服务器上的工具。客户端通常由AI应用平台提供我们不需要从头写。Tools工具这是MCP协议中定义的“能力单元”。每个工具都有明确的名称、描述、输入参数input_schema和输出。模型的“大脑”通过阅读工具的描述来决定在什么时候、使用什么参数来调用它。对于我们开发者而言核心工作就是实现一个MCP Server并在其中定义好我们想让模型使用的工具。2.2 为什么选择Node.js和官方SDK实现MCP Server有多种语言选择比如Python、TypeScript等。这里我强烈推荐使用TypeScript (Node.js)并结合modelcontextprotocol/sdk这个官方SDK。理由如下官方背书与活跃度这是由AnthropicClaude的创造者官方维护的SDK更新及时与协议标准同步性最好遇到问题也容易找到答案。开发体验优秀TypeScript提供了完善的类型提示SDK的封装让建立连接、定义工具、处理请求变得非常直观能避免很多低级错误。生态成熟Node.js的异步和非阻塞I/O特性非常适合处理MCP这种多请求、需要调用外部网络API的场景。NPM上有海量的包可以辅助开发比如我们马上会用到的axios。注意虽然Python也有社区实现的库但就目前的稳定性和文档完整性来看官方的Node.js SDK是新手入门阻力最小的选择。2.3 天气数据源的选择与考量工具的核心是数据。为天气查询工具选择一个可靠、免费或低成本、易于使用的数据源至关重要。这里有几个常见选项OpenWeatherMap老牌服务提供免费层每分钟60次调用数据全面文档清晰。免费层需要注册获取API Key。WeatherAPI另一个流行的选择免费层额度也不错提供多种数据。和风天气国内如果你主要查询国内地点这是个非常优秀的选择中文支持好免费额度足够个人开发使用。我个人的选择是 OpenWeatherMap。原因在于其国际覆盖广API设计规范社区资源多遇到问题容易搜索到解决方案。我们接下来的实现也将以它为例。关键一步请立即去 OpenWeatherMap官网 注册一个免费账户获取你的API Key。这个Key将用于在代码中认证你的请求。3. 手把手实现MCP天气查询服务器理论铺垫完毕现在进入实战环节。请确保你的开发环境已经安装了Node.js (版本18或以上)和npm。3.1 项目初始化与依赖安装首先创建一个新的项目目录并初始化mkdir mcp-weather-server cd mcp-weather-server npm init -y接着安装我们需要的核心依赖npm install modelcontextprotocol/sdk axios npm install --save-dev typescript ts-node types/nodemodelcontextprotocol/sdk MCP官方SDK。axios 一个优秀的HTTP客户端用于调用OpenWeatherMap的API。typescript,ts-node,types/node 用于TypeScript开发和执行。然后初始化TypeScript配置npx tsc --init你可以根据需要修改生成的tsconfig.json一个简单的可用于快速启动的配置如下{ compilerOptions: { target: ES2022, module: commonjs, outDir: ./dist, rootDir: ./src, strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true }, include: [src/**/*], exclude: [node_modules] }创建源代码目录和入口文件mkdir src touch src/index.ts3.2 构建MCP服务器骨架现在打开src/index.ts开始编写服务器的核心代码。我们先从导入依赖和搭建基础结构开始import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import axios from axios; // 1. 定义工具Tool的输入参数结构 // 这告诉MCP客户端和模型调用get_weather工具时需要提供一个location字符串参数。 const WEATHER_TOOL { name: get_weather, description: 获取指定城市或地区的当前天气信息。, inputSchema: { type: object, properties: { location: { type: string, description: 城市或地区名称例如: Beijing, London, Tokyo, }, }, required: [location], }, }; // 2. 创建MCP服务器实例 // WeatherServer 是我们给这个服务器起的名字。 const server new Server( { name: WeatherServer, version: 1.0.0, }, { capabilities: { tools: {}, // 这里先留空我们会在后面动态添加工具处理逻辑 }, } );3.3 实现工具处理逻辑这是服务器的“大脑”。我们需要告诉服务器当get_weather工具被调用时具体要执行什么操作。// 3. 设置工具处理函数 server.setRequestHandler(tools/call, async (request) { // 检查被调用的工具名称是否是我们定义的get_weather if (request.params.name WEATHER_TOOL.name) { const location (request.params.arguments as any).location; if (!location) { throw new Error(Location parameter is required.); } // 你的OpenWeatherMap API Key务必替换成你自己的 const API_KEY YOUR_OPENWEATHERMAP_API_KEY_HERE; // 使用标准单位摄氏度、米/秒等 const url https://api.openweathermap.org/data/2.5/weather?q${encodeURIComponent(location)}appid${API_KEY}unitsmetric; try { // 调用外部天气API const response await axios.get(url); const data response.data; // 从API响应中提取我们需要的信息 const weatherInfo { location: data.name, country: data.sys.country, temperature: ${data.main.temp}°C, feels_like: ${data.main.feels_like}°C, humidity: ${data.main.humidity}%, pressure: ${data.main.pressure} hPa, weather: data.weather[0].description, wind_speed: ${data.wind.speed} m/s, }; // 将结果格式化成易读的文本返回给MCP客户端最终给到大模型 const contentText 当前 ${weatherInfo.location} (${weatherInfo.country}) 的天气状况 - 天气${weatherInfo.weather} - 温度${weatherInfo.temperature} (体感 ${weatherInfo.feels_like}) - 湿度${weatherInfo.humidity} - 气压${weatherInfo.pressure} - 风速${weatherInfo.wind_speed} .trim(); return { content: [ { type: text, text: contentText, }, ], }; } catch (error: any) { // 错误处理网络问题、城市未找到、API Key无效等 let errorMessage 无法获取天气信息。; if (axios.isAxiosError(error) error.response) { if (error.response.status 404) { errorMessage 未找到地点 ${location}请检查名称是否正确。; } else if (error.response.status 401) { errorMessage 天气服务认证失败请检查API Key。; } else { errorMessage 天气服务返回错误: ${error.response.status}; } } // 将错误信息返回模型可以据此回复用户 return { content: [ { type: text, text: 错误: ${errorMessage}, }, ], isError: true, }; } } // 如果收到其他未知工具的调用请求抛出错误 throw new Error(Unknown tool: ${request.params.name}); });3.4 启动服务器与连接传输MCP服务器需要通过一种“传输”方式与客户端通信。对于本地开发调试最常用、最简单的方式是标准输入输出stdio。这意味着我们的服务器将通过命令行启动并通过控制台的输入输出来与客户端如Claude Desktop交换数据。// 4. 启动服务器 async function runServer() { // 使用Stdio传输这是与桌面客户端集成的最常见方式 const transport new StdioServerTransport(); await server.connect(transport); console.error(MCP Weather Server is running on stdio...); } // 5. 在服务器连接前告知客户端本服务器提供哪些工具 // 这是关键一步客户端在连接时会请求工具列表。 server.setRequestHandler(tools/list, async () { return { tools: [WEATHER_TOOL], }; }); runServer().catch((error) { console.error(Server fatal error:, error); process.exit(1); });至此一个完整的MCP天气查询服务器就编写完成了。你的src/index.ts文件现在应该包含了以上所有代码块。实操心得在开发过程中务必用你自己的真实API Key替换YOUR_OPENWEATHERMAP_API_KEY_HERE。一个常见的错误是忘记替换或误将Key提交到公开的代码仓库这会导致API调用失败或Key泄露。建议使用环境变量来管理敏感信息例如process.env.OPENWEATHER_API_KEY。4. 编译、运行与测试代码写好了我们得让它跑起来并验证是否工作。4.1 编译TypeScript并运行首先编译TypeScript代码到JavaScriptnpx tsc这会在dist目录下生成index.js文件。更便捷的方式是使用ts-node直接运行省去编译步骤特别适合开发阶段npx ts-node src/index.ts如果一切正常你会看到MCP Weather Server is running on stdio...这条信息输出到标准错误流stderr然后程序看起来就“挂起”了。这是正常的因为它正在等待来自标准输入stdin的MCP协议消息。此时你需要一个MCP客户端来连接它。4.2 使用MCP Inspector进行本地测试在集成到Claude Desktop等大型应用之前强烈建议先用一个轻量级的调试工具进行测试。这就是MCP Inspector。全局安装MCP Inspectornpm install -g modelcontextprotocol/inspector启动Inspector并连接我们的服务器 我们需要告诉Inspector如何启动我们的服务器。创建一个简单的配置文件比如server-config.json{ mcpServers: { weather: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/mcp-weather-server/dist/index.js], env: { NODE_ENV: development } } } }注意args中的路径必须替换为你项目dist/index.js的绝对路径。如果使用ts-nodecommand可以是npxargs可以是[ts-node, /ABSOLUTE/PATH/TO/src/index.ts]。运行Inspectormcp-inspector --config ./server-config.json这会打开一个本地网页通常是http://localhost:5173这就是MCP Inspector的界面。在Inspector中测试工具在Inspector网页中你应该能在左侧看到连接的weather服务器。点击它你会看到它提供的get_weather工具。在工具面板的输入框里输入{location: Beijing}。点击 “Call Tool”。如果一切配置正确右侧会显示来自OpenWeatherMap API的、格式化的北京天气信息。成功这证明你的MCP服务器逻辑正确能够处理请求、调用外部API并返回结果。4.3 集成到Claude Desktop可选但推荐真正的魅力在于让AI模型使用你的工具。以Claude Desktop为例找到Claude Desktop的配置文件位置。macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.json编辑这个JSON文件如果不存在则创建{ mcpServers: { weather: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/mcp-weather-server/dist/index.js] } } }同样请替换为你的绝对路径。如果使用ts-node配置方式同Inspector。重启Claude Desktop。现在当你和Claude对话时你可以直接说“帮我查一下东京的天气。” Claude会自动识别出它有一个get_weather工具可用并在后台调用你的服务器然后将结果融入它的回复中。整个过程无缝衔接用户感知到的就是Claude“知道”了天气。5. 进阶优化与问题排查实录一个基础工具跑起来后我们可以让它更健壮、更实用。下面分享几个我在实际开发中总结的优化点和常见坑位。5.1 功能优化从基础查询到实用工具多单位支持让用户或模型可以选择温度单位摄氏/华氏。修改工具的inputSchema增加一个unit可选参数然后在调用API时根据这个参数决定units字段是metric还是imperial。位置模糊处理OpenWeatherMap对某些中文地名支持可能不完美。可以引入一个地理位置解析服务如OpenCage Geocoder先将地名转换为经纬度再用经纬度去查询天气提高准确率。缓存机制天气数据变化不频繁频繁调用API会浪费额度且慢。可以引入一个简单的内存缓存如node-cache将相同地点的结果缓存5-10分钟。更丰富的信息除了当前天气OpenWeatherMap的API还提供预报、空气质量等。你可以定义更多工具如get_forecast或者扩展当前工具的参数来返回更多数据。5.2 常见问题与解决方案速查表问题现象可能原因排查步骤与解决方案Inspector/Claude 无法连接服务器1. 配置文件路径错误。2. Node命令执行失败。3. 服务器代码有语法错误立即崩溃。1.检查绝对路径确保args中的路径完全正确特别是使用ts-node时。一个技巧是先在对应目录下用命令行手动执行该命令看是否成功。2.查看日志Claude Desktop会在其日志文件中记录MCP服务器的启动错误。去上述配置文件的同级目录找日志文件。3.独立运行测试在项目目录下直接运行node dist/index.js观察是否有错误输出。调用工具返回“未找到地点”1. 输入的地点名称API不认识。2. 地点名称含有特殊字符或格式问题。1.尝试英文名用“Beijing”而不是“北京”试试。2.URL编码确保代码中使用了encodeURIComponent(location)来处理输入。3.提供更具体信息在工具描述中提示用户输入“城市名,国家代码”格式如“London,GB”。返回“认证失败”1. API Key未设置或错误。2. API Key对应的免费额度已用尽。1.检查代码确认API_KEY变量已正确替换。2.访问OpenWeatherMap网站登录账号在控制面板检查API Key状态和调用次数。服务器运行后无响应或卡死1. 没有正确处理请求或响应格式不符合MCP协议。2.async/await使用不当Promise未捕获。1.使用Inspector调试Inspector能清晰显示通信的原始JSON消息对比MCP协议文档检查你的请求处理器返回的结构是否正确。2.强化错误处理确保所有可能的异常都被try...catch包裹并返回格式正确的错误信息给客户端而不是让进程崩溃或挂起。Claude不主动使用工具1. 工具描述不够清晰。2. 用户提问方式不够直接。1.优化工具描述description字段要写得非常清晰说明工具用途、输入是什么。例如“获取全球城市的当前天气情况需要提供城市名称。”2.明确指令直接对Claude说“请使用天气工具查询XX的天气”看它是否会调用。这是测试工具是否成功加载的好方法。5.3 安全与生产环境考量保护API Key永远不要将硬编码的API Key提交到Git仓库。使用环境变量.env文件配合dotenv包或运行时配置来管理。输入验证与清理虽然MCP客户端和模型会进行初步校验但服务器端仍应对location参数进行基本的清理和验证防止注入攻击。限流与配额如果你的工具公开使用需要考虑对调用频率进行限制防止滥用耗尽你的API免费额度。6. 总结与延伸思考通过这个简易的MCP天气查询工具项目我们完整走通了一个MCP Server从概念到实现、再到测试集成的全流程。你亲手搭建了一个桥梁让原本“困在”文本世界的大模型获得了感知现实世界天气的能力。这个项目的价值远不止于查询天气。它提供了一个可复用的范式。下一次当你想让AI模型帮你“读取某个GitHub仓库的最新Issue”、“查询数据库里的用户数据”、“控制家里的智能灯光”时你只需要做同样的事情定义一个工具实现一个MCP Server然后将它连接到你的AI助手。我个人在实践中的体会是MCP最大的魅力在于标准化和生态。一旦工具按照协议实现它就可以被任何支持MCP的客户端Claude, Cursor, 未来可能更多的AI应用所使用。这意味着你的一次开发可以赋能多个AI入口。随着协议的发展或许未来会出现一个丰富的“MCP工具商店”开发者可以共享工具用户则可以像安装插件一样为自己AI助手扩展各种超能力。从这个小工具出发你可以尝试更复杂的场景比如一个需要多步交互的工具先搜索再选择最后执行或者结合多个API的工具链。MCP的世界刚刚打开更多的可能性正等待被构建。