我为什么开始写 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.logWindows 的日志路径:
%APPDATA%\Claude\logs\mcp-server-weather.log3. 常见问题排查
- 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 Hub:524900.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 相关的内容,从服务器选型到开发教程都有,希望能帮到更多刚入门的开发者。