前言:AI 时代的“万能插座”与 USB 革命

在探讨 MCP(Model Context Protocol,模型上下文协议) 之前,让我们先回到个人电脑诞生初期的 20 世纪 80 年代。

在那个年代,给电脑插上一个外设是所有用户的噩梦。如果你买了一台打印机、一个鼠标或是一个游戏手柄,你必须面对不同厂商各自设计的奇形怪状的物理接口,还要在操作系统中手动安装专属的驱动程序。如果接口不兼容,你甚至需要去买一块专用的扩展卡。

这种混乱的局面,直到 USB(Universal Serial Bus,通用串行总线) 协议诞生才被彻底终结。USB 定义了统一的物理接口标准和统一的数据传输协议,任何硬件厂商只要遵循 USB 标准,其设备就能插在任何支持 USB 的电脑上即插即用。

现在的 AI 应用领域正经历着一模一样的“前 USB 时代”。

大语言模型(LLM)能力强大,但它们默认是被困在沙盒里的“信息孤岛”。为了让 AI 能够连接你的代码库、读取数据库、调用 GitHub API 或是浏览网页,开发者们不得不面对极度割裂的局面:

  • 针对 OpenAI,你需要按照其 tools 规范写一套 Function Calling;
  • 针对 Anthropic Claude,你又得适配它的 Tool Use 格式;
  • 在 LangChain 框架里写好的 Tool,换到 Semantic Kernel 里又得重构;
  • 在 Cursor 里面能用的代码检索工具,在 Claude Code 或 Windsurf 里完全无法复用。

每个大模型、每个 AI IDE 都在自己造轮子,工具的开发者和工具的使用者之间隔着无数道高墙。

MCP(Model Context Protocol),正是 Anthropic 为了终结这种混乱而提出的 “AI 时代的 USB 协议”。它是一套开放的、标准化的通信协议,旨在统一“大模型客户端”与“外部数据源/工具集”之间的连接方式。有了 MCP,你只需要编写一次工具服务,任何支持 MCP 的 IDE(Cursor, Windsurf, Claude Code, Claude Desktop 等)都能即刻无缝调用。

本文将为您深度拆解 MCP 的底层架构、核心支柱,并手把手带您用 TypeScript 开发一个企业级的 MCP 服务。


一、MCP 核心架构:Host、Client 与 Server

MCP 的通信架构非常优雅,它采用了一种**“三层编排”**的设计模式。理解这三层角色是掌握 MCP 的关键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌────────────────────────────────────────────────────────┐
│ AI 助手客户端 (Client) │
│ (例如: Claude Code, Cursor, Windsurf) │
└───────────────────────────┬────────────────────────────┘
│ (基于标准 JSON-RPC 2.0 协议)

┌────────────────────────────────────────────────────────┐
│ MCP 进程编排器 (Host) │
│ (作为桥梁,负责管理上下文、权限控制与安全流转) │
└───────────────────────────┬────────────────────────────┘
│ (通过 stdio 或 SSE 连接)

┌────────────────────────────────────────────────────────┐
│ MCP 服务端 (Server) │
│ ┌───────────────────┬────────────────────┐ │
│ │ Resources (数据) │ Tools (动作执行) │ │
│ └───────────────────┴────────────────────┘ │
└────────────────────────────────────────────────────────┘
  1. Client(AI 助手客户端)
    这是大模型的代理人(例如 IDE 中的 AI 引擎、Claude Desktop 软件等)。它负责与用户对话,理解用户的意图,决定什么时候需要调用外部工具或读取外部数据,并将数据格式化为大模型的上下文输入。
  2. Host(中间运行环境/编排器)
    通常是运行在本地的命令行工具或 IDE 进程(如物理机运行的 claude 命令行,或 Cursor 编辑器进程)。它充当安全卫士和路由器,负责解析本地配置文件,启动并管理 MCP Server 进程,拦截敏感操作并向用户发起安全授权弹窗。
  3. Server(MCP 服务端)
    这是一个独立的轻量级进程,负责暴露出特定的资源、工具或模板。它不需要知道是谁在调用它(无论是 GPT-4 还是 Claude 3.5 ),它只需要遵循 MCP 协议标准,通过标准输入输出(stdio)或 SSE(Server-Sent Events)向外提供 JSON-RPC 2.0 服务。

这种架构实现了完美的分离:大模型不需要关心底层数据库是怎么连的,数据库服务也不需要关心大模型用的是什么 Prompt,所有的复杂性都被 MCP 这条“USB 线的协议规范”屏蔽了。


二、MCP 的三大支柱:Resources、Tools 与 Prompts

在 MCP 协议的协议层中,Server 主要向 Client 暴露三种核心能力,它们被称为 MCP 的“三大支柱”:

