【MCP】手把手教你写第一个 MCP
手把手教你写第一个 MCP
一、MCP 是什么
Model Context Protocol(MCP) 是一套开放协议,用于把 AI 助手和外部能力(数据库、API、文件系统等)标准化地连接起来。MCP 服务端可以对外提供:
- Tools(工具):AI 可调用的动作,如计算、查接口、写文件等
- Resources(资源):只读数据,类似 GET 接口
- Prompts(提示模板):可复用的提示模板
客户端(如 Cursor)通过协议与 MCP 服务通信:先 tools/list 拉取工具列表,再通过 tools/call 传入工具名和参数执行调用。
二、示例:两个工具的定义
下文实现的 MCP 将提供两个工具,其参数与 MCP 的 inputSchema 对应关系如下。
2.1 工具一:add(计算两数之和)
描述:计算两数之和。
参数 Schema(与 MCP 的 inputSchema 对应):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| a | number | 是 | 第一个数 |
| b | number | 是 | 第二个数 |
对应 JSON Schema 示例:
{
"name": "add",
"description": "计算两数之和",
"arguments": {
"type": "object",
"properties": {
"a": { "type": "number", "description": "第一个数" },
"b": { "type": "number", "description": "第二个数" }
},
"required": ["a", "b"],
"additionalProperties": false
}
}
2.2 工具二:greet(根据名字返回问候语)
描述:根据名字返回一句问候语。
参数 Schema:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 对方的名字 |
对应 JSON Schema 示例:
{
"name": "greet",
"description": "根据名字返回一句问候语",
"arguments": {
"type": "object",
"properties": {
"name": { "type": "string", "description": "对方的名字" }
},
"required": ["name"],
"additionalProperties": false
}
}
以上 arguments 即 MCP 中工具的 inputSchema,Cursor 会据此生成工具描述并做参数校验。
三、MCP 服务端怎么写(第一个 MCP 的写法)
下面用 Node.js + TypeScript + 官方 MCP SDK 实现一个提供 add 和 greet 的 MCP 服务。
3.1 环境准备
mkdir first-mcp && cd first-mcp
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node tsx
(若使用 Zod 做参数校验,可再安装 zod。)
3.2 用 TypeScript 实现服务端
协议层面,MCP 要求服务端:
- 声明支持 tools 能力
- 在 tools/list 中返回工具列表(含 name、description、inputSchema)
- 在 tools/call 中根据 name 和 arguments 执行逻辑并返回 content(如文本)
以下示例使用 stdio 传输(标准输入/输出),适合 Cursor 通过“命令行启动子进程”的方式连接。
// index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{
name: "demo-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 注册工具列表:add、greet
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "add",
description: "计算两数之和",
inputSchema: {
type: "object",
properties: {
a: { type: "number", description: "第一个数" },
b: { type: "number", description: "第二个数" },
},
required: ["a", "b"],
additionalProperties: false,
},
},
{
name: "greet",
description: "根据名字返回一句问候语",
inputSchema: {
type: "object",
properties: {
name: { type: "string", description: "对方的名字" },
},
required: ["name"],
additionalProperties: false,
},
},
],
}));
// 处理 tools/call:根据 name 执行并返回 content
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "add") {
const a = Number((args as { a?: number }).a);
const b = Number((args as { b?: number }).b);
if (Number.isNaN(a) || Number.isNaN(b)) {
return {
content: [{ type: "text", text: "参数 a、b 须为数字" }],
isError: true,
};
}
const sum = a + b;
return {
content: [{ type: "text", text: `${a} + ${b} = ${sum}` }],
isError: false,
};
}
if (name === "greet") {
const nameArg = (args as { name?: string }).name ?? "";
return {
content: [{ type: "text", text: `你好,${nameArg}!` }],
isError: false,
};
}
return {
content: [{ type: "text", text: `未知工具: ${name}` }],
isError: true,
};
});
// 使用 stdio 传输,便于 Cursor 以子进程方式启动
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("demo-mcp 已就绪(stdio)");
要点小结:
- ListToolsRequestSchema:对应协议的 tools/list,返回的 inputSchema 与 Cursor 里看到的 arguments 一致。
- CallToolRequestSchema:对应 tools/call,根据 name 和 arguments 执行,返回 content(文本)和可选的 isError。
- StdioServerTransport:通过标准输入/输出与 Cursor 通信,无需自己起 HTTP 服务。
3.3 运行方式
在 package.json 里可加:
"scripts": {
"start": "tsx index.ts"
}
Cursor 的 MCP 配置里会通过类似 npx tsx index.ts 或 node dist/index.js 启动该进程,并通过 stdio 与它通信。
3.4 在 Cursor 中配置该 MCP 服务
在 Cursor 的 MCP 配置(如全局或项目级设置)中增加一项,例如:
{
"mcpServers": {
"demo-mcp": {
"command": "node",
"args": ["path/to/demo-mcp/dist/index.js"]
}
}
}
若用 tsx 直接跑 TS:
"demo-mcp": {
"command": "npx",
"args": ["tsx", "path/to/demo-mcp/index.ts"]
}
配置完成后,Cursor 会启动该进程,发送 tools/list 拿到工具列表,并在需要时发送 tools/call。Cursor 会根据 tools/list 的返回生成或缓存工具描述,供 AI 与调用方使用。
四、在 Cursor 里如何调用(调用方式)
在 Cursor 中,AI 助手不是在终端里输入命令,而是通过内置的 MCP 调用工具 与已配置的 MCP 服务通信。
4.1 使用的“命令”实质:call_mcp_tool
调用方式不是 shell 命令,而是 Cursor 提供的工具调用:
- 工具名:call_mcp_tool
- 参数:
- server:MCP 服务标识,即你在配置里填写的服务名(如 “demo-mcp”)
- toolName:工具名,如 “add” 或 “greet”
- arguments:该工具需要的参数对象,必须符合该工具的 inputSchema
4.2 调用示例
调用 add(计算 10 + 20):
- server: “demo-mcp”
- toolName: “add”
- arguments: { “a”: 10, “b”: 20 }
返回示例:“10 + 20 = 30”(以 MCP 的 text content 形式返回)。
调用 greet(问候「你」):
- server: “demo-mcp”
- toolName: “greet”
- arguments: { “name”: “你” }
返回示例:“你好,你!”。
4.3 协议层面的对应关系
Cursor 内部会大致完成:
- 发现工具:向已配置的 MCP 服务发 tools/list,得到 add、greet 及各自的 inputSchema。
- 调用工具:当 AI 决定调用某个工具时,发 tools/call,例如:
- name: “add”
- arguments: { “a”: 10, “b”: 20 }
- 解析结果:把返回的 content[].text 交给 AI 或展示给用户。
也就是说,“调用命令” 在实现上 = Cursor 根据 AI 的决策,对 MCP 服务发 tools/call,而你在对话里看到的是“通过 call_mcp_tool 调用了 add / greet”。
更多推荐


所有评论(0)