从开发天气MCP服务入门什么是MCP

什么是 MCP

MCP(Model Context Protocol,模型上下文协议)是一项由 Anthropic 发起的开放标准,它通过定义统一的通信格式与能力协商机制,使大型语言模型(LLMs)能够动态地发现并调用外部数据源、工具和服务,从而突破纯训练数据的局限,显著提升 AI 应用的交互和自动化能力。其核心优势在于"模型主导"的工具调用方式------由模型根据上下文决定何时、调用哪种工具及其参数,而非由应用层预先硬编码,这赋能了更灵活的 AI Agent 架构。

MCP 是 CS ( client-server) 架构,也就是客户端-服务器架构,这个和我们的 Web 端开发类似,架构图如下:

开发前准备

安装nodejs 环境

此教程天气 mcp 服务使用 typescript-sdk 开发,所以你的电脑需要安装 nodejs 环境,如何安装 nodejs 本文不做赘述,对于本教程,您需要 Node.js 版本 16 或更高版本。

安装 Cursor

你的电脑需要安装支持 MCP 服务的客户端,如 Claude for Desktop、Cursor、VsCode等,本文使用 Cursor 客户端来连接 mcp 服务。

你也可以查看其他支持 mcp 协议的客户端:modelcontextprotocol.io/clients

注册心知天气

心知天气是一家企业级高精度气象数据服务提供商,个人就可以注册使用,有一定的免费额度。

官网:www.seniverse.com/

MCP 核心概念

  • 资源 (Resources) :客户端可以读取的类似文件的数据(例如 API 响应或文件内容)
  • 工具 (Tools) :可由用户调用的函数(需经用户批准)
  • 提示 (Prompts) :预先编写的模板,帮助用户完成特定任务

了解了上面的基础知识后,让我们开始构建我们的天气服务器吧!

创建项目

shell 复制代码
# 创建一个项目文件夹
mkdir weather
cd weather

# 初始化npm项目
npm init -y

# 安装依赖
npm install @modelcontextprotocol/sdk zod axios
npm install -D @types/node typescript

# 创建文件
mkdir src
touch src/index.ts

更新你的package.json

json 复制代码
{
  "type": "module",
  "bin": {
    "weather": "./dist/index.js"
  },
  "scripts": {
    "build": "tsc && chmod 755 dist/index.js",
    "start": "node dist/index.js",
    "dev": "tsc --watch && node dist/index.js"
  },
  "files": [
    "dist"
  ],
}

在项目根目录下创建 tsconfig.json

json 复制代码
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

上面的文件都完成后,就让我开始完成天气 MCP 服务吧!

开始编写代码

src/index.ts

先在 src/index.ts 文件中加入如下内容:

js 复制代码
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios from "axios";

/**
 * 创建MCP服务器实例
 */
const server = new McpServer({
  name: "weather",
  version: "1.0.0",
  capabilities: {
    resources: {},
    tools: {},
  },
});

基础代码解释

#!/usr/bin/env node 用于告诉操作系统"调用环境变量 PATH 中的 node 可执行程序来运行本文件",从而使脚本可执行并具备跨平台的可移植性

McpServer McpServer 是 Model Context Protocol 的 TypeScript SDK 提供的核心类,用于创建符合 MCP 规范的服务器实例

StdioServerTransport StdioServerTransport 实现了 MCP 的标准输入/输出(stdio)通信层,适合在本地进程间以流的方式收发消息,MCP 支持多种传输机制,其中 stdio 传输最常用于桌面客户端与本地服务器的交互场景 。

Zod zod 是一个 TypeScript 优先的声明式数据模式验证库,可在运行时对任意数据进行类型校验,定义一次验证模式后,Zod 会自动推断出静态的 TypeScript 类型,并在 .parse() 时抛出详细错误信息。

name version:为该服务器指定唯一标识和版本号,便于调试和管理 。

capabilities.resources:用于声明该服务器可读写的"资源"(如文件内容、API 响应等)。

capabilities.tools:用于注册可被 LLM 调用的"工具"函数(如获取天气、解析地理位置等)。

如何定义工具 Tools

(Tools)工具是模型上下文协议 (MCP) 中一个强大的原语,它使服务器能够向客户端公开可执行功能。通过工具,LLM 可以与外部系统交互、执行计算并在现实世界中采取行动。

工具定义结构

js 复制代码
{
  name: string;          // 工具的唯一标识符
  description?: string;  // 人类可读的描述
  inputSchema: {         // 工具参数的 JSON 模式
    type: "object",
    properties: { ... }  // 工具特定的参数
  },
  annotations?: {        // 关于工具行为的可选提示
    title?: string;      // 工具的人类可读标题
    readOnlyHint?: boolean;    // 如果为 true,则工具不会修改其环境
    destructiveHint?: boolean; // 如果为 true,则工具可能执行破坏性更新
    idempotentHint?: boolean;  // 如果为 true,则使用相同参数重复调用不会产生额外影响
    openWorldHint?: boolean;   // 如果为 true,则工具会与外部实体交互
  }
}

示例:

js 复制代码
{
  "name": "github_create_issue",
  "description": "Create a GitHub issue",
  "inputSchema": {
    "type":"object",
    "properties": {
      "title": {"type":"string"},
      "body": {"type":"string"}
    },
    "required":["title","body"]
  }
}

定义天气 tool

了解了什么是 Tools 以及如何描述 Tools 那么我们就可以完成下面的代码了。

