构建你的第一个 MCP 服务器:新手教程

227 阅读4分钟

本文首发【零一探秘】公众号
在这篇文章中,我将构建一个天气查询的MCP Server,让 AI 代理(如 Cursor)能够获取实时天气数据。通过这个简单的示例,让你掌握模型上下文协议(MCP)的基本用法和开发流程。

大致的流程如下:

  • 用 TypeScript SDK 从零写一个 MCP Server
  • 接入真实的天气 API
  • 跟Cursor IDE 集成
  • 学会使用 MCP Inspector 调试

在构建之前,需要安装node.js环境以及Cursor IDE(或者是Claude、VS Code都可),另外最好掌握基本的TypeScript/JavaScript 语法.

什么是 MCP 服务器?

模型上下文协议(MCP)服务器是连接 AI 代理与外部工具和数据源的桥梁。你可以把它们看作帮助 AI 理解和操作真实世界应用的“翻译器”。

举个例子,当你在 Cursor 里查询天气时,它会返回如下结果:

“我无法通过当前的工具访问实时天气数据或天气 API。”

解决方案: 这时,我们就可以通过构建一个MCP Server, 让 AI 代理能够访问实时数据并执行相关操作。

在这个场景中, 我们的天气服务器将作为一个工具,任何兼容 MCP 协议的 AI 代理 都可以调用它来获取全球任意城市的实时天气信息。

下面我们一步步来完成这个MCP 工具的构建:

第一步:项目初始化

  1. 初始化项目
mkdir mcp-weather-server
cd mcp-weather-server
npm init -y

2. 创建主文件 touch main.ts 3. 在编辑器中打开 ⁠package.json⁠,添加 ⁠type⁠ 字段启用 ES 模块:

{
  "name""mcp-weather-server",
  "version""1.0.0",
  "type""module",
  "scripts": {
    "test""echo "Error: no test specified" && exit 1"
  }
}

第二步:安装依赖

  1. 安装 MCP SDK
npm install @modelcontextprotocol/sdk

2. 安装 Zod 用于数据校验

npm install zod

现在 package.json⁠ 依赖应如下所示:

"dependencies": {
  "@modelcontextprotocol/sdk""^1.13.1",
  "zod""^3.25.67"
}

第三步:构建基础服务器

打开 main.ts⁠,按以下步骤操作。

  1. 引入所需模块
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from "zod";

2. 创建服务器实例

const server new McpServer({
  name"Weather Server",
  version"1.0.0"
});

3. 定义你的第一个工具

工具是 AI 代理可以调用的函数。我们创建一个 ⁠get-weather⁠ 工具:

server.tool(
  'get-weather',
  'Tool to get the weather of a city',
  {
    city: z.string().describe("The name of the city to get the weather for")
  },
  async({ city }) => {
    // 目前先返回静态内容
    return {
      content: [
        {
          type: "text",
          text: `The weather in ${city} is sunny`
        }
      ]
    };
  }
);

4. 设置通信方式

const transport = new StdioServerTransport();
server.connect(transport);

第四步:用 MCP Inspector 测试

在添加真实天气数据前,先用 MCP Inspector(MCP 服务器的网页调试工具)测试你的服务器。

启动 Inspector

npx -y @modelcontextprotocol/inspector npx -y tsx main.ts

终端会输出本地地址和 session token,点击带 token 的链接即可。

连接并测试

  1. 点击 Inspector 里的“Connect”
  2. 选择“Tools”导航栏
  3. 选择 get-weather⁠ 工具
  4. 输入城市名(如“Shanghai”),点击“Run Tool”

你会看到响应:“The weather in Shanghai is sunny”

第五步:接入真实天气数据

我们用 Open-Meteo 免费天气 API(无需 API key)。

原理很简单:

  1. 先用地理编码 API 把城市名转成坐标
  2. 再用天气 API 查这个坐标的天气

工具代码升级如下:

server.tool(
  'get-weather',
  '查询指定城市天气',
  {
    city: z.string().describe("要查询天气的城市名")
  },
  async({ city }) => {
    try {
      // 第一步:查城市坐标
      const geoResponse = await fetch(
        `https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1&language=en&format=json`
      );
      const geoData = await geoResponse.json();

      if (!geoData.results || geoData.results.length === 0) {
        return {
          content: [
            {
              type"text",
              text`没找到城市 "${city}",请检查拼写再试一次。`
            }
          ]
        };
      }

      // 第二步:查天气
      const { latitude, longitude } = geoData.results[0];
      const weatherResponse = await fetch(
        `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code&hourly=temperature_2m,precipitation&forecast_days=1`
      );
      const weatherData = await weatherResponse.json();

      return {
        content: [
          {
            type"text",
            textJSON.stringify(weatherData, null2)
          }
        ]
      };
    } catch (error) {
      return {
        content: [
          {
            type"text",
            text`获取天气失败:${error.message}`
          }
        ]
      };
    }
  }
);

重启 MCP Inspector,重新连接,再输入“Tokyo”或“New York”试试,就能看到最新的天气数据了。

第六步:与 Cursor IDE 集成

我们打开cursor settings配置面板,找到tools一栏,点击添加(最后一行路径替换为你本地的代码路径):

然后我们可以看到天气MCP Server已经启动。

启动并测试

  1. 点击 MCP 面板中服务器名旁的“Start”
  2. 状态应为“Running”
  3. 切换到 Copilot 侧边栏 → “Agent Mode”
  4. 询问:“What’s the weather like in Tokyo?”

现在我们在Cursor的agent模式下询问:What's the weather like in Shanghai,可以看到正确调用了天气查询MCP Server:

通过上述流程,可以看到在cursor中调用MCP 工具已经将原始 JSON 转换为美观的天气报告。

完整代码参考

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

const server = new McpServer({
  name"Weather Server",
  version"1.0.0"
});

server.tool(
  'get-weather',
  '查询指定城市天气',
  {
    city: z.string().describe("要查询天气的城市名")
  },
  async({ city }) => {
    try {
      // 第一步:查城市坐标
      const geoResponse = await fetch(
        `https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1&language=en&format=json`
      );
      const geoData = await geoResponse.json();

      if (!geoData.results || geoData.results.length === 0) {
        return {
          content: [
            {
              type"text",
              text`没找到城市 "${city}",请检查拼写再试一次。`
            }
          ]
        };
      }

      // 第二步:查天气
      const { latitude, longitude } = geoData.results[0];
      const weatherResponse = await fetch(
        `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code&hourly=temperature_2m,precipitation&forecast_days=1`
      );
      const weatherData = await weatherResponse.json();

      return {
        content: [
          {
            type"text",
            textJSON.stringify(weatherData, null2)
          }
        ]
      };
    } catch (error) {
      return {
        content: [
          {
            type"text",
            text`获取天气失败:${error.message}`
          }
        ]
      };
    }
  }
);

const transport = new StdioServerTransport();
server.connect(transport);

写在最后

好了,现在我们已经完成了一个最基础的 MCP 天气服务器搭建:

  • ✅ 从零实现了 MCP 服务器
  • ✅ 能查到实时天气
  • ✅ 与 Cursor IDE集成
  • ✅ MCP 的基本用法也掌握了

其实 MCP 服务器没想象中复杂,查天气只是个入门示例,后续你完全可以扩展查数据库、抓网页、连各种服务都行。 今天的分享就到这里,如果有疑问,欢迎留言讨论。