Model Context Protocol: 工具协议的技术本质与架构价值
协议诞生的技术背景
当前 AI 工具的集成方式存在一个根本性问题:每个工具都在重复实现相同的功能。
以"读取 Notion 数据库"为例,实现路径:
Clawdbot → 实现 Notion Tool
Claude Code → 实现 Notion Extension
Cursor → 实现 Notion Plugin
Windsurf → 实现 Notion Integration
每个团队都要:
- 研究 Notion API 文档
- 处理认证(OAuth / API Token)
- 实现数据查询、创建、更新逻辑
- 处理错误和重试
- 写测试用例
这是 N × M 的复杂度(N 个 AI 工具 × M 个外部服务)。
Model Context Protocol (MCP) 试图将其降低到 N + M。
协议的核心抽象
工具即服务
MCP 的核心思想:把工具变成一个独立的"服务",通过标准协议对外提供能力。
传统模式:
AI Tool ──┬── Notion API (代码耦合)
├── GitHub API
└── Gmail API
MCP 模式:
AI Tool ── MCP Client ──┬── MCP Server (Notion)
├── MCP Server (GitHub)
└── MCP Server (Gmail)
AI 工具只需要实现一次 MCP Client,就能连接所有 MCP Server。
协议规范
MCP 基于 JSON-RPC 2.0,通过 stdio 或 HTTP 通信。
Server 暴露的接口
// MCP Server 必须实现的方法
interface MCPServer {
// 列出可用工具
tools/list(): Tool[];
// 调用工具
tools/call(name: string, arguments: object): any;
// 列出可用资源(可选)
resources/list(): Resource[];
// 读取资源
resources/read(uri: string): string;
}
Tool 的数据结构
{
"name": "notion_create_page",
"description": "创建一个新的 Notion 页面",
"inputSchema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"content": {"type": "string"}
},
"required": ["title"]
}
}
AI 通过 inputSchema 知道这个工具需要什么参数,可以自动生成调用代码。
技术架构剖析
Client 端实现
export class MCPClient {
private transports: Map<string, Transport> = new Map();
// 连接到一个 MCP Server
async connect(serverConfig: ServerConfig): Promise<void> {
const transport = serverConfig.type === 'stdio'
? new StdioTransport(serverConfig.command)
: new HTTPTransport(serverConfig.url);
await transport.connect();
this.transports.set(serverConfig.name, transport);
}
// 发现所有可用工具
async listTools(serverName: string): Promise<Tool[]> {
const transport = this.transports.get(serverName);
const response = await transport.request({
jsonrpc: '2.0',
method: 'tools/list',
id: generateId()
});
return response.result;
}
// 调用工具
async callTool(
serverName: string,
toolName: string,
args: object
): Promise<any> {
const transport = this.transports.get(serverName);
const response = await transport.request({
jsonrpc: '2.0',
method: 'tools/call',
params: { name: toolName, arguments: args },
id: generateId()
});
return response.result;
}
}
Server 端实现
export abstract class MCPServer {
protected tools: Map<string, ToolHandler> = new Map();
// 子类注册工具
protected registerTool(
name: string,
schema: JSONSchema,
handler: ToolHandler
): void {
this.tools.set(name, { schema, handler });
}
// 处理客户端请求
async handleRequest(request: JSONRPCRequest): Promise<JSONRPCResponse> {
switch (request.method) {
case 'tools/list':
return {
jsonrpc: '2.0',
id: request.id,
result: Array.from(this.tools.entries()).map(([name, tool]) => ({
name,
description: tool.schema.description,
inputSchema: tool.schema
}))
};
case 'tools/call':
const { name, arguments: args } = request.params;
const tool = this.tools.get(name);
if (!tool) {
return {
jsonrpc: '2.0',
id: request.id,
error: { code: -32601, message: 'Tool not found' }
};
}
const result = await tool.handler(args);
return {
jsonrpc: '2.0',
id: request.id,
result
};
default:
return {
jsonrpc: '2.0',
id: request.id,
error: { code: -32601, message: 'Method not found' }
};
}
}
}
示例:Notion MCP Server
export class NotionMCPServer extends MCPServer {
private notionClient: NotionClient;
constructor(apiKey: string) {
super();
this.notionClient = new NotionClient({ auth: apiKey });
// 注册工具
this.registerTool('create_page', {
type: 'object',
properties: {
title: { type: 'string' },
content: { type: 'string' }
},
required: ['title']
}, this.createPage.bind(this));
this.registerTool('search_pages', {
type: 'object',
properties: {
query: { type: 'string' }
},
required: ['query']
}, this.searchPages.bind(this));
}
private async createPage(args: any): Promise<any> {
const page = await this.notionClient.pages.create({
parent: { database_id: process.env.NOTION_DB_ID },
properties: {
title: { title: [{ text: { content: args.title } }] }
},
children: [{
object: 'block',
type: 'paragraph',
paragraph: { rich_text: [{ text: { content: args.content } }] }
}]
});
return { pageId: page.id, url: page.url };
}
private async searchPages(args: any): Promise<any> {
const response = await this.notionClient.search({
query: args.query,
filter: { property: 'object', value: 'page' }
});
return response.results.map(page => ({
id: page.id,
title: page.properties.title?.title[0]?.plain_text || 'Untitled'
}));
}
}
// 启动 Server
const server = new NotionMCPServer(process.env.NOTION_API_KEY);
const transport = new StdioTransport();
transport.listen(server.handleRequest.bind(server));
与传统 API 的对比
REST API
// Clawdbot 直接调用 Notion API
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json'
},
body: JSON.stringify({
parent: { database_id: dbId },
properties: { ... }
})
});
问题:
- Clawdbot 必须硬编码 Notion API 的调用逻辑
- 需要处理 Notion 特有的认证方式
- Notion API 更新时,Clawdbot 必须跟着更新
MCP
// Clawdbot 通过 MCP 调用
const result = await mcpClient.callTool('notion', 'create_page', {
title: '新页面',
content: '内容'
});
优势:
- Clawdbot 不需要知道 Notion API 细节
- MCP Server 维护者负责适配 Notion API 变更
- 同样的代码可以调用任何 MCP Server
技术优势分析
跨语言支持
MCP Server 可以用任何语言实现:
# Python 实现的 MCP Server
from mcp_server import MCPServer
class WeatherServer(MCPServer):
def register_tools(self):
self.add_tool('get_weather', {
'city': {'type': 'string'}
}, self.get_weather)
def get_weather(self, args):
city = args['city']
# 调用天气 API
return {'temperature': 25, 'condition': 'sunny'}
server = WeatherServer()
server.listen() # 监听 stdio
Clawdbot (Node.js) 可以通过 child_process 启动这个 Python 服务,通过 stdin/stdout 通信。
动态工具发现
Clawdbot 启动时,可以自动发现所有可用工具:
async function discoverTools(mcpServers: ServerConfig[]) {
const allTools = [];
for (const server of mcpServers) {
await mcpClient.connect(server);
const tools = await mcpClient.listTools(server.name);
allTools.push(...tools.map(t => ({
...t,
serverName: server.name
})));
}
return allTools;
}
AI Agent 在运行时动态获取工具列表,不需要重启就能使用新安装的 MCP Server。
权限隔离
每个 MCP Server 是独立进程,有自己的权限边界:
// MCP Server 运行在沙盒中
const server = spawn('mcp-server-notion', {
env: {
NOTION_API_KEY: '...', // 只给需要的环境变量
PATH: '/usr/bin' // 限制可执行路径
},
cwd: '/var/mcp/notion', // 限制工作目录
stdio: ['pipe', 'pipe', 'pipe']
});
如果 MCP Server 被攻击,也只能影响自己的进程,无法波及主程序。
技术挑战
性能开销
每次工具调用的路径:
AI Agent
→ MCP Client
→ IPC (stdin/stdout)
→ MCP Server
→ 外部 API
相比直接调用 API,增加了两层间接调用和一次进程间通信。
实测数据(调用 Notion API):
直接调用: ~150ms
通过 MCP: ~180ms (+20%)
对于大部分场景可接受,但对延迟敏感的应用(比如实时对话)需要考虑。
错误传播
MCP Server 内部错误需要正确传播到 Client:
// Server 端
try {
const result = await someAPI();
return { success: true, data: result };
} catch (error) {
return {
jsonrpc: '2.0',
error: {
code: -32000,
message: error.message,
data: {
stack: error.stack,
type: error.name
}
}
};
}
Client 需要解析这些错误,转换成用户友好的提示。
版本兼容性
MCP 协议本身在演进,可能出现:
Clawdbot MCP Client v1.0
↓ 连接
MCP Server v2.0 (使用新协议)
↓ 不兼容
解决方案:协议版本协商
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": { "listChanged": true },
"sampling": {}
}
}
}
Server 返回自己支持的协议版本,Client 适配。
Clawdbot 的集成路径
配置文件
{
"mcpServers": {
"notion": {
"command": "node",
"args": ["./mcp-servers/notion/index.js"],
"env": {
"NOTION_API_KEY": "${NOTION_API_KEY}"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
}
}
}
启动流程
export class ClawdbotMCPIntegration {
private mcpClient: MCPClient;
private toolRegistry: Map<string, MCPTool> = new Map();
async initialize(config: MCPConfig): Promise<void> {
this.mcpClient = new MCPClient();
// 连接所有配置的 MCP Server
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
await this.mcpClient.connect({
name,
type: 'stdio',
command: serverConfig.command,
args: serverConfig.args,
env: serverConfig.env
});
// 发现工具
const tools = await this.mcpClient.listTools(name);
for (const tool of tools) {
this.toolRegistry.set(tool.name, {
serverName: name,
schema: tool.inputSchema,
description: tool.description
});
}
}
}
// Agent 调用工具时的入口
async executeTool(toolName: string, args: object): Promise<any> {
const tool = this.toolRegistry.get(toolName);
if (!tool) {
throw new Error(`Tool not found: ${toolName}`);
}
return await this.mcpClient.callTool(
tool.serverName,
toolName,
args
);
}
}
结论
MCP 不是一个新概念(RPC、插件系统早就存在),但它在 AI 工具场景下提供了恰当的抽象层次:
过于底层的抽象(如 HTTP API):
- 需要每个 AI 工具实现完整的集成逻辑
- 无法动态发现能力
过于高层的抽象(如通用插件系统):
- 协议过于复杂
- 对 AI 场景的优化不足
MCP 找到了中间点:
- 协议足够简单(JSON-RPC)
- 抽象足够高层(工具 + 资源)
- 针对 LLM 场景优化(inputSchema 可直接用于 function calling)
随着更多 AI 工具支持 MCP,一个"工具生态系统"正在形成。开发者不再需要为每个 AI 工具单独实现集成,只需要写一次 MCP Server,就能被所有客户端使用。
这是协议标准化的价值。
技术参考:
- MCP 规范: https://modelcontextprotocol.io/docs/specification
- JSON-RPC 2.0: https://www.jsonrpc.org/specification
- MCP Server 示例: https://github.com/modelcontextprotocol/servers