支柱名称 核心定位 读写属性 典型应用场景
Resources (资源) 静态/动态数据源 只读 读取日志文件、查询数据库 Schema、浏览项目文档
Tools (工具) 动作与副作用执行 可读写 / 可执行 执行命令行、修改代码文件、发送 API 请求、创建 Issue
Prompts (提示词模板) 预设对话与任务模板 只读 代码审查模版、Bug 排查引导、周报生成模版

2.1 资源 (Resources):AI 的“外接只读硬盘”

Resources 是 Server 暴露给 AI 的只读数据源。它使用类似网页 URL 的统一资源定位符(URI)来标识。

例如,一个 SQLite MCP Server 可以暴露如下 Resource:
sqlite://localhost/main_db/tables/users/schema

当 Client 发现用户的意图是“了解 users 表的结构”时,它会向 Server 发起 resources/read 请求,Server 将返回该 Schema 的文本或二进制内容。

MIME 类型支持:MCP 资源不仅支持纯文本(text/plain),还支持 application/json 甚至二进制数据(如图片),这使得多模态大模型能够通过资源直接读取并理解各种格式的数据。

2.2 工具 (Tools):AI 的“多功能工具箱”

Tools 是 AI 能够执行的具有副作用的动作。它是 MCP 中最强大的部分。

每个 Tool 都有一个唯一的名称、一段详细的自然语言描述(告诉大模型这个工具是干什么用的,以便大模型决定何时调用它),以及一个基于 JSON Schema 定义的输入参数规范。

例如,一个执行写入文件的 Tool 声明可能如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "write_file",
"description": "向指定的绝对路径写入文件内容,若文件不存在则自动创建。",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "文件的绝对路径" },
"content": { "type": "string", "description": "要写入的文本内容" }
},
"required": ["path", "content"]
}
}

当大模型觉得需要写文件时,它会在回复中输出一个“Tool Call”请求,Host 拦截到这个请求后,会在终端或 IDE 中提示用户:“是否允许 AI 运行工具 write_file 写入内容到 /app/index.js?”。用户点击同意后,Host 将参数传递给 MCP Server 执行,并将执行结果返回给大模型。

2.3 提示词模板 (Prompts):AI 的“快捷任务指令”

Prompts 是预先设定好的提示词模板。它们可以接受参数,帮助用户快速开启一个特定场景的对话。

比如,一个代码审查 Server 可以暴露一个 code-review 提示词模板,接受一个 file_path 参数。当用户选择这个模板时,系统会自动生成一段包含特定审查标准、格式要求的 Prompt 喂给大模型,极大免去了用户反复手写长 prompt 的麻烦。


三、主流 AI 客户端 MCP 配置实战

在本地使用 MCP 插件非常简单,主流的 AI 编程辅助工具均已原生支持。MCP Server 的底层是通过进程通信的,因此配置的核心就是告诉 Host:如何通过命令行启动你的 MCP Server

下面分别介绍最常用的四种客户端的配置方式。

3.1 Claude Code 命令行配置

Claude Code 提供了极其好用的命令行工具来管理 MCP 插件,它会自动为您读写配置文件。

配置文件路径

  • 用户全局范围:~/.claude.json
  • 项目级范围:项目根目录下的 .mcp.json(可随 Git 提交,团队共享)

通过命令增删(推荐,极其方便)

1
2
3
4
5
6
7
8
9
10
11
# 1. 交互式添加一个全局 SQLite 数据库 MCP 插件
claude mcp add --scope user sqlite -- uvx mcp-server-sqlite --db-path ./prod.db

# 2. 为当前项目添加一个 GitHub 管理 MCP 插件(带敏感环境变量传递)
claude mcp add --scope project github -- npx @anthropic/mcp-server-github --env GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxx

# 3. 查看当前已加载并激活的所有 MCP 服务
claude mcp list

# 4. 删除某个不需要的 MCP 服务
claude mcp remove sqlite

3.2 Cursor 配置方式

Cursor 在其图形化界面中提供了直观的 MCP 管理后台:

  1. 打开 Cursor,进入 Settings -> Features -> MCP
  2. 点击 + Add New MCP Server
  3. 在弹出的窗口中配置:
    • Name: 比如 filesystem
    • Type: 选择 command(基于 stdio 管道连接)或 SSE(通过 HTTP 网络连接)
    • Command: 填入启动命令,例如 npx -y @anthropic/mcp-server-filesystem /path/to/project
  4. 点击保存,若状态指示灯变绿,说明连接成功,你可以在 AI Chat 或 Composer 中直接要求它读取该目录下的文件。

3.3 Windsurf 配置方式

Windsurf 的配置也是基于 JSON 文件的。
配置文件路径~/.codeium/windsurf/mcp_config.json

