MCP Server 开发入门:2 小时从零构建你的第一个 AI 工具服务器

|孙浩然|16 分钟

全栈开发者,AI 工具链爱好者,开发了 3 个开源 MCP Server,累计获得 2000+ GitHub stars

我为什么开始写 MCP Server

2024 年 11 月,Anthropic 发布了 MCP(Model Context Protocol)协议。我当时正在折腾 Claude Desktop,看到官方演示里 Claude 直接读取本地文件、查询数据库、调用 GitHub API,整个人都兴奋了——这不就是我梦寐以求的"AI 能动手干活"吗?

但兴奋劲过了之后,我发现一个问题:官方提供的 MCP Server 就那么几个,filesystem、github、postgres、fetch……够用是够用,但离"好用"还差得远。我想让 Claude 帮我查天气,没有现成的 Server;想让它帮我查快递,也没有;想让它读我 Notion 里的笔记,还是得自己写。

于是我就硬着头皮开始研究怎么自己写一个 MCP Server。说实话,一开始挺懵的——文档散落在各处,TypeScript SDK 的类型定义还在频繁变动,网上几乎找不到完整的教程。但折腾了两个多小时之后,我的第一个 MCP Server 居然跑起来了!那一刻的成就感,怎么说呢,比写出第一个 Hello World 还爽。

后来我把这个经验整理成教程发到 GitHub 上,收到了不少 star 和 issue。很多人说"终于有人把 MCP Server 开发讲明白了",我才发现原来不止我一个人在踩坑。所以今天这篇文章,就是想把 MCP Server 开发的完整流程从头到尾讲清楚,让你也能在 2-4 小时内从零构建并发布自己的第一个 AI 工具服务器。

MCP 协议核心概念:先搞懂再动手

在写代码之前,你得先搞清楚 MCP 到底是什么。别急着跳到代码部分,这一步很关键。

MCP(Model Context Protocol)是一个开放协议,它定义了 AI 应用(比如 Claude Desktop)如何与外部工具和数据源通信。简单来说,它让 AI 不再是"只会聊天"的文字生成器,而是能真正调用工具、操作数据的智能助手。

MCP 的架构有三个核心角色:

  • Host(宿主):就是运行 AI 的应用程序,比如 Claude Desktop、Cursor、Windsurf。Host 负责管理多个 Client。
  • Client(客户端):Host 内部的组件,每个 Client 与一个 Server 建立一对一连接。你可以理解为一个"翻译官",把 AI 的请求翻译成 Server 能懂的格式。
  • Server(服务器):提供具体能力的程序,比如文件系统访问、数据库查询、API 调用等。这就是我们要开发的东西。

通信方式有两种:stdio(标准输入输出)和 SSE(Server-Sent Events)。本地运行用 stdio,远程部署用 SSE。对于初学者来说,先用 stdio 就够了,等需要远程访问再考虑 SSE。

Server 能提供三种能力:

  • Tools(工具):AI 可以调用的函数,比如"查天气""搜索代码""创建文件"。这是最常用的能力,也是我们今天重点要实现的。
  • Resources(资源):AI 可以读取的数据,比如文件内容、数据库记录。类似于 REST API 的 GET。
  • Prompts(提示模板):预定义的提示词模板,让 AI 按特定格式输出内容。

搞懂这些概念之后,你就能理解后面代码里每个部分在干什么了。别小看这些概念,我见过不少人直接跳到代码,结果连 Tool 和 Resource 都分不清,写出来的 Server 自然也是一团糟。

开发环境准备

好,概念搞清楚了,开始搭环境。你需要准备这些东西:

  • Node.js 18+:MCP SDK 依赖较新的 Node 特性,建议用 LTS 版本
  • TypeScript:官方 SDK 是 TypeScript 写的,类型提示能帮你少踩一半坑
  • @modelcontextprotocol/sdk:这是官方的 TypeScript MCP SDK,封装了协议细节,让你专注写业务逻辑

创建项目:

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

这里我额外装了 zod,它是做参数校验的库,MCP SDK 内部用它来定义 Tool 的参数 schema。不装也行,但你会失去类型安全和自动校验,出了 bug 很难排查。

