MCP 协议详解:从原理到手把手实现一个 MCP Server
一、什么是 MCP
🔔
MCP (Model Context Protocol) 是一个开放的、基于 JSON-RPC 2.0 的协议,它定义了 LLM 应用(客户端)与外部数据源/工具(服务器)之间的标准化通信方式。
你可以把 MCP 理解为 AI 世界的 USB-C 接口:
•
就像 USB-C 让各种设备通过统一接口连接一样,MCP 让各种 AI 应用通过统一协议接入外部能力
•
无论是文件系统、数据库、API 服务,还是浏览器控制,都可以通过 MCP 暴露给 LLM
核心设计原则:


二、为什么需要 MCP
在 MCP 出现之前,每个 AI 应用都需要为每个外部工具编写定制的集成代码。这导致了 M×N 问题:
代码块
Plain Text
没有 MCP:
应用 A ──定制代码──> 工具 1
应用 A ──定制代码──> 工具 2
应用 B ──定制代码──> 工具 1
应用 B ──定制代码──> 工具 2
→ 需要 M × N 个集成
有了 MCP:
应用 A ──MCP──> MCP Server 1 (封装工具 1)
应用 B ──MCP──> MCP Server 1 (封装工具 1)
应用 A ──MCP──> MCP Server 2 (封装工具 2)
→ 只需要 M + N 个实现
三、MCP 核心架构
MCP 采用 客户端-服务器 (Client-Server) 架构:
代码块
Plain Text
┌─────────────────────────────────────────────────┐
│ MCP Host │
│ ┌───────────────┐ ┌───────────────────────┐ │
│ │ MCP Client │───>│ MCP Server A │ │
│ │ (协议层) │ │ (文件系统工具) │ │
│ └───────────────┘ └───────────────────────┘ │
│ ┌───────────────┐ ┌───────────────────────┐ │
│ │ MCP Client │───>│ MCP Server B │ │
│ │ (协议层) │ │ (数据库查询) │ │
│ └───────────────┘ └───────────────────────┘ │
│ ┌───────────────┐ ┌───────────────────────┐ │
│ │ MCP Client │───>│ MCP Server C │ │
│ │ (协议层) │ │ (API 集成) │ │
角色定义:
四、协议通信机制
4.1 消息格式:JSON-RPC 2.0
•
MCP 所有消息都遵循 JSON-RPC 2.0 规范:
请求 (Request):
代码块
JSON
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": { "path": "/tmp/hello.txt" }
}
}
响应 (Response):
代码块
JSON
{
"jsonrpc": "2.0",
"id": 1,
"result": {
通知 (Notification):
代码块
JSON
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": { "progress": 50, "total": 100 }
}
4.2 传输方式
•
MCP 支持两种标准传输方式:
•
stdio(标准输入/输出)
代码块
Plain Text
Client Server
│ │
│──stdin: JSON-RPC Request─────>│
│ │
│<──stdout: JSON-RPC Response────│
│ │
适用于本地进程间通信:
🔔
•
Client 启动 Server 作为子进程
•
通过 stdin/stdout 传递 JSON-RPC 消息
最常用的传输方式:
•
HTTP + SSE(Server-Sent Events)
代码块
Plain Text
Client Server
│──GET /sse────────────────────>│ (建立 SSE 连接)
│<──SSE: endpoint URL───────────│
│ │
│──POST /messages──────────────>│ (发送请求)
│<──SSE: JSON-RPC Response──────│ (通过 SSE 接收)
🔔
•
适用于远程服务、Web 部署
•
支持流式响应
2025 年已更新为 Streamable HTTP 传输方式
五、MCP 生命周期
一个完整的 MCP 会话经历以下阶段:
代码块
SQL
Client Server
│ │
│ 1. 初始化 (Initialize) │
│──{ method: "initialize" }────────>│
│<──{ result: { capabilities } }────│
│ │
│ 2. 确认 (Initialized) │
│──{ method: "initialized" }───────>│
│ │
│ 3. 正常通信 │
│──{ method: "tools/list" }────────>│
│<──{ result: { tools: [...] } }────│
│ │
│──{ method: "tools/call" }────────>│
│<──{ result: { content: [...] } }──│
│ │
初始化握手细节
Client 发送自身信息和能力:
代码块
JSON
{
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {
"roots": { "listChanged": true },
"sampling": {}
},
"clientInfo": {
"name": "MyApp",
"version": "1.0.0"
}
Server 响应自身能力:
代码块
JSON
{
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": { "listChanged": true },
"resources": { "subscribe": true },
"prompts": { "listChanged": true },
"logging": {}
},
"serverInfo": {
"name": "MyServer",
"version": "1.0.0"
}
六、核心能力详解
MCP 定义了三大核心能力(Primitives):
6.1 Tools(工具)
工具是 模型控制 的能力 —— LLM 决定何时调用。
代码块
JSON
{
"name": "get_weather",
"description": "获取指定城市的天气信息",
"inputSchema": {
"type": "object",
"properties": {
6.2 Resources(资源)
资源是 应用控制 的能力 —— 应用代码决定何时读取。
代码块
JSON
{
"uri": "file:///project/README.md",
"name": "项目 README",
"description": "项目的说明文档",
"mimeType": "text/markdown"
}
6.3 Prompts(提示词模板)
提示词是 用户控制 的能力 —— 用户主动选择使用。
代码块
JSON
{
"name": "code_review",
"description": "代码审查提示词",
"arguments": [
{
"name": "language",
"description": "编程语言",
"required": true
},
{
"name": "code",
"description": "待审查的代码",
"required": true
能力对比:
七、手把手实现一个 MCP Server
接下来,我们用 TypeScript + Node.js 当前主流的方式从零实现一个功能完整的 MCP Server。这个 Server 将提供:
•
一个查询天气的 Tool
•
一个读取本地文件的 Resource
•
一个代码审查的 Prompt Template
🔔
为什么选 TypeScript? MCP 协议本身由 Anthropic 用 TypeScript 开发,官方 SDK 最为成熟;Node.js 生态与 AI 应用天然契合,async/await 异步模型也与 MCP 的异步通信完美匹配。
7.1 环境准备
代码块
Plain Text
创建项目目录
mkdir my-mcp-server && cd my-mcp-server
初始化项目
npm init -y
初始化 TypeScript 配置:
代码块
Plain Text
npx tsc --init
修改 tsconfig.json 关键配置:
代码块
JSON
{
"compilerOptions": {
在 package.json 中添加启动脚本:
代码块
JSON
{
"type": "module",
"scripts": {
"build": "tsc",
7.2 最小可运行示例
先从一个最简单的 Server 开始,确保环境正确:
代码块
JavaScript
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// 创建 MCP Server 实例
const server = new McpServer({
运行测试:
开发模式运行(无需编译)
代码块
Plain Text
npx tsx src/index.ts
此时 Server 已在 stdin 上监听 JSON-RPC 消息,等待 Client 连接。
7.3 实现完整功能
下面我们扩展 Server,加入实际有用的功能:
代码块
TypeScript
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "node:fs/promises";
import path from "node:path";
const server = new McpServer({
name: "demo-server",
7.4 代码逐段解析
Tool 定义
代码块
JavaScript
•
server.tool(name, description, schema, handler) 四个参数
•
Zod schema 自动转换为 JSON Schema,同时提供运行时验证
•
handler 接收已验证的参数,返回 { content: [...] } 格式
•
复杂数据用 JSON.stringify() 序列化为 text 类型
Resource 定义
代码块
JavaScript
🔔
•
静态 URI 直接匹配,动态 URI(如 file://{path})通过模板解析参数
•
handler 接收解析后的 URL 对象,返回 { contents: [...] } 格式
•
应用代码通过 URI 主动读取,LLM 不会自动调用
Prompt 定义
代码块
ProtoBuf
server.prompt(
"code_review", // Prompt 名称
🔔
•
返回 { messages: [...] } 格式,支持多轮消息
•
参数使用 Zod 验证,.optional() 标记可选参数
7.5 添加生命周期管理
对于需要初始化资源的 Server(如数据库连接),可以在连接前后管理生命周期:
代码块
TypeScript
// src/lifecycle-server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import Database from "better-sqlite3";
7.6 使用 Server 实例进行高级交互
MCP TypeScript SDK 的 McpServer 实例提供了丰富的 API:
代码块
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({
name: "advanced-server",
version: "1.0.0",
});
// 注册 Tool 并在内部使用 server 能力
server.tool(
"long_running_task",
"执行一个长时间运行的任务",
{ data: z.string().describe("输入数据") },
SDK 核心 API 速览:
八、进阶:Resources 与 Prompts
8.1 动态 Resources
URI 模板中的 {param} 会自动提取为参数:
代码块
TypeScript
server.resource(
"user-profile",
"users://{user_id}/profile",
8.2 Resource 的订阅机制
Client 可以订阅 Resource 的变更通知:
代码块
Plain Text
// 当资源内容变化时通知已订阅的客户端
server.sendResourceListChanged();
8.3 复杂 Prompt 模板
Prompt 可以返回多条消息,支持更复杂的交互:
代码块
Go
server.prompt(
"debug_session",
"启动一个调试会话",
{
error: z.string().describe("错误信息"),
logs: z.string().optional().describe("相关日志"),
},
九、调试与测试
9.1 使用 MCP Inspector
MCP 官方提供了一个可视化调试工具 Inspector:
代码块
Plain Text
安装并启动 Inspector
npx @modelcontextprotocol/inspector npx tsx src/index.ts
Inspector 提供 Web UI,可以:
查看 Server 暴露的所有 Tools / Resources / Prompts
手动调用 Tool 并查看返回值
查看 JSON-RPC 消息日志
9.2 使用 Claude Desktop 测试
在 Claude Desktop 配置文件中添加你的 Server:
🔔
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
代码块
JSON
{
"mcpServers": {
"demo-server": {
"command": "node",
"args": ["/absolute/path/to/build/index.js"],
"env": {
"API_KEY": "your-api-key-if-needed"
}
}
}
开发阶段可以用 tsx 直接运行 TypeScript:
代码块
JSON
{
"mcpServers": {
"demo-server": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/src/index.ts"]
}
}
}
重启 Claude Desktop 后,在对话中即可使用你的 Server 提供的工具。
9.3 单元测试
使用 Vitest 进行测试:
代码块
Plain Text
npm install -D vitest
代码块
JavaScript
// src/tests/server.test.ts
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { createServer } from "../server.js"; // 提取 server 创建函数
describe("MCP Server", () => {
let client: Client;
在 package.json 中添加测试脚本:
代码块
Plain Text
{
"scripts": {
"test": "vitest run"
十、最佳实践与总结
🔔
安全性
🔔
性能
•
异步优先:所有 handler 都是 async,避免阻塞事件循环
•
流式处理:大数据集使用分页返回
🔔
可维护性
•
清晰的描述:Tool 的 description 要详细,这是 LLM 理解工具的唯一依据
•
Zod schema:完善的类型定义同时生成 JSON Schema 和运行时验证
•
错误处理:返回有意义的错误信息,而非抛出异常让 Server 崩溃
•
日志记录:使用 console.error() 输出调试信息(stdout 用于协议通信)
协议版本兼容
代码块
YAML
// SDK 自动处理协议版本协商
const server = new McpServer({
附录:快速参考
完整项目结构
代码块
Python
my-mcp-server/
├── src/
│ ├── index.ts # 主服务入口
│ ├── server.ts # Server 实例创建(便于测试)
│ ├── tools/ # 工具实现
package.json 完整示例:
代码块
JSON
{
"name": "my-mcp-server",
"version": "1.0.0",
常用 MCP 客户端
总结
MCP 协议通过标准化的 JSON-RPC 通信,为 LLM 生态建立了统一的工具接入标准。
本文从协议设计到实际实现,完整覆盖了 MCP 的核心概念:
🔔
•
架构:Host → Client → Server 三层结构
•
通信:JSON-RPC 2.0 + stdio/SSE 传输