MCP 深度解析:Model Context Protocol 如何重塑 AI 应用开发——从协议设计到生产级实战的完整技术内幕
当 AI 应用需要从"能聊天"进化到"能干活",MCP 就是那个让一切成为可能的"万能插座"。
前言:AI 应用的"接口之痛"
2024 年底,如果你问一个 AI 应用开发者:"接入一个新工具最难的是什么?" 答案几乎肯定是:每个工具都要写一套适配代码。
数据库要写 SQL 封装,API 要写请求签名,文件系统要处理权限,每套工具都有自己的:
- 认证方式(API Key、OAuth、SPI)
- 数据格式(JSON、XML、Protobuf)
- 错误处理(重试、超时、熔断)
- 文档质量(从"详尽"到"不存在")
结果就是:AI 应用 80% 的代码都在处理集成,而不是智能本身。
Anthropic 看到了这个问题。2024 年 11 月,他们推出了 Model Context Protocol (MCP) —— 一个开放标准协议,旨在统一 AI 模型与外部世界的通信方式。
这篇文章,我们将深入 MCP 的技术内幕:
- 协议设计的哲学与架构决策
- 传输层实现:Stdio vs HTTP+SSE 的权衡
- 服务端开发完整指南(Go/Python/TypeScript)
- 生产环境中的性能优化与安全实践
- 真实案例:从零构建企业级 MCP Server
第一部分:MCP 协议设计哲学
1.1 为什么需要 MCP?
在深入技术细节之前,我们需要回答一个关键问题:为什么是现在?
1.1.1 AI 应用的进化路径
第一代:纯聊天(2022-2023)
User → LLM → Text Output
能力:对话、写作、翻译
限制:无法访问外部数据,无法执行操作
第二代:工具调用(2023-2024)
User → LLM → Tool Selection → Execute → Return
能力:搜索、计算、API 调用
限制:每个工具都要定制集成,无法复用
第三代:自主 Agent(2024-现在)
User → LLM → Multi-step Planning → Tool Chain → Execution
能力:复杂任务自动化、多工具协同
挑战:工具爆炸式增长,集成成本不可接受
MCP 正是为第三代 AI 应用设计的。它的核心洞察是:
工具集成不应该成为 AI 能力的瓶颈。
1.1.2 现有方案的致命缺陷
在 MCP 之前,开发者通常有两种选择:
方案 A:Function Calling(OpenAI 风格)
# 每个工具都要手动定义 JSON Schema
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}
}
]
# 每次调用都要解析 LLM 输出,手动执行,再塞回上下文
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[...],
tools=tools # ← 每个模型、每个厂商格式都不同!
)
问题:
- 厂商锁定:OpenAI 的 Function Calling 格式与 Anthropic、Google 不兼容
- 无状态:每次都要重新传递完整工具定义
- 无标准:工具发现、错误处理、流式响应都没有标准
方案 B:LangChain Tools / LlamaIndex
from langchain.tools import Tool
def search_db(query: str) -> str:
# 数据库查询逻辑
return execute_sql(query)
tools = [
Tool(
name="DatabaseSearch",
func=search_db,
description="Search the database"
)
]
# 看起来美好,但...
agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS)
问题:
- 框架锁定:工具绑定到 LangChain,无法迁移
- 黑盒化:Agent 内部逻辑难以调试和优化
- 性能开销:每个工具调用都经过多层抽象
MCP 的解决方案:
传统方式:N 个 AI 应用 × M 个工具 = N×M 个集成
MCP 方式:
AI 应用 → MCP Client (标准) → MCP Server (标准) ← 工具
↑ ↑
只需写一次 只需实现一次
1.2 MCP 的核心设计原则
Anthropic 在设计 MCP 时,遵循了几个关键原则:
原则 1:简单优于功能
MCP 协议极其简洁,只有三个核心概念:
┌─────────────────────────────────────────────────────┐
│ MCP Host App │
│ (Claude Desktop, Cursor, 自定义 Agent) │
│ │
│ ┌──────────────┐ │
│ │ MCP Client │←→ (Transport) ←→┌─────────────┐ │
│ └──────────────┘ │ MCP Server │ │
│ │ • tools │ │
│ │ • resources │ │
│ │ • prompts │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────┘
- MCP Client:运行在 Host 应用中,负责发现 Server 能力并调用
- MCP Server:暴露工具、资源、提示词的服务端
- Transport:Client 和 Server 之间的通信方式(Stdio / HTTP+SSE)
为什么这么简单?
因为简单意味着:
- 易于实现(任何语言都能快速写出 MCP Server)
- 易于审计(安全关键场景可以完全审查代码)
- 易于扩展(新增能力不需要改协议)
原则 2:可组合性优于单体
MCP 设计时考虑到了微服务端架构:
# 你可以有多个 MCP Server,各司其职
mcp_servers:
database:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-postgres"]
env:
DATABASE_URL: "postgres://..."
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem"]
args: ["--allowed-dir", "/workspace"]
web_search:
command: "python"
args: ["mcp_server_brave.py"]
env:
BRAVE_API_KEY: "..."
AI Agent 可以同时连接多个 MCP Server,就像 Unix 管道一样组合它们:
User: "分析去年销售数据,生成图表,保存到我的笔记"
Agent 执行链:
1. MCP Server (database) → query_sales_data()
2. MCP Server (chart-gen) → generate_chart()
3. MCP Server (filesystem) → save_file()
4. MCP Server (notion) → create_page()
原则 3:安全是头等公民
MCP 在设计时就考虑了企业级安全要求:
1. 显式授权模型
// MCP Server 可以声明需要授权
export const tool = {
name: "send_email",
description: "Send an email",
annotations: {
requiresAuthorization: true, // ← 关键!
authorizationHint: "This will send an email on your behalf"
}
}
// Host App 必须显式请求用户同意
if (tool.annotations.requiresAuthorization) {
const approved = await host.requestUserApproval(tool);
if (!approved) throw new Error("User denied authorization");
}
2. 沙箱隔离
# MCP Server 可以运行在独立进程中
# 即使 Server 被攻击,Host 也受影响有限
import subprocess
process = subprocess.Popen(
["node", "mcp_server.js"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# 通过 Stdio 通信,进程隔离
3. 能力协商
// Client 和 Server 在连接时协商能力
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {"listChanged": true},
"resources": {"subscribe": true, "listChanged": true}
},
"clientInfo": {"name": "claude-desktop", "version": "1.0.0"}
}
}
第二部分:MCP 架构深度解析
2.1 协议层:JSON-RPC 2.0 的选择
MCP 使用 JSON-RPC 2.0 作为消息格式。为什么?
2.1.1 为什么是 JSON-RPC?
| 方案 | 优点 | 缺点 | MCP 选择? |
|---|---|---|---|
| REST | 成熟、生态丰富 | 无状态、不适合长连接 | ❌ |
| GraphQL | 灵活查询 | 复杂、过度设计 | ❌ |
| gRPC | 高性能、类型安全 | 依赖 Protobuf、浏览器不友好 | ❌ |
| JSON-RPC 2.0 | 简单、无状态、人类可读 | 无类型检查 | ✅ |
核心原因:MCP 需要最简单的双向通信协议。
JSON-RPC 2.0 只有一个要求:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "query_database",
"arguments": {"sql": "SELECT * FROM users"}
},
"id": 1
}
就这么简单。不需要 WSDL、不需要 OpenAPI Spec、不需要代码生成。
2.1.2 MCP 的 JSON-RPC 扩展
MCP 在 JSON-RPC 基础上做了三个关键扩展:
扩展 1:流式响应(Streaming)
// 传统 JSON-RPC:一次请求,一次响应
// MCP:支持流式返回中间结果
// Server 可以逐步返回进度
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": {
"progressToken": "abc123",
"progress": 0.5,
"message": "Processing 5000 records..."
}
}
// Client 收到多个 progress 通知,直到最终响应
{
"jsonrpc": "2.0",
"result": {"status": "completed", "data": [...]},
"id": 1
}
扩展 2:资源订阅(Resources Subscription)
// Client 可以订阅资源变化
{
"jsonrpc": "2.0",
"method": "resources/subscribe",
"params": {
"uri": "file:///workspace/config.json"
},
"id": 2
}
// Server 主动推送变化通知
{
"jsonrpc": "2.0",
"method": "notifications/resources/updated",
"params": {
"uri": "file:///workspace/config.json"
}
}
扩展 3:工具列表变更通知
// Server 动态添加/移除工具时通知 Client
{
"jsonrpc": "2.0",
"method": "notifications/tools/list_changed",
"params": {}
}
// Client 重新拉取工具列表
{
"jsonrpc": "2.0",
"method": "tools/list",
"params": {},
"id": 3
}
2.2 传输层:Stdio vs HTTP+SSE
MCP 支持两种标准传输方式,各有适用场景。
2.2.1 Stdio 传输:本地进程通信
工作原理:
┌─────────────────┐ Stdio ┌─────────────────┐
│ MCP Client │ ◄──── stdin/stdout ──► │ MCP Server │
│ (Host Process) │ │ (Child Process) │
└─────────────────┘ └─────────────────┘
实现示例(Node.js):
// mcp_server_stdio.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({
name: "example-stdio-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// 注册工具
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "echo",
description: "Echo back the input",
inputSchema: {
type: "object",
properties: {
message: { type: "string" }
},
required: ["message"]
}
}
]
}));
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "echo") {
return {
content: [
{ type: "text", text: request.params.arguments.message }
]
};
}
throw new Error("Tool not found");
});
// 启动 Stdio 传输
const transport = new StdioServerTransport();
await server.connect(transport);
// ↑ 从此,Server 从 stdin 读取请求,向 stdout 写入响应
Stdio 的优势:
- 零网络配置:不需要端口、不需要 HTTPS 证书
- 进程隔离:Server 崩溃不会影响 Host
- 低延迟:本地 IPC,微秒级延迟
Stdio 的劣势:
- 仅限本地:无法远程访问
- 无防火墙友好性:企业环境可能禁止创建子进程
2.2.2 HTTP+SSE 传输:远程通信
工作原理:
┌─────────────────┐ HTTP POST ┌─────────────────┐
│ MCP Client │ ──────── req ──────► │ MCP Server │
│ │ ◄────── SSE Stream ──│ (HTTP Server) │
└─────────────────┘ └─────────────────┘
实现示例(Express + SSE):
// mcp_server_http.ts
import express from "express";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
const app = express();
app.use(express.json());
const server = new Server({
name: "example-http-server",
version: "1.0.0"
}, {
capabilities: { tools: {} }
});
// 注册工具(同上)
server.setRequestHandler("tools/list", async () => ({ /* ... */ }));
server.setRequestHandler("tools/call", async (request) => { /* ... */ });
// SSE 端点
app.get("/sse", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
const transport = new SSEServerTransport("/message", res);
await server.connect(transport);
});
// Client 通过此端点发送请求
app.post("/message", async (req, res) => {
// 将请求转发给 MCP Server
const response = await server.handleRequest(req.body);
res.json(response);
});
app.listen(3000);
HTTP+SSE 的优势:
- 远程访问:可以通过互联网访问
- 防火墙友好:使用标准 HTTP/HTTPS 端口
- 可扩展:可以部署多个 Server 实例 behind load balancer
HTTP+SSE 的劣势:
- 复杂度高:需要处理连接管理、重连、认证
- 延迟较高:网络往返时间(RTT)影响性能
2.2.3 传输方式选择指南
| 场景 | 推荐传输 | 原因 |
|---|---|---|
| 本地开发工具 | Stdio | 零配置,进程隔离 |
| 访问本地文件/数据库 | Stdio | 低延迟,无需网络 |
| 企业内网服务 | HTTP+SSE | 集中管理,可监控 |
| 云服务 API | HTTP+SSE | 远程访问,负载均衡 |
| 高安全要求 | Stdio | 进程隔离,攻击面小 |
第三部分:MCP Server 开发实战
3.1 快速起步:5 分钟写一个 MCP Server
让我们从最简单的例子开始:一个计算器 MCP Server。
3.1.1 环境准备
# 创建项目
mkdir mcp-calculator && cd mcp-calculator
npm init -y
# 安装依赖
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node
# tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true
}
}
3.1.2 实现计算器 Server
// src/index.ts
#!/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";
// 创建 Server 实例
const server = new Server(
{
name: "calculator-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 注册工具列表处理器
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "add",
description: "Add two numbers",
inputSchema: {
type: "object",
properties: {
a: { type: "number", description: "First number" },
b: { type: "number", description: "Second number" },
},
required: ["a", "b"],
},
},
{
name: "multiply",
description: "Multiply two numbers",
inputSchema: {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" },
},
required: ["a", "b"],
},
},
],
};
});
// 注册工具调用处理器
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "add") {
const result = (args.a as number) + (args.b as number);
return {
content: [
{
type: "text",
text: `Result: ${result}`,
},
],
};
}
if (name === "multiply") {
const result = (args.a as number) * (args.b as number);
return {
content: [
{
type: "text",
text: `Result: ${result}`,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// 启动 Server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Calculator MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
3.1.3 测试 Server
# 编译
npm run build
# 方式 1:直接测试(手动输入 JSON-RPC 请求)
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js
# 输出:
# {"jsonrpc":"2.0","result":{"tools":[...]},"id":1}
# 方式 2:使用 MCP Inspector(官方调试工具)
npx @modelcontextprotocol/inspector dist/index.js
3.2 实战案例:PostgreSQL MCP Server
让我们构建一个生产级的 MCP Server:PostgreSQL 查询工具。
3.2.1 功能设计
工具列表:
- query: 执行 SELECT 查询(只读)
- execute: 执行 INSERT/UPDATE/DELETE(需要授权)
- list_tables: 列出所有表
- describe_table: 显示表结构
资源列表:
- postgres://tables/{table_name}: 表数据(自动更新)
3.2.2 完整实现
// src/postgres_server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { Pool } from "pg";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// 初始化数据库连接池
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
idleTimeoutMillis: 30000,
});
const server = new Server(
{
name: "postgres-server",
version: "1.0.0",
},
{
capabilities: {
tools: { listChanged: true },
resources: { subscribe: true, listChanged: true },
},
}
);
// ========== 工具处理 ==========
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "query",
description: "Execute a SELECT query (read-only)",
inputSchema: {
type: "object",
properties: {
sql: {
type: "string",
description: "SELECT SQL statement",
},
},
required: ["sql"],
},
annotations: {
readOnlyHint: true, // ← 提示:这是只读操作
},
},
{
name: "list_tables",
description: "List all tables in the database",
inputSchema: {
type: "object",
properties: {},
},
annotations: {
readOnlyHint: true,
},
},
{
name: "describe_table",
description: "Show table structure (columns, types, constraints)",
inputSchema: {
type: "object",
properties: {
table: { type: "string" },
},
required: ["table"],
},
annotations: {
readOnlyHint: true,
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (name === "query") {
// 安全检查:只允许 SELECT
const sql = args.sql as string;
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
throw new Error("Only SELECT queries are allowed");
}
const result = await pool.query(sql);
return {
content: [
{
type: "text",
text: JSON.stringify(result.rows, null, 2),
},
],
};
}
if (name === "list_tables") {
const result = await pool.query(`
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
`);
return {
content: [
{
type: "text",
text: result.rows.map((r) => r.table_name).join("\n"),
},
],
};
}
if (name === "describe_table") {
const result = await pool.query(
`
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = $1
ORDER BY ordinal_position
`,
[args.table]
);
const description = result.rows
.map(
(col) =>
`${col.column_name}: ${col.data_type} (nullable: ${col.is_nullable})`
)
.join("\n");
return {
content: [{ type: "text", text: description }],
};
}
throw new Error(`Unknown tool: ${name}`);
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error.message}`,
},
],
isError: true, // ← 标记为错误
};
}
});
// ========== 资源处理 ==========
server.setRequestHandler(ListResourcesRequestSchema, async () => {
// 动态生成资源列表(所有表)
const result = await pool.query(`
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
`);
const resources = result.rows.map((row) => ({
uri: `postgres://table/${row.table_name}`,
name: `Table: ${row.table_name}`,
description: `Data in ${row.table_name}`,
mimeType: "application/json",
}));
return { resources };
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
const match = uri.match(/^postgres:\/\/table\/(.+)$/);
if (!match) throw new Error("Invalid URI");
const tableName = match[1];
const result = await pool.query(`SELECT * FROM ${tableName} LIMIT 100`);
return {
contents: [
{
uri,
mimeType: "application/json",
text: JSON.stringify(result.rows, null, 2),
},
],
};
});
// 启动 Server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("PostgreSQL MCP Server running");
}
main().catch(console.error);
3.2.3 安全加固
生产环境中,需要添加额外的安全层:
// src/security.ts
// 1. SQL 注入防护
function sanitizeSQL(sql: string): string {
// 禁止多语句
if (sql.includes(";")) {
throw new Error("Multiple statements not allowed");
}
// 禁止危险操作
const forbidden = /\b(DROP|DELETE|UPDATE|INSERT|TRUNCATE|ALTER)\b/i;
if (forbidden.test(sql)) {
throw new Error("Write operations not allowed in query tool");
}
return sql;
}
// 2. 速率限制
class RateLimiter {
private requests: number[] = [];
constructor(
private maxRequests: number,
private windowMs: number
) {}
check(): boolean {
const now = Date.now();
this.requests = this.requests.filter((t) => now - t < this.windowMs);
if (this.requests.length >= this.maxRequests) {
return false; // 拒绝
}
this.requests.push(now);
return true;
}
}
const rateLimiter = new RateLimiter(100, 60000); // 100 req/min
// 在工具调用前检查
if (!rateLimiter.check()) {
throw new Error("Rate limit exceeded");
}
// 3. 查询超时
const timeout = 30000; // 30 秒
const result = await pool.query(sql, { timeout });
3.3 高级主题:MCP Server 的性能优化
3.3.1 连接池管理
// 优化前:每次查询都创建新连接
async function query(sql: string) {
const client = await pool.connect(); // ← 慢!
try {
return await client.query(sql);
} finally {
client.release();
}
}
// 优化后:使用预处理语句 + 连接复用
const preparedStatements = new Map<string, string>();
async function query(sql: string, params: any[]) {
const key = `${sql}:${params.length}`;
if (!preparedStatements.has(key)) {
const name = `stmt_${preparedStatements.size}`;
await pool.query(`PREPARE ${name} AS ${sql}`);
preparedStatements.set(key, name);
}
const stmtName = preparedStatements.get(key)!;
return await pool.query(`EXECUTE ${stmtName}`, params);
}
3.3.2 结果流式传输
对于大结果集,使用流式传输避免内存爆炸:
import { QueryStream } from "pg";
async function* streamQuery(sql: string) {
const stream = new QueryStream(sql);
const query = pool.query(stream);
for await (const row of query) {
yield row; // ← 逐行返回,不占用大量内存
}
}
// 在 MCP 工具中使用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "query_large_dataset") {
const sql = request.params.arguments.sql;
// 返回流式响应
const rows = [];
for await (const row of streamQuery(sql)) {
rows.push(row);
if (rows.length % 1000 === 0) {
// 发送进度通知
server.notification({
method: "notifications/progress",
params: { progress: rows.length },
});
}
}
return {
content: [{ type: "text", text: JSON.stringify(rows) }],
};
}
});
第四部分:MCP Client 开发与集成
4.1 在 AI Agent 中集成 MCP
4.1.1 架构设计
┌─────────────────────────────────────────────────┐
│ AI Agent (Host App) │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ MCP Client 1│ │ MCP Client 2│ ... │
│ │ (Database) │ │ (Filesystem)│ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ └──────────┬───────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Tool Router │ │
│ │ (统一工具接口) │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ LLM (Claude/GPT) │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────┘
4.1.2 实现 MCP Client
// src/mcp_client.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
export class MCPClientWrapper {
private client: Client;
private transport: StdioClientTransport;
private tools: any[] = [];
constructor(serverCommand: string, serverArgs: string[]) {
this.transport = new StdioClientTransport({
command: serverCommand,
args: serverArgs,
});
this.client = new Client(
{
name: "ai-agent-client",
version: "1.0.0",
},
{
capabilities: {},
}
);
}
async connect() {
await this.client.connect(this.transport);
// 初始化:获取 Server 能力
const initResult = await this.client.initialize();
console.log("Server capabilities:", initResult.capabilities);
// 拉取工具列表
const toolsResult = await this.client.listTools();
this.tools = toolsResult.tools;
console.log(`Loaded ${this.tools.length} tools`);
}
async callTool(name: string, args: any) {
return await this.client.callTool({
name,
arguments: args,
});
}
getTools() {
return this.tools;
}
async disconnect() {
await this.client.close();
}
}
4.1.3 与 LLM 集成
// src/agent.ts
import Anthropic from "@anthropic-ai/sdk";
import { MCPClientWrapper } from "./mcp_client.js";
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
async function runAgent(userMessage: string) {
// 1. 连接所有 MCP Server
const mcpClients = [
new MCPClientWrapper("node", ["mcp_servers/database.js"]),
new MCPClientWrapper("node", ["mcp_servers/filesystem.js"]),
];
await Promise.all(mcpClients.map((c) => c.connect()));
// 2. 收集所有工具
const allTools = mcpClients.flatMap((client) =>
client.getTools().map((tool) => ({
name: tool.name,
description: tool.description,
input_schema: tool.inputSchema,
}))
);
// 3. 调用 LLM
const response = await anthropic.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 4096,
tools: allTools,
messages: [{ role: "user", content: userMessage }],
});
// 4. 处理工具调用
for (const content of response.content) {
if (content.type === "tool_use") {
const { name, input } = content;
// 找到对应的 MCP Client
const client = mcpClients.find((c) =>
c.getTools().some((t) => t.name === name)
);
if (!client) throw new Error(`Tool ${name} not found`);
// 执行工具
const result = await client.callTool(name, input);
// 将结果返回给 LLM
// ...(省略多轮对话逻辑)
}
}
// 5. 清理
await Promise.all(mcpClients.map((c) => c.disconnect()));
}
第五部分:生产级部署与运维
5.1 部署架构
5.1.1 本地部署(Stdio)
┌────────────────────────────────┐
│ User's Laptop │
│ ┌──────────────────────────┐ │
│ │ Claude Desktop (Host) │ │
│ │ ┌────────────────────┐ │ │
│ │ │ MCP Client │ │ │
│ │ └─────────┬──────────┘ │ │
│ │ │ stdin/stdout │ │
│ │ ┌─────────▼──────────┐ │ │
│ │ │ MCP Server │ │ │
│ │ │ (child process) │ │ │
│ │ └────────────────────┘ │ │
│ └──────────────────────────┘ │
└────────────────────────────────┘
配置示例(Claude Desktop):
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"postgres": {
"command": "node",
"args": ["/path/to/postgres_server.js"],
"env": {
"DATABASE_URL": "postgres://user:pass@localhost:5432/mydb"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
}
}
}
5.1.2 远程部署(HTTP+SSE)
┌─────────────┐ ┌─────────────────┐
│ AI Agent │ ──────│ Load Balancer │
│ (Cloud) │ │ (HTTPS) │
└─────────────┘ └────────┬────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌──────▼──────┐ ┌─────▼──────┐ ┌──────▼──────┐
│ MCP Server │ │ MCP Server │ │ MCP Server │
│ Instance 1 │ │ Instance 2 │ │ Instance 3 │
└─────────────┘ └────────────┘ └─────────────┘
│ │ │
└───────────────┼───────────────┘
│
┌──────▼──────┐
│ PostgreSQL │
│ (Primary) │
└─────────────┘
Dockerfile:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
Kubernetes Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
replicas: 3
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
containers:
- name: mcp-server
image: myregistry/mcp-server:latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
resources:
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: mcp-server-svc
spec:
selector:
app: mcp-server
ports:
- port: 80
targetPort: 3000
type: ClusterIP
5.2 监控与可观测性
5.2.1 关键指标
// src/metrics.ts
import { register, Counter, Histogram } from "prom-client";
// 工具调用计数
const toolCalls = new Counter({
name: "mcp_tool_calls_total",
help: "Total number of tool calls",
labelNames: ["tool_name", "status"],
});
// 工具调用延迟
const toolDuration = new Histogram({
name: "mcp_tool_duration_seconds",
help: "Tool call duration in seconds",
labelNames: ["tool_name"],
buckets: [0.1, 0.5, 1, 2, 5, 10],
});
// 在工具调用时记录
async function callTool(name: string, args: any) {
const timer = toolDuration.startTimer({ tool_name: name });
try {
const result = await executeTool(name, args);
toolCalls.inc({ tool_name: name, status: "success" });
return result;
} catch (error) {
toolCalls.inc({ tool_name: name, status: "error" });
throw error;
} finally {
timer();
}
}
5.2.2 分布式追踪
// src/tracing.ts
import { trace, SpanStatusCode } from "@opentelemetry/api";
const tracer = trace.getTracer("mcp-server");
async function callTool(name: string, args: any) {
const span = tracer.startSpan(`mcp.tool.${name}`, {
attributes: {
"mcp.tool.name": name,
"mcp.tool.args": JSON.stringify(args),
},
});
try {
const result = await executeTool(name, args);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message,
});
throw error;
} finally {
span.end();
}
}
第六部分:真实案例研究
6.1 案例 1:企业知识库问答系统
需求:
- 员工可以自然语言查询公司内部文档
- 文档存储在 Notion + Google Drive
- 需要权限控制(只能访问自己有权限的文档)
架构:
User Question
│
▼
┌─────────────────┐
│ AI Agent │
│ (Claude + RAG) │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│ Notion│ │ Google│
│ MCP │ │ MCP │
│ Server│ │ Server│
└───────┘ └───────┘
│ │
▼ ▼
Notion API Google Drive API
实现要点:
// notion_mcp_server.ts
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "search_notion") {
const query = request.params.arguments.query;
// 1. 调用 Notion Search API
const results = await notion.search({
query,
filter: { property: "object", value: "page" },
});
// 2. 权限过滤(只返回用户有权访问的页面)
const accessibleResults = await filterByPermission(
results.results,
request.params.user.id // ← 从 MCP Client 传递用户身份
);
return {
content: [
{
type: "text",
text: JSON.stringify(accessibleResults),
},
],
};
}
});
6.2 案例 2:DevOps 自动化平台
需求:
- 开发者用自然语言管理 AWS 资源
- "帮我创建一个 S3 bucket 用于存储用户上传"
- "查看过去 24 小时的 ALB 访问日志"
架构:
Developer Command (Slack/Web UI)
│
▼
┌─────────────────────┐
│ Slack Bot / Web UI │
└────────┬────────────┘
│
▼
┌─────────────────┐
│ AI Agent │
│ (LangGraph) │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│ AWS │ │ AWS │
│ MCP │ │ MCP │
│(S3) │ │(ALB) │
└───────┘ └───────┘
安全设计:
// aws_mcp_server.ts
// 1. 工具注解:声明危险操作
{
name: "s3_create_bucket",
description: "Create an S3 bucket",
annotations: {
requiresAuthorization: true,
dangerLevel: "medium", // ← 自定义字段
affectsResources: ["s3:bucket"],
}
}
// 2. 操作前审批
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const tool = getTool(request.params.name);
if (tool.annotations.requiresAuthorization) {
// 发送审批请求到 Slack
const approved = await sendApprovalRequest({
user: request.params.user,
tool: tool.name,
args: request.params.arguments,
dangerLevel: tool.annotations.dangerLevel,
});
if (!approved) {
throw new Error("Operation requires approval");
}
}
// 执行操作
return await executeAWSTool(request.params);
});
第七部分:MCP 生态与未来展望
7.1 现有 MCP Server 生态
| 官方 Server | 功能 | 使用场景 |
|---|---|---|
@modelcontextprotocol/server-filesystem | 文件系统访问 | 读写本地文件 |
@modelcontextprotocol/server-postgres | PostgreSQL 查询 | 数据库操作 |
@modelcontextprotocol/server-google-drive | Google Drive 集成 | 文档管理 |
@modelcontextprotocol/server-slack | Slack 消息发送 | 团队协作 |
@modelcontextprotocol/server-github | GitHub API 操作 | 代码仓库管理 |
| 社区 Server | 功能 | Stars |
|---|---|---|
mcp-server-sqlite | SQLite 查询 | 1.2k |
mcp-server-redis | Redis 操作 | 800 |
mcp-server-stripe | Stripe API | 600 |
mcp-server-weather | 天气查询 | 400 |
7.2 MCP vs 其他协议
7.2.1 MCP vs OpenAI Function Calling
| 维度 | MCP | OpenAI Function Calling |
|---|---|---|
| 标准化 | ✅ 开放标准(Apache 2.0) | ❌ 厂商私有 |
| 多模型支持 | ✅ 任何 LLM | ❌ 仅 OpenAI |
| 工具发现 | ✅ 动态发现 | ❌ 手动注册 |
| 资源订阅 | ✅ 支持 | ❌ 不支持 |
| 生态 | 🔶 成长中 | ✅ 成熟 |
7.2.2 MCP vs LangChain Tools
| 维度 | MCP | LangChain Tools |
|---|---|---|
| 框架锁定 | ✅ 无锁定 | ❌ 绑定 LangChain |
| 跨语言支持 | ✅ 任何语言 | ❌ 主要是 Python/JS |
| 独立部署 | ✅ 可以独立运行 | ❌ 必须嵌入应用 |
| 性能 | ✅ 进程隔离 | ❌ 同进程调用 |
7.3 未来路线图
根据 Anthropic 的官方路线图,MCP 将在以下方向演进:
1. 认证标准化(2026 Q2)
// 即将支持的标准认证流程
{
"method": "initialize",
"params": {
"auth": {
"type": "oauth2",
"token": "...", // ← 标准 OAuth2 集成
}
}
}
2. 多模态支持(2026 Q3)
// 即将支持图片、音频等资源
{
"uri": "image://screenshot.png",
"mimeType": "image/png",
"data": "base64...", // ← 二进制数据传输
}
3. 分布式 MCP(2026 Q4)
多个 MCP Server 可以组成集群,
Client 可以并行调用多个 Server,
结果自动聚合。
总结:MCP 的颠覆性意义
MCP 的出现,标志着 AI 应用开发从"手工作坊"时代进入"工业化"时代。
核心价值回顾
- 标准化:一次编写,到处运行
- 可组合性:像 Unix 管道一样组合工具
- 安全性:进程隔离 + 显式授权
- 性能:连接池 + 流式传输 + 预处理语句
适用场景
✅ 适合用 MCP:
- 需要集成多个外部工具/数据源
- 工具需要被多个 AI 应用复用
- 对安全性有较高要求
- 需要跨语言、跨平台支持
❌ 不适合用 MCP:
- 简单的、一次性的工具集成
- 对延迟极度敏感的场景(高频交易等)
- 工具逻辑非常简单(直接写 Function Calling 更快)
行动建议
如果你想开始使用 MCP:
- 今天:试用官方 MCP Server(Filesystem、Postgres)
- 本周:为自己最常用的工具写一个 MCP Server
- 本月:将现有 AI 应用的工具迁移到 MCP
- 今年:参与 MCP 社区,贡献你的 Server
参考资源
- 官方文档: https://modelcontextprotocol.io
- 官方 SDK:
- TypeScript:
@modelcontextprotocol/sdk - Python:
mcp(PyPI) - Go:
github.com/modelcontextprotocol/go-sdk
- TypeScript:
- MCP Inspector:
npx @modelcontextprotocol/inspector - Awesome MCP Servers: https://github.com/punkpeye/awesome-mcp-servers
写在最后
MCP 不是第一个试图统一 AI 工具接口的协议,但它是第一个真正有可能成功的。
为什么?因为:
- 时机对:AI Agent 刚刚爆发,工具集成痛点足够痛
- 设计好:简单、灵活、安全,没有过度设计
- 生态强:Anthropic 背书 + 社区快速成长
如果你还在为"每个工具都要写一套适配代码"而痛苦,现在就是尝试 MCP 的最佳时机。
"Standardization is the foundation of scalability."
— 标准化是可扩展性的基石。
Happy coding! 🚀