ARTICLE DETAIL

资讯详情

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

基于MCP协议构建天气查询工具:从原理到Node.js实战

基于MCP协议构建天气查询工具:从原理到Node.js实战

1. 项目概述:为什么从MCP天气查询工具入手?

最近和几个做AI应用开发的朋友聊天,发现大家不约而同地都在研究一个叫“模型上下文协议”的东西,也就是MCP。这玩意儿听起来挺高大上,但说白了,它就像给大语言模型(比如ChatGPT、Claude)装上了一套标准化的“手”和“眼睛”。模型本身是个聪明的“大脑”,但它没法直接操作电脑、读取文件、查询网络数据。MCP就是定义了一套标准方法,让“大脑”可以安全、可控地指挥“手”去执行具体任务。

那为什么选择从“天气查询”这个工具开始呢?原因很简单:它是一个绝佳的MCP入门练手项目。首先,它的业务逻辑清晰——输入地点,返回天气信息。其次,它涉及了MCP最核心的几个概念:工具(Tools)的定义、服务器(Server)的实现、以及客户端(Client)的调用。最后,它需要与外部API(天气服务)交互,这正好体现了MCP“连接模型与现实世界”的核心价值。通过亲手实现一个天气查询MCP工具,你能把MCP的抽象概念迅速具象化,理解数据是如何在模型、MCP服务器和外部服务之间流转的。这比你读十篇文档都管用。

2. 核心概念与工具选型:构建MCP的基石

在动手写代码之前,我们必须把几个关键概念和工具理清楚。这就像盖房子前得先认识砖瓦和图纸。

2.1 MCP的三层架构:Server, Client & Tools

MCP的架构非常清晰,主要包含三层:

  1. MCP Server(服务器):这是你将要编写的核心部分。它对外暴露一组定义好的“工具”(Tools)。你可以把它想象成一个“技能提供者”。在我们的天气项目中,这个服务器就提供了一个叫get_weather的工具。
  2. MCP Client(客户端):这是与大语言模型(如Claude Desktop、Cursor等)集成的部分。客户端负责与MCP Server建立连接,获取可用的工具列表,并在模型需要时,代表模型去调用服务器上的工具。客户端通常由AI应用平台提供,我们不需要从头写。
  3. Tools(工具):这是MCP协议中定义的“能力单元”。每个工具都有明确的名称、描述、输入参数(input_schema)和输出。模型的“大脑”通过阅读工具的描述来决定在什么时候、使用什么参数来调用它。

对于我们开发者而言,核心工作就是实现一个MCP Server,并在其中定义好我们想让模型使用的工具。

2.2 为什么选择Node.js和官方SDK?

实现MCP Server有多种语言选择,比如Python、TypeScript等。这里我强烈推荐使用TypeScript (Node.js)并结合@modelcontextprotocol/sdk这个官方SDK。理由如下:

  • 官方背书与活跃度:这是由Anthropic(Claude的创造者)官方维护的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/node
  • @modelcontextprotocol/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.ts

3.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}&units=metric`; 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代码到JavaScript:

npx 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

  1. 全局安装MCP Inspector

    npm install -g @modelcontextprotocol/inspector
  2. 启动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"]

  3. 运行Inspector

    mcp-inspector --config ./server-config.json

    这会打开一个本地网页(通常是http://localhost:5173),这就是MCP Inspector的界面。

  4. 在Inspector中测试工具

    • 在Inspector网页中,你应该能在左侧看到连接的weather服务器。
    • 点击它,你会看到它提供的get_weather工具。
    • 在工具面板的输入框里,输入{"location": "Beijing"}
    • 点击 “Call Tool”。
    • 如果一切配置正确,右侧会显示来自OpenWeatherMap API的、格式化的北京天气信息。

成功!这证明你的MCP服务器逻辑正确,能够处理请求、调用外部API并返回结果。

4.3 集成到Claude Desktop(可选但推荐)

真正的魅力在于让AI模型使用你的工具。以Claude Desktop为例:

  1. 找到Claude Desktop的配置文件位置。
    • macOS:~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows:%APPDATA%\Claude\claude_desktop_config.json
  2. 编辑这个JSON文件(如果不存在则创建):
    { "mcpServers": { "weather": { "command": "node", "args": ["/ABSOLUTE/PATH/TO/YOUR/mcp-weather-server/dist/index.js"] } } }
    (同样,请替换为你的绝对路径。如果使用ts-node,配置方式同Inspector)。
  3. 重启Claude Desktop。
  4. 现在,当你和Claude对话时,你可以直接说:“帮我查一下东京的天气。” Claude会自动识别出它有一个get_weather工具可用,并在后台调用你的服务器,然后将结果融入它的回复中。整个过程无缝衔接,用户感知到的就是Claude“知道”了天气。

5. 进阶优化与问题排查实录

一个基础工具跑起来后,我们可以让它更健壮、更实用。下面分享几个我在实际开发中总结的优化点和常见坑位。

5.1 功能优化:从基础查询到实用工具

  1. 多单位支持:让用户或模型可以选择温度单位(摄氏/华氏)。修改工具的inputSchema,增加一个unit可选参数,然后在调用API时根据这个参数决定units字段是metric还是imperial
  2. 位置模糊处理:OpenWeatherMap对某些中文地名支持可能不完美。可以引入一个地理位置解析服务(如OpenCage Geocoder)先将地名转换为经纬度,再用经纬度去查询天气,提高准确率。
  3. 缓存机制:天气数据变化不频繁,频繁调用API会浪费额度且慢。可以引入一个简单的内存缓存(如node-cache),将相同地点的结果缓存5-10分钟。
  4. 更丰富的信息:除了当前天气,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的世界刚刚打开,更多的可能性正等待被构建。

返回列表