tsconfig.json 建议这样配置:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

项目结构我习惯这样组织:

weather-mcp-server/
├── src/
│   └── index.ts      # 主入口
├── build/            # 编译输出
├── package.json
└── tsconfig.json

简单项目一个文件就够了,别过度设计。等你真正需要拆模块的时候再拆,别上来就搞什么分层架构。

实战:开发一个天气查询 MCP Server

环境搭好了,开始写代码!我们要开发一个天气查询 MCP Server,让 Claude 能根据城市名查询实时天气。用的是 Open-Meteo 的免费 API,不需要申请 Key,直接就能调。

完整代码如下(src/index.ts):

#!/usr/bin/env node

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

// 1. 创建 MCP Server 实例
const server = new McpServer({
  name: "weather-server",
  version: "1.0.0",
});

// 2. 城市名转经纬度的辅助函数
async function getCoordinates(city: string): Promise<{ latitude: number; longitude: number }> {
  const response = await fetch(
    `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}&count=1`
  );
  const data = await response.json() as any;
  if (!data.results || data.results.length === 0) {
    throw new Error(`找不到城市: ${city}`);
  }
  return {
    latitude: data.results[0].latitude,
    longitude: data.results[0].longitude,
  };
}

// 3. 注册"查天气"工具
server.tool(
  "get_weather",
  "根据城市名查询当前天气信息,包括温度、风速、天气状况",
  {
    city: z.string().describe("城市名称,支持中文和英文,如'北京'或'Beijing'"),
  },
  async ({ city }) => {
    try {
      const { latitude, longitude } = await getCoordinates(city);
      const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true&timezone=auto`;
      const response = await fetch(weatherUrl);
      const data = await response.json() as any;
      const weather = data.current_weather;

      const weatherCodes: Record = {
        0: "晴朗", 1: "大部晴朗", 2: "多云", 3: "阴天",
        45: "雾", 48: "雾凇", 51: "小毛毛雨", 53: "毛毛雨",
        55: "大毛毛雨", 61: "小雨", 63: "中雨", 65: "大雨",
        71: "小雪", 73: "中雪", 75: "大雪", 80: "阵雨",
        95: "雷暴",
      };

      return {
        content: [
          {
            type: "text",
            text: `📍 ${city} 当前天气\n` +
              `🌡️ 温度: ${weather.temperature}°C\n` +
              `💨 风速: ${weather.windspeed} km/h\n` +
              `🌤️ 天气: ${weatherCodes[weather.weathercode] || "未知"}\n` +
              `🕐 更新时间: ${weather.time}`,
          },
        ],
      };
    } catch (error: any) {
      return {
        content: [{ type: "text", text: `查询失败: ${error.message}` }],
        isError: true,
      };
    }
  }
);

// 4. 启动 Server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Weather MCP Server 已启动");
}

main().catch(console.error);

来,我逐段解释一下:

第 1 步:创建 McpServer 实例,传入 name 和 version。这个 name 会显示在 Claude Desktop 的工具列表里,起个有意义的名字。

第 2 步:辅助函数 getCoordinates,把城市名转成经纬度。Open-Meteo 的天气 API 需要经纬度参数,所以得先做一步地理编码。这里用了 Open-Meteo 自带的 geocoding API,免费且不需要 Key。

第 3 步:核心部分——注册 Tool。server.tool() 接受四个参数:工具名、描述、参数 schema(用 zod 定义)、处理函数。注意描述很重要,AI 会根据描述决定什么时候调用这个工具,写不清楚 AI 就不会用。参数校验 zod 帮你做了,传入的 city 一定是 string 类型,不用自己写类型检查。

第 4 步:启动 Server,连接 stdio 传输层。注意我用的是 console.error 输出日志,不是 console.log。因为 stdio 模式下,stdout 是给 MCP 协议通信用的,你往里面写日志会直接导致协议解析失败。这个坑我踩过,调试了半小时才发现。

编译并测试:

npx tsc
node build/index.js

如果看到"Weather MCP Server 已启动",说明编译和运行都没问题。但这时候它还在等 stdio 输入,你直接 Ctrl+C 退出就行,等配置到 Claude Desktop 之后再正式测试。

配置到 Claude Desktop

Server 写好了,怎么让 Claude Desktop 用上它?很简单,改一个配置文件。

找到 Claude Desktop 的配置文件:

  • macOS:~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows:%APPDATA%\Claude\claude_desktop_config.json

在配置文件里加上你的 Server:

{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["/你的完整路径/weather-mcp-server/build/index.js"]
    }
  }
}

几个注意事项:

  • args 里的路径必须是绝对路径,相对路径不靠谱
  • 改完配置文件后需要重启 Claude Desktop才能生效
  • 如果你的 Server 需要环境变量(比如 API Key),在配置里加 env 字段:
    "env": { "API_KEY": "xxx" }

重启 Claude Desktop 后,随便开一个对话,输入"帮我查一下北京的天气"。如果一切正常,Claude 会调用你的 weather 工具,然后返回天气信息。第一次看到 AI 调用你自己写的工具,那种感觉真的挺奇妙的。

调试技巧:别靠猜,用工具

说实话,MCP Server 的调试体验目前还不太友好。没有断点调试,没有可视化的调用日志,出了问题只能靠猜。但有几个工具和方法能帮你大幅提升效率。

1. MCP Inspector

这是官方提供的调试工具,能让你在浏览器里直接测试 MCP Server,不用每次都开 Claude Desktop。

npx @modelcontextprotocol/inspector node build/index.js

运行后会启动一个本地 Web 服务,打开浏览器就能看到交互界面。你可以手动调用 Tool、查看请求和响应、检查参数校验结果。调试的时候强烈建议用这个,比在 Claude Desktop 里盲测效率高 10 倍。

2. 日志输出

前面说了,stdio 模式下不能用 console.log,但可以用 console.error。我习惯在关键位置加日志:

console.error("[DEBUG] 收到查询请求:", city);
console.error("[DEBUG] 地理编码结果:", coordinates);
console.error("[DEBUG] 天气数据:", weatherData);

这些日志会输出到 Claude Desktop 的日志文件里。macOS 的日志路径:

~/Library/Logs/Claude/mcp-server-weather.log

Windows 的日志路径:

%APPDATA%\Claude\logs\mcp-server-weather.log

3. 常见问题排查

  • Server 启动失败:检查 Node 版本、依赖是否安装完整、路径是否正确
  • Tool 不显示:检查 tool 描述是否为空、参数 schema 是否合法
  • 调用报错:看日志,90% 的情况是网络请求失败或参数格式不对
  • Server 超时:MCP 默认超时 30 秒,如果你的 API 调用比较慢,考虑加缓存或异步处理

发布流程:让更多人用上你的 Server

Server 写好了、测试通过了,下一步就是发布出去。我建议按这个顺序来:

第一步:发布到 npm

在 package.json 里加上 bin 字段,让用户可以直接 npx 运行:

{
  "name": "mcp-server-weather",
  "version": "1.0.0",
  "bin": {
    "mcp-server-weather": "./build/index.js"
  }
}

然后在 build/index.js 的第一行加上 shebang:

#!/usr/bin/env node

发布:

npm publish

第二步:创建 GitHub 仓库

写好 README,至少包含:功能介绍、安装方式、Claude Desktop 配置示例、开发指南。别偷懒,README 写得好不好直接影响别人愿不愿意用。我之前有个 Server 代码写得一般,但 README 写得很详细,结果 star 数比代码质量更好的另一个项目还多。

第三步:提交到 MCP Server 列表

目前有几个主要的 MCP Server 目录:

  • 官方 awesome-mcp-servers:GitHub 上最全的 MCP Server 列表,提 PR 就能收录
  • Free API Hub524900.xyz 上整理了 158+ 个 MCP 服务器,涵盖开发与代码、数据库、文件系统、AI 与机器学习等 15 个分类,每个都有安装配置指南。提交收录也很简单,在网站上提交就行。

整个发布流程走下来大概 30 分钟到 1 小时。加上前面写代码的 1-2 小时,从零到发布一个 MCP Server,总共也就 2-4 小时。比写一个完整的 Web 应用快多了。

进阶方向:从"能用"到"好用"

天气查询 Server 只是个入门例子。真正在生产环境用的 MCP Server,还需要考虑更多东西:

  • 错误处理和重试:网络请求不可能 100% 成功,加上重试机制和友好的错误提示
  • 缓存:天气数据 10 分钟更新一次就够了,没必要每次都调 API。加个内存缓存,响应速度提升 10 倍
  • SSE 传输:如果想让你的 Server 支持远程访问,把 StdioServerTransport 换成 SSEServerTransport
  • 多 Tool 支持:一个 Server 可以注册多个 Tool,比如天气 Server 还可以加"查未来 7 天预报""查空气质量"等
  • Resources 和 Prompts:除了 Tool,你还可以提供 Resources(让 AI 读取数据)和 Prompts(预定义提示模板),丰富 Server 的能力

说到这里,不得不提一下目前 MCP 生态的规模。从 2024 年 11 月协议发布到现在,GitHub 上 MCP 相关仓库已经从 0 增长到 5000+,其中不乏高质量的项目。比如 GitHub 官方的 MCP Server 已经有 889k stars,Context7 提供了实时文档查询能力,Filesystem Server 是本地文件操作的事实标准。这些项目都是很好的学习参考,建议你去读读源码。

如果你正在找更多 MCP Server 的灵感或现成方案,Free API Hub 上的 MCP 服务器列表是个不错的起点。158+ 个 MCP 服务器按 15 个分类整理好了,每个都有安装难度标注和配置指南,省得你自己一个个去 GitHub 搜。

总结

回顾一下,开发一个 MCP Server 的完整流程就是:

  • 搞懂 MCP 协议的核心概念(Host、Client、Server、Transport)
  • 搭好 Node.js + TypeScript + MCP SDK 的开发环境
  • 用 McpServer 注册 Tool,实现业务逻辑
  • 配置到 Claude Desktop 测试
  • 用 MCP Inspector 调试
  • 发布到 npm 和 GitHub,提交到 MCP Server 列表

整个过程 2-4 小时,比你想的简单得多。MCP 协议的设计初衷就是降低 AI 工具开发的门槛——你不需要理解 LLM 的内部原理,只需要写一个"接收参数、返回结果"的普通函数,MCP SDK 帮你搞定剩下的协议通信。

我觉得 MCP 协议入门最大的门槛不是技术,而是"不知道从哪开始"。希望这篇文章能帮你跨过这个门槛。等你写完第一个 MCP Server,你会发现——原来让 AI 用上自定义工具,就这么点事。

最后,如果你在开发 MCP Server 的过程中遇到问题,或者想找更多 MCP 服务器做参考,欢迎来 Free API Hub 看看。我们持续在更新 MCP 相关的内容,从服务器选型到开发教程都有,希望能帮到更多刚入门的开发者。

常见问题

Q:MCP Server 开发入门:2 小时从零构建你的第一个 AI 工具服务器的核心观点是什么?

本文深入探讨了MCP、AI开发、MCP Server等相关内容,为开发者提供了实用的MCP指导和建议。

Q:如何应用本文介绍的技术?

文章提供了详细的步骤说明和代码示例,你可以按照文中的指导逐步实践。同时建议结合自己的项目需求进行适当调整。

Q:Free API Hub还提供哪些相关资源?

Free API Hub收录了500+个免费API接口,你可以在API列表中找到各种实用的接口。同时我们的技术博客会持续更新更多开发教程和最佳实践。

相关关键词

MCPAI开发MCP ServerClaudeAI工具TypeScript协议开发MCP Server 开发入门:2 小时从零构建你的第一个 AI 工具服务器教程MCP Server 开发入门:2 小时从零构建你的第一个 AI 工具服务器指南API教程API开发免费APIAPI接口开发者教程编程教程技术博客API最佳实践API性能优化API安全API集成