配置 JSON 结构示例

1
2
3
4
5
6
7
8
9
10
11
{
"mcpServers": {
"git": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git"],
"env": {
"PATH": "/usr/bin:/usr/local/bin"
}
}
}
}

四、手把手带你开发一个 TypeScript MCP Server

现在,让我们一起“创造”一个属于自己的 MCP Server!

为了让这个实战项目既简单又实用,我们将开发一个**系统性能监控与硬件分析(SysMonitor)**的 MCP 服务。它将暴露:

  1. 一个只读资源system://spec,用于返回本机的静态硬件配置规格(操作系统、CPU核心数等)。
  2. 一个可执行工具get_system_metrics,用于实时抓取系统当前的 CPU 负载、空闲内存、磁盘空间,供大模型进行诊断。

我们将使用官方的 Node.js SDK 进行开发。

4.1 初始化项目

首先,在干净的目录下初始化一个 Node.js TypeScript 项目:

1
2
3
4
5
6
7
8
mkdir mcp-server-sysmonitor
cd mcp-server-sysmonitor
npm init -y

# 安装核心依赖
npm install @modelcontextprotocol/sdk
# 安装开发工具和类型文件
npm install -D typescript @types/node tsx

在根目录下创建 tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}

修改 package.json,添加编译与运行脚本,并确保声明 "type": "module"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "mcp-server-sysmonitor",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx src/index.ts"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.1"
},
"devDependencies": {
"@types/node": "^20.0.0",
"tsx": "^4.0.0",
"typescript": "^5.0.0"
}
}

4.2 编写核心代码

创建 src/index.ts,写入以下完整、注释详尽的 MCP Server 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import * as os from "os";

// 1. 创建一个名为 "sysmonitor" 的 MCP Server 实例
const server = new Server(
{
name: "sysmonitor-server",
version: "1.0.0",
},
{
capabilities: {
resources: {}, // 声明拥有资源服务能力
tools: {}, // 声明拥有工具执行能力
},
}
);

// 定义资源 URI 规范
const SPEC_RESOURCE_URI = "system://spec";

/**
* 2. 注册资源列表查询 Handler
* 当 AI 客户端想要知道我们暴露了哪些数据源时,会调用此方法
*/
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: SPEC_RESOURCE_URI,
name: "本机硬件静态规格说明",
mimeType: "application/json",
description: "提供操作系统的类型、架构、CPU型号及物理内存总量信息。",
},
],
};
});

/**
* 3. 注册资源读取 Handler
* 当 AI 想要读取具体资源内容时,会调用此方法
*/
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri === SPEC_RESOURCE_URI) {
const specData = {
platform: os.platform(),
release: os.release(),
arch: os.arch(),
cpuModel: os.cpus()[0]?.model || "未知 CPU",
cpuCores: os.cpus().length,
totalMemoryGB: (os.totalmem() / (1024 * 1024 * 1024)).toFixed(2) + " GB",
};

return {
contents: [
{
uri: SPEC_RESOURCE_URI,
mimeType: "application/json",
text: JSON.stringify(specData, null, 2),
},
],
};
}

throw new Error(`未找到指定的资源: ${request.params.uri}`);
});

/**
* 4. 注册工具列表查询 Handler
* 声明我们拥有哪些动作工具,以及它们的参数格式
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_system_metrics",
description: "获取服务器或本地主机的当前实时运行指标(CPU 负载、空闲内存与系统运行时间)。",
inputSchema: {
type: "object",
properties: {}, // 此工具无需输入参数
},
},
],
};
});

/**
* 5. 注册工具执行 Handler
* 真正执行底层的 Node.js 动作,并将结果返回给 AI
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_system_metrics") {
// 获取 CPU 的 1/5/15 分钟平均负载情况
const loadAvg = os.loadavg();
// 计算空闲内存占比
const freeMem = os.freemem();
const totalMem = os.totalmem();
const freeMemPercentage = ((freeMem / totalMem) * 100).toFixed(1);
const uptimeHours = (os.uptime() / 3600).toFixed(2);

const metrics = {
loadAverage1Min: loadAvg[0].toFixed(2),
loadAverage5Min: loadAvg[1].toFixed(2),
loadAverage15Min: loadAvg[2].toFixed(2),
freeMemoryPercentage: `${freeMemPercentage}%`,
freeMemoryGB: (freeMem / (1024 * 1024 * 1024)).toFixed(2) + " GB",
systemUptimeHours: uptimeHours,
};

return {
content: [
{
type: "text",
text: `📊 实时硬件指标已获取:\n${JSON.stringify(metrics, null, 2)}`,
},
],
};
}

throw new Error(`未找到指定的工具: ${request.params.name}`);
});

/**
* 6. 启动进程连接
* 使用 Stdio(标准输入输出)作为底层的传输协议,将当前 Server 挂载在管道上
*/
async function run() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("SysMonitor MCP Server 已成功通过 stdio 启动!");
}

