TL;DR: 使用 Node.js + 官方 TypeScript SDK,你可以在 5 分钟内构建一个功能完整的 MCP Server,并在 Claude Desktop 中直接调用它。本文提供从项目初始化到工具注册、本地调试、客户端对接的完整流程,附带每一步的可运行代码。
核心要点
- 5 分钟上手:从
npm init到 Claude Desktop 成功调用,整个流程不超过 5 分钟 - 零协议知识要求:官方 SDK 封装了 JSON-RPC 2.0 通信细节,你只需关注业务逻辑
- stdio 传输:本教程使用最简单的 stdio 模式,无需网络配置,Claude Desktop 原生支持
- 完整可运行:每段代码都经过验证,复制即用,不是伪代码
- 调试友好:包含 MCP Inspector 工具的使用方法和常见报错排查方案
如果你还不了解 MCP 协议的整体架构,建议先阅读 MCP 协议深度解析。
前置准备
开始之前,确保你的开发环境满足以下要求:
| 依赖 | 最低版本 | 检查命令 |
|---|---|---|
| Node.js | 18.0+ | node --version |
| npm | 9.0+ | npm --version |
| TypeScript | 5.0+ | 全局安装或项目内安装均可 |
本教程的所有代码基于 @modelcontextprotocol/sdk 官方 TypeScript SDK 最新版本。确认 Node.js 版本后,我们直接开始。
第一步:初始化项目
创建一个新的 Node.js 项目并安装 MCP SDK:
mkdir my-first-mcp-server
cd my-first-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
这里安装了三个关键依赖:
@modelcontextprotocol/sdk:MCP 官方 SDK,处理所有协议层通信zod:运行时 Schema 验证库,用于定义 Tool 的输入参数类型typescript:编译器,SDK 的类型推断需要它
初始化 TypeScript 配置:
npx tsc --init
修改生成的 tsconfig.json,确保以下关键配置:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
同时更新 package.json,添加 type 字段和构建脚本:
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
项目骨架搭建完成。整个过程不到 1 分钟。
第二步:编写 MCP Server 核心代码
在 src/index.ts 中编写完整的 Server 代码。我们从最小可用版本开始——注册一个 Tool,让 AI Agent 能够调用它。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 1. 创建 Server 实例
const server = new McpServer({
name: "my-first-mcp-server",
version: "1.0.0",
});
// 2. 注册一个 Tool
server.tool(
"get-weather",
"获取指定城市的当前天气信息",
{
city: z.string().describe("城市名称,例如 Beijing、Tokyo、New York"),
},
async ({ city }) => {
// 这里用模拟数据演示,实际项目中替换为真实 API 调用
const weatherData: Record<string, { temp: number; condition: string }> = {
Beijing: { temp: 22, condition: "晴" },
Tokyo: { temp: 18, condition: "多云" },
"New York": { temp: 15, condition: "小雨" },
};
const weather = weatherData[city];
if (!weather) {
return {
content: [
{
type: "text" as const,
text: `未找到城市 "${city}" 的天气数据。支持的城市:${Object.keys(weatherData).join("、")}`,
},
],
};
}
return {
content: [
{
type: "text" as const,
text: `${city} 当前天气:${weather.condition},温度 ${weather.temp}°C`,
},
],
};
}
);
// 3. 启动 Server
const transport = new StdioServerTransport();
await server.connect(transport);
这段代码做了三件事:
- 创建 McpServer 实例:指定 Server 名称和版本号,这些信息会在客户端的
initialize握手中使用 - 注册 Tool:通过
server.tool()声明一个名为get-weather的工具。第二个参数是自然语言描述——LLM 会根据这段描述决定何时调用该工具。第三个参数用zod定义参数的 JSON Schema,SDK 会自动将其转换为 MCP 协议要求的inputSchema格式 - 连接传输层:使用
StdioServerTransport通过标准输入/输出与客户端通信
关于 Tool 描述的编写技巧
Tool 的 description 字段直接影响 LLM 的 Tool Use 决策。好的描述应该:
- 明确说明工具的功能("获取天气"而不是"天气工具")
- 包含关键约束("需要传入城市名称")
- 避免歧义(如果有多个类似工具,描述中应体现差异)
这与 Function Calling 中函数描述的编写原则一致——Prompt Engineering 的基本功在这里同样适用。
第三步:编译并本地测试
编译 TypeScript 代码:
npx tsc
编译成功后,dist/index.js 就是可运行的 Server。先在终端手动验证它能正常启动:
node dist/index.js
如果没有任何输出且进程没有退出,说明 Server 已在等待 stdin 输入——这是 stdio 模式的正常行为。按 Ctrl+C 退出。
使用 MCP Inspector 调试
手动构造 JSON-RPC 消息来测试 Server 既繁琐又容易出错。MCP 官方提供了一个可视化调试工具——MCP Inspector:
npx @modelcontextprotocol/inspector node dist/index.js
Inspector 启动后会在浏览器中打开一个调试界面,你可以:
- 查看 Server 注册的所有 Tools、Resources 和 Prompts
- 手动输入参数调用 Tool 并查看返回结果
- 查看完整的 JSON-RPC 消息收发日志
在 Inspector 的 Tools 面板中,你应该能看到 get-weather 工具。填入 city: "Beijing" 点击调用,预期返回:
{
"content": [
{
"type": "text",
"text": "Beijing 当前天气:晴,温度 22°C"
}
]
}
调试确认无误后,进入下一步。
第四步:对接 Claude Desktop
Claude Desktop 是目前最成熟的 MCP 客户端之一。配置方法如下:
1. 找到配置文件
macOS 的配置文件路径:
~/Library/Application Support/Claude/claude_desktop_config.json
Windows 的配置文件路径:
%APPDATA%\Claude\claude_desktop_config.json
如果文件不存在,手动创建即可。
2. 添加 Server 配置
在配置文件中注册你的 MCP Server:
{
"mcpServers": {
"my-first-mcp-server": {
"command": "node",
"args": ["/absolute/path/to/my-first-mcp-server/dist/index.js"]
}
}
}
注意两个关键点:
command和args中的路径必须是绝对路径。相对路径会导致 Claude Desktop 无法找到可执行文件- 如果你使用
nvm管理 Node.js 版本,command应指向nvm环境下的node绝对路径(通过which node查看)
3. 重启 Claude Desktop
完全退出 Claude Desktop 后重新打开(macOS 上需要从 Dock 栏右键退出,仅关闭窗口不会重新加载配置)。
重启后,在 Claude 的对话输入框旁边应该能看到一个工具图标(锤子形状),点击展开可以看到 get-weather 工具已注册。
4. 测试对话
在 Claude Desktop 中输入:
北京今天天气怎么样?
Claude 会识别出这个问题可以通过 get-weather 工具回答,自动调用你的 MCP Server,并将结果整合到回复中。整个 Tool Use 流程对用户完全透明。
扩展:添加更多 Tools
一个 MCP Server 可以注册任意数量的 Tool。我们继续添加两个实用工具来演示更复杂的场景。
带有多参数的 Tool
server.tool(
"calculate-bmi",
"根据身高和体重计算 BMI 指数",
{
height: z.number().describe("身高,单位厘米"),
weight: z.number().describe("体重,单位千克"),
},
async ({ height, weight }) => {
const heightInMeters = height / 100;
const bmi = weight / (heightInMeters * heightInMeters);
const bmiFixed = bmi.toFixed(1);
let category: string;
if (bmi < 18.5) category = "偏瘦";
else if (bmi < 24) category = "正常";
else if (bmi < 28) category = "偏胖";
else category = "肥胖";
return {
content: [
{
type: "text" as const,
text: `BMI: ${bmiFixed}(${category})\n身高: ${height}cm / 体重: ${weight}kg`,
},
],
};
}
);
返回结构化数据的 Tool
server.tool(
"generate-uuid",
"生成一个或多个 UUID v4",
{
count: z.number().min(1).max(50).default(1).describe("生成数量,1-50"),
},
async ({ count }) => {
const uuids = Array.from({ length: count }, () => crypto.randomUUID());
return {
content: [
{
type: "text" as const,
text: uuids.join("\n"),
},
],
};
}
);
注意 count 参数使用了 z.number().min(1).max(50).default(1)——zod 的链式校验会自动转换为 JSON Schema 中的 minimum、maximum 和 default 约束。SDK 在收到客户端请求时会自动执行输入验证,不合法的参数会返回标准的 JSON-RPC 错误响应。
添加 Resources 和 Prompts
除了 Tools,MCP 协议还定义了另外两个核心原语。
Resource:暴露数据给 AI
Resource 让 AI Agent 能够读取你的 Server 中的数据,类似于一个只读的数据接口:
server.resource(
"server-info",
"info://server",
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
name: "my-first-mcp-server",
version: "1.0.0",
tools: ["get-weather", "calculate-bmi", "generate-uuid"],
uptime: process.uptime(),
}),
},
],
})
);
Prompt:预置对话模板
Prompt 是可复用的对话模板,客户端可以让用户选择并填充参数:
server.prompt(
"weather-report",
{ city: z.string() },
({ city }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `请查询 ${city} 的天气,并用简洁的方式报告当前温度和天气状况。`,
},
},
],
})
);
Tools、Resources、Prompts 这三个原语的协作方式在 MCP 协议进阶实战中有更深入的讲解。
完整项目结构
完成以上步骤后,你的项目结构应如下所示:
my-first-mcp-server/
├── src/
│ └── index.ts # Server 主文件
├── dist/
│ └── index.js # 编译产物
├── package.json
├── tsconfig.json
└── node_modules/
这是一个最小化的结构。随着 Server 复杂度增长,你可以将不同的 Tool 拆分到独立模块中:
src/
├── index.ts # Server 入口,注册所有 Tool
├── tools/
│ ├── weather.ts # 天气相关工具
│ ├── calculator.ts # 计算类工具
│ └── uuid.ts # UUID 生成
└── utils/
└── validators.ts # 通用校验逻辑
调试与排错
常见报错及解决方案
1. Error: Cannot find module '@modelcontextprotocol/sdk/server/mcp.js'
原因:TypeScript 的 moduleResolution 配置不正确。确保 tsconfig.json 中设置为 "Node16" 或 "NodeNext",且 package.json 中有 "type": "module"。
2. Claude Desktop 中看不到工具
排查步骤:
- 检查
claude_desktop_config.json的 JSON 格式是否合法(多余逗号是常见错误) - 确认
args中的路径是绝对路径 - 在终端手动运行
node /absolute/path/to/dist/index.js,确认没有报错 - 完全退出并重启 Claude Desktop
3. Tool 调用返回空结果
检查 Tool handler 函数是否正确返回了 content 数组。MCP 协议要求返回格式必须包含 content 字段,每个 content 项必须有 type 和 text(或其他支持的 MIME 类型)。
查看日志
Claude Desktop 的 MCP 相关日志路径:
# macOS
tail -f ~/Library/Logs/Claude/mcp*.log
# Windows
# %APPDATA%\Claude\Logs\mcp*.log
日志中可以看到完整的 JSON-RPC 消息交互过程,是排查问题的第一手资料。
生产环境注意事项
本教程构建的是一个学习用的最小 Server。投入生产前,你需要关注以下几点:
错误边界:为每个 Tool handler 添加 try-catch,防止单个工具的异常导致整个 Server 进程崩溃。
输入校验:虽然 zod 提供了基本的类型校验,但对于涉及文件系统、网络请求或数据库操作的 Tool,需要额外的安全校验——防止路径遍历、注入攻击等。
超时控制:长时间运行的 Tool 应设置超时。LLM 客户端通常有请求超时限制,你的 Tool 如果执行时间过长会导致请求失败。
传输层选型:如果你的 Server 需要远程访问或供多个客户端同时使用,应从 stdio 切换到 SSE 传输。关于 SSE 传输层的实现细节,参见 用 Go 从零实现 MCP 协议 SSE 传输层。关于高并发场景下的网关设计,参见 MCP 网关高并发架构设计。
性能考量:当 Server 的 Tool 数量增多或调用频率变高时,Node.js 的单线程模型可能成为瓶颈。此时可以考虑 Go 实现——详见 MCP Server 性能对比:Node.js vs Go。
下一步学习路线
完成本教程后,你已经掌握了 MCP Server 开发的基础。以下是建议的进阶路线:
- 深入协议细节:阅读 MCP 协议进阶实战,了解 Sampling、Roots、协议协商等高级特性
- 了解最新规范:2025 MCP 规范更新引入了 OAuth 认证和远程 Server 支持,这对生产部署至关重要
- 性能优化:通过 Node.js vs Go 性能对比了解不同语言实现的性能特征,为你的场景选择最合适的技术栈
- 架构设计:当你需要管理多个 MCP Server 时,MCP 网关高并发架构设计提供了经过验证的网关方案
总结
本文从零开始,用 Node.js 和官方 TypeScript SDK 构建了一个包含天气查询、BMI 计算、UUID 生成三个 Tool 的 MCP Server,并成功对接了 Claude Desktop。整个过程涵盖了 MCP Server 开发的核心流程:
- 项目初始化:安装 SDK 和 zod,配置 TypeScript
- Tool 注册:使用
server.tool()声明工具名称、描述、参数 Schema 和处理函数 - 本地调试:通过 MCP Inspector 可视化测试
- 客户端对接:配置
claude_desktop_config.json实现 Claude Desktop 集成 - 扩展能力:添加 Resources 和 Prompts 丰富 Server 功能
MCP 协议正在成为 AI Agent 连接外部世界的标准方式。掌握 MCP Server 开发,意味着你编写的任何能力都可以被整个 MCP 生态中的客户端复用——这正是 MCP 最大的价值所在。