开发一个最简单的 MCP 指南
开发一个最基础的MCP
MCP 采用客户端-服务器架构,MCP 主机为每个 MCP 服务器创建一个 MCP 客户端。每个 MCP 客户端与其 MCP 服务器保持一对一的专用连接。
MCP组成
MCP Host:协调和管理一个或多个 MCP 客户端的 AI 应用程序(cursor,claude,vscode)
MCP 客户端:维护与 MCP 服务器的连接,并从 MCP 服务器获取上下文,以供 MCP 主机使用的组件(vscode 运行时会实例化一个 MCP 客户端对象,用于维护与 MCP 服务器的连接)
MCP 服务器:为 MCP 客户端提供上下文的程序。这里并非常规认知里的Server端接口。
特别说明:MCP 服务器指的是 提供上下文数据的程序,无论其运行在何处。MCP 服务器可以本地也可以远程。
启动文件系统服务器时,由于使用 STDIO 传输,且在同一台机器上运行,被称为“本地” MCP 服务器。
官方 Sentry MCP 服务器运行在 Sentry 平台上,并使用 Streamable HTTP 传输,被称为“远程” MCP 服务器。
一、核心内容
1.1 数据层(JSON-RPC 2.0)
定义基于 JSON-RPC 的客户端-服务器通信协议,包括生命周期管理、核心原语(如工具、资源、提示和通知)
JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议。 本规范主要定义了一些数据结构及其相关的处理规则。它允许运行在基于socket,http等诸多不同消息传输环境的同一进程中。其使用JSON(RFC 4627)作为数据格式。
核心组成
-
生命周期管理:处理客户端和服务器之间的连接初始化、功能协商和连接终止
-
服务器功能:服务器提供核心功能,包括 AI 操作的工具、上下文数据的资源和从客户端到交互模板的提示
-
客户端功能:使服务器能够要求客户端从主机 LLM 进行采样,从用户那里获取输入,并将消息记录到客户端
-
实用功能:支持附加功能,例如实时更新通知和长期运行操作的进度跟踪
Primitives(原语)
MCP 原语是 MCP 中最重要的概念。它们定义了客户端和服务器可以相互提供哪些功能。这些原语指定了可以与 AI 应用程序共享的上下文信息类型以及可以执行的操作范围。
服务器公开的三个核心原语
-
工具:AI 应用程序可以调用以执行操作的可执行函数(文件操作、API 调用、数据库查询)
-
资源:向 AI 应用程序提供上下文信息的数据源(文件内容、数据库记录、API 响应)
-
提示:可重复使用的模板,有助于构建与语言模型的交互(系统提示、少量示例)
客户端公开的原语
-
采样:允许服务器向客户端的 AI 应用程序请求语言模型补全。
-
启发式:允许服务器向用户请求更多信息。当服务器开发者想要从用户那里获取更多信息,或者请求用户确认某个操作时,此功能非常有用。
-
日志记录:使服务器能够向客户端发送日志消息以进行调试和监控。
1.2 传输层
-
Stdio 传输:使用标准输入/输出流在同一台机器上的本地进程之间进行直接进程通信,提供最佳性能且无网络开销。
-
可流式传输的 HTTP 传输:使用 HTTP POST 协议发送客户端到服务器的消息,并可选用服务器发送事件来实现流式传输功能。
二、核心开发要点
2.1 核心步骤
- 安装SDK:
npm install @modelcontextprotocol/sdk - 创建服务器: 使用
Server类 - 定义工具: 在
tools数组中定义工具 - 实现逻辑: 在
toolImplementations中实现 - 注册处理器: 使用
setRequestHandler - 启动服务: 连接传输层并启动
2.2 常见错误
- 切勿写入标准输出 (stdout),最佳实践是使用写入 stderr,即不使用
console.log/print输出日志(干扰JSON-RPC),应该使用console.error - 违反单一职责,工具功能过于复杂
- 缺少参数验证(安全风险)
- 同步处理耗时操作(阻塞服务器)
三、关键实现步骤
3.1 项目初始化
# 创建项目结构
mkdir mcp-server
cd mcp-server
# 初始化package.json
npm init -y
# 安装MCP SDK
npm install @modelcontextprotocol/sdk
3.2 核心代码结构
3.2.1 服务器主结构 (src/server.js)
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// 1. 定义工具列表
const tools = [
{
name: "get_weather", // 工具唯一标识
description: "获取指定城市的天气信息", // 介绍描述
inputSchema: { // 工具参数的JSON格式
type: "object",
// 系统操作,与本地系统交互的工具
// properties: {
// command: { type: "string" },
// args: { type: "array", items: { type: "string" } }
// },
// 转换或分析数据的工具
// properties: {
// filepath: { type: "string" },
// operations: {
// type: "array",
// items: {
// enum: ["sum", "average", "count"]
// }
// }
// }
// API 集成,包装外部 API 的工具
properties: { // 工具指定参数
city: {
type: "string",
description: "城市名称",
},
},
required: ["city"],
},
// annotations?: { // 工具注释
// title?: string; // 工具注释人类可读标题
// readOnlyHint?: boolean; // 如果为 true,则该工具不会修改其环境
// destructiveHint?: boolean; // 如果为 true,则该工具可能会执行破坏性更新
// idempotentHint?: boolean; // 如果为 true,则使用相同参数重复调用不会产生额外效果
// openWorldHint?: boolean; // 如果为 true,则工具会与外部实体交互
// }
},
];
// 2. 实现工具函数
const toolImplementations = {
get_weather: async (args) => {
const { city } = args;
// 业务逻辑实现
return {
content: [
{
type: "text",
text: `${city}的天气信息...`,
},
],
};
},
};
// 3. 创建MCP服务器实例
const server = new Server(
{
name: "mcp-server-example",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 4. 注册请求处理器
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools };
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (!toolImplementations[name]) {
throw new Error(`工具 '${name}' 不存在`);
}
try {
const result = await toolImplementations[name](args);
return result;
} catch (error) {
throw new Error(`执行工具 '${name}' 时出错: ${error.message}`);
}
});
// 5. 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP服务器已启动");
}
main().catch((error) => {
console.error("服务器启动失败:", error);
process.exit(1);
});
3.2.2 mcp-inspector开发调试
-
上面主题搭建完毕,如果上面没有问题,可以正常启动,那么我们可以进入调试
-
调试阶段使用 mcp-inspector 工具,可以看下面截图
// package.json
"scripts": {
"inspector": "mcp-inspector --config mcp-config.json --server mcp-server-example"
}
// --config mcp-config.json 读取根目录 mcp-config.json
{
"mcpServers": {
"mcp-server-example": {
"command": "node",
"args": ["src/server.js"],
"env": {
"NODE_ENV": "development"
}
}
}
}
// --server mcp-server-example 启动的就是通过 mcp-config.json 的 mcp-server-example 找到命令执行
- 从上面初始化的内容,我们可以看到提供了2个工具,可以在inspector的web界面的tool查看到
- 启动时提供了token内容,需要在inspector的web界面输入才能启动调试