run().catch((error) => {
console.error("启动失败:", error);
process.exit(1);
});

[!WARNING]
极其关键的避坑指南
在 MCP 的 stdio 传输模式下,所有的 JSON-RPC 请求与响应都是通过**标准输出(stdout)**传输的。这意味着:严禁在你的服务端代码中使用任何 console.log() 输出普通的日志调试信息! 任何非 JSON-RPC 格式的普通字符混入 stdout,都会导致 IDE 客户端的解析协议崩溃,从而导致连接中断。所有的调试、提示日志都必须使用 console.error() 输出到标准错误流中!

4.3 编译并加载到 IDE

首先,运行编译命令将 TypeScript 代码编译为 JavaScript:

1
npm run build

接着,我们将这个 Server 配置到我们的 IDE 中。以 Cursor 为例:

  1. 打开 Cursor 选项面板,进入 Features -> MCP
  2. 点击 + Add New MCP Server
  3. Name: sysmonitor
  4. Type: command
  5. Command:
    1
    node /absolute/path/to/mcp-server-sysmonitor/dist/index.js
    (请将 /absolute/path/to/ 替换为您本地克隆该项目的真实绝对物理路径)
  6. 点击保存。

现在,打开 Cursor Chat 窗口,输入:
“帮我查看一下这台电脑的系统静态指标,并诊断一下它当前的 CPU 负载和内存状况是否健康。”

你会看到大模型(比如 Claude 3.5 Sonnet)熟练地运行了 system://spec 获取了你的 CPU 型号和内存总量,紧接着又调用了 get_system_metrics 工具拿到了实时的负载数据,并用专业的口吻给出了系统的健康分析报告!这一切,都在你毫秒级的对话间自动完成。


五、企业级项目落地:安全管控与隔离实践

MCP 的便利性是毋庸置疑的,但是如果将它应用到企业级生产环境或是联入公司内网数据,安全(Security) 则是不可逾越的红线。

大模型具有“幻觉”和“过度执行指令”的风险。如果你直接给它一个拥有物理删除权限的 Tool,在面对恶意的 Prompt 注入时,AI 可能会在不经意间把数据库全删掉。

5.1 最小权限原则(Principle of Least Privilege)

  1. 数据库连接隔离:如果开发一个读取数据库的 MCP Server,请务必使用只读(Read-Only)权限的数据库账号,严格禁止在大模型工具端直接暴露 DELETEDROPUPDATE 等写入与删除接口。
  2. 文件系统限定:官方的 @anthropic/mcp-server-filesystem Server 强制要求在启动参数中明确指定允许访问的绝对路径列表。AI 试图读取这几个指定目录之外的任何文件都会被底层直接拦截,无法越界越权。

5.2 显式人类确认(Human-in-the-loop)

由于 MCP 的 Host 进程(如 Claude Code)充当着大门守卫的角色,它默认会在执行具有“副作用”的 Tool 时,强行暂停执行并抛出确认请求。
企业规范建议:在开发具有修改代码、提交 Git、调用外部收费 API 等操作的 Tool 时,不要试图在代码中自动通过,务必依赖客户端的确认拦截,或者在 Server 代码中加入二次令牌校验。

5.3 传输层协议的安全选择(Stdio vs SSE)

  • Stdio 模式:MCP 进程作为 IDE 子进程启动,它的生命周期完全由 IDE 掌控,通信只存在于本机的内存管道中,不经过网络,天然具备极高的局域安全性。非常适合个人本地开发、代码读取等场景。
  • SSE(Server-Sent Events)模式:如果企业的内部数据存放在私有云端或局域网服务器,可以通过 HTTP 建立 SSE 连接。此时,必须在 SSE Server 前置一层 API Gateway(API 网关),实施严格的 OAuth2.0 / Token 鉴权TLS 加密传输,防止内网敏感数据泄露或工具被非法越权调用。

结语:下一代 AI 工具生态的基石

MCP 协议的出现,正如当年的 USB 标准一样,彻底打破了 AI 模型与工具应用之间的孤岛壁垒。它不仅极大减轻了工具开发者的开发心智负担,也为 AI IDE 提供了无穷无尽的扩展可能性。

未来,无论是操作 Jira 自动分配任务、连入内部知识库做高精度 RAG,还是驱动浏览器进行复杂的 UI 自动化测试,我们都可以通过统一配置、即插即用的 MCP Server 轻松达成。

遵循开放标准,拥抱 MCP 协议,让你的 AI 助手真正长出强有力的“双手”,连通无限的外部世界!