首页 › MCP › MCP 协议详解:从原理到手把手实现一个 MCP Server

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 传输​

​

​

​