ts 复制代码
interface WeatherResponse {
  results: [
    {
      location: {
        id: string;
        name: string;
        country: string;
        path: string;
        timezone: string;
        timezone_offset: string;
      };
      daily: Array<{
        date: string;
        text_day: string;
        code_day: string;
        text_night: string;
        code_night: string;
        high: string;
        low: string;
        precip: string;
        wind_direction: string;
        wind_direction_degree: string;
        wind_speed: string;
        wind_scale: string;
        rainfall: string;
        humidity: string;
      }>;
      last_update: string;
    }
  ];
}

/**
 * 获取天气信息的工具
 */
server.tool(
  "get_weather",
  "获取天气预报信息",
  {
    city: z.string().describe("要获取天气预报的城市名称"),
    days: z
      .number()
      .optional()
      .describe("预测天数 (最大15, 默认3)"),
    language: z
      .string()
      .optional()
      .describe("响应语言 (默认: zh-Hans)"),
    unit: z
      .string()
      .optional()
      .describe("温度单位 (c 或 f, 默认: c)"),
  },
  async ({ city, days = 3, language = "zh-Hans", unit = "c" }) => {
    try {
      // 获取Seniverse API密钥
      const API_KEY = process.env.SENIVERSE_API_KEY;
      if (!API_KEY) {
        return {
          content: [
            {
              type: "text",
              text: "Error: Seniverse API key not found. Please set SENIVERSE_API_KEY environment variable.",
            },
          ],
          isError: true,
        };
      }

      // 发送请求获取天气预报数据
      const response = await axios.get<WeatherResponse>(
        `https://api.seniverse.com/v3/weather/daily.json?key=${API_KEY}&location=${encodeURIComponent(
          city
        )}&language=${language}&unit=${unit}&start=0&days=${days}`
      );

      // 解析响应数据 
      const { location, daily, last_update } = response.data.results[0];

      const forecastText = daily
        .map((day) => {
          return `${day.date}:
- 白天: ${day.text_day}, 夜间: ${day.text_night}
- 温度: ${day.low}°${unit.toUpperCase()} ~ ${day.high}°${unit.toUpperCase()}
- 降水概率: ${day.precip}%
- 风速: ${day.wind_speed}${unit === "c" ? "km/h" : "mph"}
- 湿度: ${day.humidity}%`;
        })
        .join("\n\n");

      return {
        content: [
          {
            type: "text",
            text: `${location.name} (${location.path}) 天气预报:
            
${forecastText}

最后更新时间: ${last_update}`,
          },
        ],
      };
    } catch (error: unknown) {
      if (axios.isAxiosError(error)) {
        const errorMessage = error.response?.data?.status || error.message;
        return {
          content: [
            {
              type: "text",
              text: `Error fetching weather data: ${errorMessage}`,
            },
          ],
          isError: true,
        };
      }
      return {
        content: [
          {
            type: "text",
            text: `Error fetching weather data: ${
              error instanceof Error ? error.message : "Unknown error"
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

代码解释

我们通过 server.tool 注册一个天气工具,代码中使用axios 来调用心知天气api,我们使用 zod来描述心知天气 API 的参数让 LLM 能知道如何调用接口。我们调用心知天气成功后,要封装其数据并按要求返回如下结构:

ts 复制代码
return {
        content: [
          {
            type: "text", // 返回的数据类型
            text: ``,  // 要返回的文本内容,内容会被 LLM 读取。
          },
        ],
      };

process.env.SENIVERSE_API_KEY 将在下文的配置中介绍如何配置。

配置客户端 mcp 服务

默认配置文件为 mcp.json,在mcp.json 文件中加入如下内容:

ts 复制代码
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["/path/to/weather/dist/index.js"],
      "env": {
        "SENIVERSE_API_KEY": "心知天气 api key"
      }
    }
  }
}

路径 /path/to/weather/dist/index.js 要根据你电脑的上的文件位置进行更改。SENIVERSE_API_KEY 就是 process.env.SENIVERSE_API_KEY 的变量。

如果配置正确,你将看到如下图所示:

调用 mcp 服务

问今天上海的天气,回答如下图所示:

完整代码

仓库地址: github.com/HelTi/mcp-s...

总结

MCP 协议不仅强化了大型语言模型的能力边界,更赋能了更灵活的 AI Agent 架构。在对话的过程中,AI 不仅仅可以告诉我们如何操作,而且可以代替人类进行一些系统操作。

在本文的教程中我们通过标准传输协议(standard input/output )在本地进行服务通信,另外mcp也支持 http 传输协议,本文不做叙述。

相关文档:

相关推荐
花生侠1 分钟前
记录:前端项目使用pnpm+husky(v9)+commitlint,提交代码格式化校验
前端
一涯8 分钟前
Cursor操作面板改为垂直
前端
我要让全世界知道我很低调15 分钟前
记一次 Vite 下的白屏优化
前端·css
1undefined217 分钟前
element中的Table改造成虚拟列表,并封装成hooks
前端·javascript·vue.js
蓝倾1 小时前
淘宝批量获取商品SKU实战案例
前端·后端·api
comelong1 小时前
Docker容器启动postgres端口映射失败问题
前端
花海如潮淹1 小时前
硬件产品研发管理工具实战指南
前端·python
用户3802258598241 小时前
vue3源码解析:依赖收集
前端·vue.js
WaiterL1 小时前
一文读懂 MCP 与 Agent
前端·人工智能·cursor
gzzeason1 小时前
使用Vite创建React初始化项目
前端·javascript·react.js