从零搭建一个 MCP Server 接入 Claude 和 Cursor
MCP(Model Context Protocol)是 Anthropic 提出的一个开放协议,简单说就是给 AI 模型一个标准化的方式去调用外部工具、读取数据源。你可以把它理解成「AI 的 USB 接口」——只要按协议实现一个 Server,Claude、Cursor 这些客户端就能即插即用地调用你的能力。
这篇我们从零写一个最小可用的 MCP Server,让 Claude 能调用我们自己定义的工具。
先搞清楚 MCP 在解决什么问题
没有 MCP 之前,想让 AI 调用外部能力,每个工具都得自己写适配。你给 Cursor 写的插件,搬到 Claude 上又得重来一遍。
MCP 把这件事标准化了。它定义了三类核心能力:
- Tools(工具):模型可以主动调用的函数,比如「查天气」「读数据库」。
- Resources(资源):模型可以读取的数据,比如文件、API 返回。
- Prompts(提示模板):预置的提示词模板。
只要你的 Server 遵循协议,任何支持 MCP 的客户端都能用。这篇重点讲最常用的 Tools。
一、初始化项目
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx@modelcontextprotocol/sdk 是官方 SDK,zod 用来做参数校验(SDK 推荐搭配它定义工具的入参 schema)。
配置 tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}package.json 里加上 "type": "module",因为 SDK 是 ESM 的。
二、写一个最小 Server
新建 src/index.ts。我们先实现一个简单的工具:根据城市名返回模拟天气(实际项目里换成真实 API 即可)。
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: "weather-server",
version: "1.0.0",
});
// 2. 注册一个工具
server.tool(
"get_weather",
"查询指定城市的当前天气",
{
city: z.string().describe("城市名称,例如:北京、上海"),
},
async ({ city }) => {
// 这里用模拟数据,真实场景换成天气 API 调用
const weather = {
city,
temperature: "22°C",
condition: "晴",
humidity: "45%",
};
return {
content: [
{
type: "text",
text: JSON.stringify(weather, null, 2),
},
],
};
}
);
// 3. 用 stdio 传输启动(Claude Desktop / Cursor 都走这个)
const transport = new StdioServerTransport();
await server.connect(transport);几个要点:
server.tool()四个参数分别是:工具名、描述、参数 schema、处理函数。- 描述很重要——模型靠它判断什么时候该调这个工具,写清楚能显著提升调用准确率。
- 参数用
zod定义,.describe()的说明也会传给模型,帮它理解每个字段。 - 返回值必须是
{ content: [...] }结构,type: "text"是最常用的。
三、本地调试
MCP 官方提供了一个调试工具 Inspector,不用接客户端就能测:
npx @modelcontextprotocol/inspector npx tsx src/index.ts它会起一个网页界面,你能看到注册了哪些工具、手动填参数调用、看返回结果。开发阶段强烈建议先用它跑通,再去接客户端。
四、接入 Claude Desktop
Claude Desktop 通过一个配置文件加载 MCP Server。配置文件位置:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
编辑它,加上我们的 Server:
{
"mcpServers": {
"weather": {
"command": "npx",
"args": ["tsx", "/绝对路径/my-mcp-server/src/index.ts"]
}
}
}注意路径要写绝对路径。改完重启 Claude Desktop,在对话框里问「北京天气怎么样」,Claude 就会自动调用 get_weather 工具。
五、接入 Cursor
Cursor 的配置类似,在设置里找到 MCP 配置(或编辑项目根目录的 .cursor/mcp.json):
{
"mcpServers": {
"weather": {
"command": "npx",
"args": ["tsx", "/绝对路径/my-mcp-server/src/index.ts"]
}
}
}接好之后,在 Cursor 的对话里同样能触发工具调用。
六、进阶:让工具更可靠
跑通基础版后,实际项目里还要补几件事:
参数校验和错误处理。zod 会帮你拦住非法入参,但工具内部的异常要自己处理好,别让 Server 崩掉:
async ({ city }) => {
try {
const data = await fetchRealWeather(city);
return { content: [{ type: "text", text: JSON.stringify(data) }] };
} catch (err) {
return {
content: [{ type: "text", text: `查询失败:${(err as Error).message}` }],
isError: true,
};
}
}isError: true 会告诉模型这次调用出错了,它能据此决定要不要重试或换方式。
不要把敏感信息硬编码。API key 这类东西走环境变量,别写进代码:
const apiKey = process.env.WEATHER_API_KEY;
if (!apiKey) {
throw new Error("缺少 WEATHER_API_KEY 环境变量");
}启动时就校验必需的环境变量,缺了直接报错,比运行到一半才挂掉好排查。
写在最后
MCP 的核心价值在于「写一次,到处能用」。你为团队封装的内部工具——查订单、读文档、跑脚本——做成一个 MCP Server,全公司用 Claude 或 Cursor 的人都能直接调,不用各自折腾。
这篇只覆盖了 Tools,Resources 和 Prompts 的玩法类似,官方文档和 SDK 里都有例子。先把这个最小 Server 跑通,剩下的扩展就很自然了。