- 这里就能看到工具列表
- 在左侧的配置里输入上面启动时token,点击链接才能正常链接

到这里基本上就完成初步服务开发结构和调试方式,此时就真正的跑起来了MCP,如果你只是完成天气这种简单工具MCP,那稍作维护和处理,即可发布第一个MCP。
四、开发最佳实践
4.1 工具设计原则
// 好的工具设计
const goodTool = {
name: "calculate_math",
description: "执行数学计算",
inputSchema: {
type: "object",
properties: {
expression: {
type: "string",
description: "数学表达式,如 '2 + 3 * 4'"
}
},
required: ["expression"]
}
};
// 坏的的设计
const badTool = {
name: "do_everything", // 功能过于复杂
description: "执行各种操作", // 描述不清晰
inputSchema: {
type: "object",
properties: {
data: {
type: "string",
description: "数据" // 描述过于简单
}
}
}
};
4.2 错误处理
const safeToolImplementation = async (args) => {
try {
// 1. 参数验证
if (!args.city) {
throw new Error("城市参数是必需的");
}
// 2. 业务逻辑
const result = await fetchWeatherData(args.city);
// 3. 返回标准格式
return {
content: [
{
type: "text",
text: result
}
]
};
} catch (error) {
// 4. 错误处理
console.error("工具执行错误:", error);
throw new Error(`天气查询失败: ${error.message}`);
}
};
4.3 日志处理
// 正确的日志处理(STDIO传输)
import logging from 'logging';
// 使用stderr输出日志
console.error("服务器启动成功");
// 错误的日志处理
console.log("服务器启动成功"); // 会干扰JSON-RPC消息
五、扩展功能
5.1 添加新工具
// 步骤1: 定义工具
const newTool = {
name: "translate_text",
description: "翻译文本",
inputSchema: {
type: "object",
properties: {
text: {
type: "string",
description: "要翻译的文本"
},
target_language: {
type: "string",
description: "目标语言代码"
}
},
required: ["text", "target_language"]
}
};
// 步骤2: 实现工具逻辑
const translateTool = async (args) => {
const { text, target_language } = args;
// 调用翻译API
const translated = await callTranslationAPI(text, target_language);
return {
content: [
{
type: "text",
text: `翻译结果: ${translated}`
}
]
};
};
// 步骤3: 注册工具
tools.push(newTool);
toolImplementations.translate_text = translateTool;
5.2 自定义传输层
// WebSocket传输示例
import { WebSocketServerTransport } from "@modelcontextprotocol/sdk/server/websocket.js";
const transport = new WebSocketServerTransport({
port: 8080,
host: "localhost"
});
// HTTP传输示例
import { HttpServerTransport } from "@modelcontextprotocol/sdk/server/http.js";
const transport = new HttpServerTransport({
port: 3000
});
5.3 资源管理
// 添加资源支持
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "file:///path/to/file.txt",
name: "示例文件",
description: "这是一个示例文件",
mimeType: "text/plain"
}
]
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
// 读取资源内容
const content = await readFile(uri);
return {
contents: [
{
uri,
mimeType: "text/plain",
text: content
}
]
};
});
六、部署配置
6.1 客户端配置
{
"mcpServers": {
"weather-server": {
"command": "node",
"args": ["/path/to/src/server.js"],
"env": {
"NODE_ENV": "production"
}
}
}
}
附录参考资料
https://wiki.geekdream.com/Specification/json-rpc_2.0.html
更多推荐


所有评论(0)