/ MCP  AI工具  Claude  工具开发  API集成  大模型  AI Agent  TypeScript 

MCP 工具开发实战:从零给 AI 接入任意 API 的完整指南


文章封面

什么是 MCP?为什么它能让 AI 接入任意 API

MCP(Model Context Protocol)是 Anthropic 于 2024 年底开源的一套标准化协议,专门用于解决一个老大难问题:如何让 AI 助手安全、可复用地调用外部工具与 API?

在 MCP 出现之前,每个 AI 应用都要自己实现工具调用逻辑,格式五花八门,维护成本极高。MCP 相当于给 AI 工具调用定义了一套"USB 接口标准"——只要遵循协议,任何工具都能即插即用。

目前已有大量开源社区 MCP Server 覆盖常见场景:浏览器自动化、数据库查询、GitHub 操作、Slack 消息、Google Drive 等。本文将带你从零实现一个能查询天气的 MCP Server,并与 Claude Desktop 集成。

环境准备与项目初始化

本教程使用 TypeScript + Node.js 实现 MCP Server。首先确认环境:

node -v   # 需要 v18+
npm -v    # 需要 v9+

创建项目并安装依赖:

mkdir my-weather-mcp && cd my-weather-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node ts-node
npx tsc --init

修改 tsconfig.json,确保以下配置:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  }
}

实现核心:编写 MCP Server

创建 src/index.ts,这是整个 Server 的入口:

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("城市名称,例如:Beijing、Shanghai"),
    units: z.enum(["metric", "imperial"]).default("metric").describe("温度单位:metric=摄氏度,imperial=华氏度"),
  },
  async ({ city, units }) => {
    // 调用 Open-Meteo 免费 API(无需 API Key)
    const geocodeRes = await fetch(
      `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}&count=1&language=zh&format=json`
    );
    const geocodeData = await geocodeRes.json() as any;
    
    if (!geocodeData.results?.length) {
      return { content: [{ type: "text", text: `找不到城市:${city}` }] };
    }
    
    const { latitude, longitude, name, country } = geocodeData.results[0];
    const tempUnit = units === "metric" ? "celsius" : "fahrenheit";
    
    const weatherRes = await fetch(
      `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code&temperature_unit=${tempUnit}`
    );
    const weatherData = await weatherRes.json() as any;
    const current = weatherData.current;
    
    const tempSymbol = units === "metric" ? "°C" : "°F";
    return {
      content: [{
        type: "text",
        text: `${name}, ${country} 当前天气:\n温度:${current.temperature_2m}${tempSymbol}\n湿度:${current.relative_humidity_2m}%\n风速:${current.wind_speed_10m} km/h`
      }]
    };
  }
);

// 定义资源:暴露支持的城市列表
server.resource(
  "supported-cities",
  "weather://supported-cities",
  async () => ({
    contents: [{
      uri: "weather://supported-cities",
      text: "支持全球任意城市,使用英文或拼音输入城市名即可。常用:Beijing, Shanghai, Shenzhen, Guangzhou, Chengdu"
    }]
  })
);

// 启动 Server(Stdio 模式,供 Claude Desktop 调用)
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Weather MCP Server 已启动");
}

main().catch(console.error);

注意几个关键点:

  • McpServer 是 SDK 提供的高层封装,负责处理所有 MCP 协议细节

  • server.tool() 第三个参数用 zod 定义参数 schema,AI 会根据这个生成调用参数

  • StdioServerTransport 表示通过标准输入输出通信,是 Claude Desktop 默认的连接方式

  • 我们使用了 Open-Meteo 的完全免费 API,无需注册,无需 API Key,适合学习使用

编译与本地测试

编译 TypeScript:

npx tsc

用 MCP Inspector 快速测试(官方调试工具):

npx @modelcontextprotocol/inspector node dist/index.js

浏览器打开 http://localhost:5173,在 Inspector 界面中点击 "Connect" 连接到你的 Server,然后找到 get_weather 工具,输入 {"city": "Shanghai"} 测试:

# 预期输出(成功):
Shanghai, China 当前天气:
温度:22.5°C
湿度:68%
风速:12.3 km/h

如果看到这样的输出,说明 MCP Server 工作正常!

接入 Claude Desktop

找到 Claude Desktop 的配置文件并编辑:

  • macOS:~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows:%APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["/绝对路径/my-weather-mcp/dist/index.js"],
      "env": {}
    }
  }
}

重启 Claude Desktop,在对话框左下角会看到一个锤子图标(工具列表),点击可见 get_weather 工具已加载。现在对 Claude 说"帮我查一下深圳今天的天气",它会自动调用你的 MCP Server!

进阶:添加 Prompt 模板和错误处理

MCP 还支持 server.prompt(),预定义对话模板:

server.prompt(
  "weather-report",
  "生成一份完整的天气播报",
  {
    city: z.string(),
    date: z.string().optional().describe("日期,默认今天"),
  },
  ({ city, date }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `请用专业播音员的口吻,为 ${city} 生成 ${date ?? "今天"} 的天气播报,包括穿衣建议和出行提示。`
      }
    }]
  })
);

生产环境中,还需要做好错误处理和日志记录:

// 所有对外请求都包裹 try/catch
try {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
  // ...
} catch (err) {
  return {
    content: [{ type: "text", text: `查询失败:${(err as Error).message}` }],
    isError: true  // 标记为错误响应,让 AI 知道需要重试或提示用户
  };
}

常见踩坑与解决方案

  • Claude Desktop 看不到工具:检查配置文件路径是否用了绝对路径,相对路径无效;确认 JSON 格式正确,可用 node -e "require('./config.json')" 验证

  • Server 启动就崩溃:记住不要在 Server 中用 console.log,Stdio 模式下标准输出是 MCP 协议通道,调试信息必须用 console.error

  • 工具参数识别不准:优化 zod 中的 .describe() 文字,AI 靠这些描述理解参数含义;参数描述越详细,AI 传值越准确

  • 异步工具超时:Claude Desktop 默认超时较短,耗时操作建议加进度提示,或将大任务拆分为多个小工具串联调用

  • 部署到服务器后无法连接:Stdio 模式只支持本地进程通信;需要远程调用则切换为 SSEServerTransport(HTTP + Server-Sent Events 模式)

发布评论

热门评论区: