0%

Claude Code 源码解析 (6):MCP 协议集成的完整指南

Claude Code 源码解析 (6):MCP 协议集成的完整指南

导读: 这是 Claude Code 20 个功能特性源码解析系列的第 6 篇,深入分析 MCP (Model Context Protocol) 集成的架构设计。


📋 目录

  1. 问题引入:为什么需要 MCP?
  2. [技术原理:MCP 协议核心架构](#技术原理 mcp-协议核心架构)
  3. 设计思想:为什么这样设计
  4. 解决方案:完整实现详解
  5. OpenClaw 最佳实践
  6. 总结

问题引入:为什么需要 MCP?

痛点场景

场景 1:工具孤岛

1
2
3
4
5
6
7
8
9
10
11
12
AI 助手需要访问:
- GitHub API (代码仓库)
- Slack API (团队沟通)
- Jira API (任务管理)
- 内部数据库 (业务数据)

每个 API 都要:
1. 编写专用工具
2. 处理认证逻辑
3. 维护 SDK 更新

→ 100 个 API = 100 个专用工具

场景 2:重复造轮子

1
2
3
4
5
开发者 A:写了 GitHub 工具
开发者 B:写了 Slack 工具
开发者 C:又写了一遍 GitHub 工具

→ 没有共享机制,重复劳动

场景 3:安全风险

1
2
3
4
5
6
每个工具都要处理:
- API 密钥存储
- 权限控制
- 审计日志

→ 分散实现,安全标准不统一

核心问题

设计 AI 助手的工具扩展系统时,面临以下挑战:

  1. 标准化问题

    • 如何统一工具接口?
    • 如何让不同来源的工具协同工作?
  2. 可扩展性问题

    • 如何快速集成新工具?
    • 如何支持第三方工具?
  3. 安全性问题

    • 如何统一管理认证?
    • 如何控制工具权限?
  4. 生态问题

    • 如何促进工具共享?
    • 如何建立工具市场?

Claude Code 用 MCP (Model Context Protocol) 解决了这些问题。


技术原理:MCP 协议核心架构

什么是 MCP?

MCP (Model Context Protocol) 是一个开放协议,用于标准化 AI 模型与外部工具/数据的交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────┐
│ AI Model │
│ (Claude, GPT, ...) │
└─────────────────────┬───────────────────────────────────────┘
│ MCP Protocol

┌─────────────────────┴───────────────────────────────────────┐
│ MCP Host │
│ (Claude Code, OpenClaw, ...) │
└─────────────────────┬───────────────────────────────────────┘
│ MCP Protocol

┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Server │ │ Server │ │ Server │
│ GitHub │ │ Slack │ │ Custom │
└────────┘ └────────┘ └────────┘

MCP 架构组件

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
// MCP 核心接口
interface MCPProtocol {
// 工具调用
callTool(name: string, args: Record<string, any>): Promise<ToolResult>;

// 资源访问
readResource(uri: string): Promise<ResourceContent>;

// 提示词获取
getPrompt(name: string, args?: Record<string, any>): Promise<Prompt>;

// 订阅通知
subscribe(uri: string, callback: (update: any) => void): void;
}

// MCP 服务器定义
interface MCPServer {
name: string;
version: string;
description: string;

// 连接方式
transport: {
type: 'stdio' | 'http' | 'websocket';
command?: string; // stdio 模式
url?: string; // http/ws 模式
headers?: Record<string, string>;
};

// 能力声明
capabilities: {
tools?: boolean;
resources?: boolean;
prompts?: boolean;
subscriptions?: boolean;
};

// 认证配置
auth?: {
type: 'bearer' | 'api-key' | 'oauth';
token?: string;
};
}

连接管理

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
class MCPConnectionManager {
private connections: Map<string, MCPServerConnection> = new Map();
private configStore: MCPConfigStore;

constructor(config: MCPManagerConfig) {
this.configStore = new MCPConfigStore(config);
}

// 从配置加载服务器
async loadFromConfig(): Promise<void> {
const servers = await this.configStore.loadServers();

for (const serverConfig of servers) {
await this.connect(serverConfig);
}
}

// 连接到服务器
async connect(serverConfig: MCPServer): Promise<void> {
const connection = new MCPServerConnection(serverConfig);

try {
// 1. 建立连接
await connection.initialize();

// 2. 获取服务器能力
const capabilities = await connection.getCapabilities();

// 3. 注册工具
if (capabilities.tools) {
const tools = await connection.listTools();
for (const tool of tools) {
this.registerTool(tool, connection);
}
}

// 4. 存储连接
this.connections.set(serverConfig.name, connection);

console.log(`[MCP] Connected to: ${serverConfig.name}`);

} catch (error) {
console.error(`[MCP] Failed to connect ${serverConfig.name}:`, error);
throw error;
}
}

// 调用工具
async callTool(
serverName: string,
toolName: string,
args: Record<string, any>
): Promise<ToolResult> {
const connection = this.connections.get(serverName);

if (!connection) {
throw new Error(`Server not found: ${serverName}`);
}

return await connection.callTool(toolName, args);
}

// 断开连接
async disconnect(serverName: string): Promise<void> {
const connection = this.connections.get(serverName);

if (connection) {
await connection.close();
this.connections.delete(serverName);
}
}
}

stdio 传输实现

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
class StdioTransport implements MCPTransport {
private process: ChildProcess | null = null;
private messageQueue: MessageQueue = new MessageQueue();
private messageId = 0;

async initialize(config: MCPServerConfig): Promise<void> {
// 启动子进程
this.process = spawn(config.transport.command!, config.transport.args || [], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, ...config.transport.env },
});

// 设置消息处理
this.process.stdout.on('data', (data) => {
this.handleMessage(data.toString());
});

this.process.stderr.on('data', (data) => {
console.error(`[MCP stderr] ${data}`);
});

// 发送初始化请求
await this.sendRequest('initialize', {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: {
name: 'claude-code',
version: '2026.3.28',
},
});
}

async sendRequest(method: string, params?: any): Promise<any> {
const id = ++this.messageId;

const message = {
jsonrpc: '2.0',
id,
method,
params,
};

// 发送消息
this.process!.stdin.write(JSON.stringify(message) + '\n');

// 等待响应
return await this.messageQueue.waitForResponse(id);
}

private handleMessage(data: string): void {
const message = JSON.parse(data);

if (message.id !== undefined) {
// 响应消息
this.messageQueue.resolve(message.id, message.result);
} else if (message.method) {
// 通知/请求消息
this.handleNotification(message);
}
}
}

HTTP 传输实现

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
class HTTPTransport implements MCPTransport {
private baseUrl: string;
private headers: Record<string, string>;

constructor(config: MCPServerConfig) {
this.baseUrl = config.transport.url!;
this.headers = {
'Content-Type': 'application/json',
...config.transport.headers,
};
}

async sendRequest(method: string, params?: any): Promise<any> {
const response = await fetch(`${this.baseUrl}/rpc`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({
jsonrpc: '2.0',
id: Date.now(),
method,
params,
}),
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

const data = await response.json();

if (data.error) {
throw new Error(data.error.message);
}

return data.result;
}

async subscribe(uri: string, callback: (update: any) => void): Promise<void> {
// 使用 SSE 订阅
const eventSource = new EventSource(`${this.baseUrl}/events?uri=${uri}`);

eventSource.onmessage = (event) => {
callback(JSON.parse(event.data));
};
}
}

工具映射

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
class MCPToolMapper {
// 将 MCP 工具映射为内部工具
async mapTools(serverName: string, connection: MCPServerConnection): Promise<Tool[]> {
const mcpTools = await connection.listTools();

return mcpTools.map(mcpTool => ({
name: `${serverName}.${mcpTool.name}`,
description: mcpTool.description,
inputSchema: this.convertSchema(mcpTool.inputSchema),

execute: async (input: any, context: any) => {
try {
const result = await connection.callTool(mcpTool.name, input);
return {
success: true,
output: this.formatResult(result),
};
} catch (error) {
return {
success: false,
error: error.message,
};
}
},
}));
}

// 转换 JSON Schema
private convertSchema(mcpSchema: any): ToolInputJSONSchema {
return {
type: 'object',
properties: Object.fromEntries(
Object.entries(mcpSchema.properties || {}).map(([key, value]: [string, any]) => [
key,
{
type: value.type,
description: value.description,
enum: value.enum,
default: value.default,
},
])
),
required: mcpSchema.required || [],
};
}

// 格式化结果
private formatResult(result: any): string {
if (typeof result === 'string') {
return result;
}

if (Array.isArray(result)) {
return result.map(item => this.formatResult(item)).join('\n');
}

return JSON.stringify(result, null, 2);
}
}

资源浏览

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
class MCPResourceBrowser {
constructor(private connectionManager: MCPConnectionManager) {}

// 列出资源
async listResources(serverName: string): Promise<Resource[]> {
const connection = this.connectionManager.connections.get(serverName);

if (!connection) {
throw new Error(`Server not found: ${serverName}`);
}

return await connection.listResources();
}

// 读取资源
async readResource(serverName: string, uri: string): Promise<string> {
const connection = this.connectionManager.connections.get(serverName);

if (!connection) {
throw new Error(`Server not found: ${serverName}`);
}

return await connection.readResource(uri);
}

// 订阅资源更新
async subscribeResource(
serverName: string,
uri: string,
callback: (content: string) => void
): Promise<void> {
const connection = this.connectionManager.connections.get(serverName);

if (!connection) {
throw new Error(`Server not found: ${serverName}`);
}

await connection.subscribe(uri, (update) => {
callback(update.content);
});
}
}

interface Resource {
uri: string;
name: string;
description?: string;
mimeType?: string;
}

配置管理

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
# ~/.openclaw/config/mcp.yaml

servers:
# GitHub 服务器
- name: github
description: GitHub API 集成
transport:
type: http
url: https://mcp.github.com/rpc
auth:
type: bearer
token: ${GITHUB_TOKEN}
capabilities:
tools: true
resources: true

# Slack 服务器
- name: slack
description: Slack 团队沟通
transport:
type: http
url: https://mcp.slack.com/rpc
auth:
type: api-key
token: ${SLACK_BOT_TOKEN}

# 本地文件系统
- name: filesystem
description: 本地文件访问
transport:
type: stdio
command: npx
args:
- -y
- "@modelcontextprotocol/server-filesystem"
- /home/johnzok/workspace
capabilities:
tools: true
resources: false

# 自定义服务器
- name: custom-api
description: 内部 API 集成
transport:
type: http
url: http://internal-api.company.com/mcp
auth:
type: bearer
token: ${INTERNAL_API_TOKEN}

# 默认设置
defaults:
timeout: 30000 # 30 秒超时
retry:
maxAttempts: 3
delay: 1000

设计思想:为什么这样设计

思想 1:协议标准化

问题: 每个工具都有自己的接口,难以统一管理。

解决: MCP 协议标准化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
统一接口:
┌──────────────────────────────────────┐
│ MCP Protocol │
│ - callTool(name, args) │
│ - readResource(uri) │
│ - getPrompt(name, args) │
│ - subscribe(uri, callback) │
└──────────────────────────────────────┘
│ │
▼ ▼
┌────────┐ ┌────────┐
│ GitHub │ │ Slack │
│ Server │ │ Server │
└────────┘ └────────┘

设计智慧:

标准化接口让工具集成变得简单。

思想 2:传输层抽象

问题: 不同服务器使用不同的通信方式。

解决: 传输层抽象。

1
2
3
4
5
6
7
8
9
interface MCPTransport {
sendRequest(method: string, params?: any): Promise<any>;
subscribe(uri: string, callback: (update: any) => void): void;
}

// 实现
class StdioTransport implements MCPTransport { /* ... */ }
class HTTPTransport implements MCPTransport { /* ... */ }
class WebSocketTransport implements MCPTransport { /* ... */ }

好处:

  • 上层逻辑无需关心传输细节
  • 轻松添加新传输方式
  • 统一错误处理

思想 3:能力发现

问题: 如何知道服务器支持什么功能?

解决: 能力声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface MCPCapabilities {
tools?: {
list: boolean;
call: boolean;
};
resources?: {
list: boolean;
read: boolean;
subscribe: boolean;
};
prompts?: {
list: boolean;
get: boolean;
};
}

// 服务器初始化时声明能力
const capabilities = await connection.getCapabilities();

// 根据能力启用功能
if (capabilities.tools) {
const tools = await connection.listTools();
// 注册工具...
}

思想 4:统一认证

问题: 每个工具都要处理认证,重复且不安全。

解决: 统一认证管理。

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
class MCPAuthManager {
private credentials: Map<string, Credential> = new Map();

// 存储凭证
async storeCredential(serverName: string, credential: Credential): Promise<void> {
// 加密存储
const encrypted = await this.encrypt(credential);
this.credentials.set(serverName, encrypted);
}

// 获取凭证
async getCredential(serverName: string): Promise<Credential> {
const encrypted = this.credentials.get(serverName);
return await this.decrypt(encrypted);
}

// 自动刷新 token
async refreshCredential(serverName: string): Promise<void> {
const credential = await this.getCredential(serverName);

if (credential.type === 'oauth' && credential.expiresAt < Date.now()) {
const newToken = await this.oauthRefresh(credential.refreshToken);
await this.storeCredential(serverName, {
...credential,
token: newToken,
});
}
}
}

思想 5:错误隔离

问题: 一个服务器故障不应影响其他服务器。

解决: 错误隔离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MCPConnectionManager {
async callTool(serverName: string, toolName: string, args: any): Promise<any> {
try {
const connection = this.connections.get(serverName);
return await connection.callTool(toolName, args);
} catch (error) {
// 记录错误,但不影响其他服务器
console.error(`[MCP] ${serverName} error:`, error);

// 返回友好错误
return {
success: false,
error: `Tool unavailable: ${serverName}.${toolName}`,
serverError: error.message,
};
}
}
}

解决方案:完整实现详解

MCPTool 核心实现

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
export class MCPTool extends Tool {
name = 'mcp';
description = '调用 MCP 服务器工具';

inputSchema = {
type: 'object',
properties: {
server: {
type: 'string',
description: 'MCP 服务器名称',
},
tool: {
type: 'string',
description: '工具名称',
},
args: {
type: 'object',
description: '工具参数',
},
},
required: ['server', 'tool'],
};

private connectionManager: MCPConnectionManager;

constructor(config: MCPToolConfig) {
super();
this.connectionManager = new MCPConnectionManager(config);
}

async execute(input: MCPInput, context: ToolContext): Promise<ToolResult> {
try {
// 1. 获取连接
const connection = this.connectionManager.getConnection(input.server);

if (!connection) {
return {
success: false,
error: `Server not found: ${input.server}`,
};
}

// 2. 调用工具
const result = await connection.callTool(input.tool, input.args);

return {
success: true,
output: this.formatOutput(result),
server: input.server,
tool: input.tool,
};

} catch (error) {
return {
success: false,
error: error.message,
};
}
}

private formatOutput(result: any): string {
if (typeof result === 'string') {
return result;
}
return JSON.stringify(result, null, 2);
}
}

服务器配置加载

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
class MCPConfigLoader {
async loadConfig(configPath: string): Promise<MCPConfig> {
const content = await fs.promises.readFile(configPath, 'utf-8');
const config = yaml.parse(content);

// 处理环境变量
const processed = this.processEnvVars(config);

// 验证配置
this.validateConfig(processed);

return processed;
}

private processEnvVars(config: any): any {
// 递归处理 ${VAR} 格式
return JSON.parse(JSON.stringify(config, (key, value) => {
if (typeof value === 'string' && value.startsWith('${') && value.endsWith('}')) {
const varName = value.slice(2, -1);
return process.env[varName] || value;
}
return value;
}));
}

private validateConfig(config: any): void {
for (const server of config.servers) {
if (!server.name) {
throw new Error('Server missing name');
}

if (!server.transport) {
throw new Error(`Server ${server.name} missing transport`);
}

if (server.transport.type === 'stdio' && !server.transport.command) {
throw new Error(`Server ${server.name} missing command for stdio transport`);
}

if (server.transport.type === 'http' && !server.transport.url) {
throw new Error(`Server ${server.name} missing url for http transport`);
}
}
}
}

工具发现与注册

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
class MCPToolDiscovery {
constructor(
private connectionManager: MCPConnectionManager,
private toolRegistry: ToolRegistry
) {}

// 发现并注册所有服务器工具
async discoverAll(): Promise<void> {
for (const [name, connection] of this.connectionManager.connections) {
await this.discoverServer(name, connection);
}
}

// 发现单个服务器工具
async discoverServer(name: string, connection: MCPServerConnection): Promise<void> {
try {
// 1. 获取能力
const capabilities = await connection.getCapabilities();

if (!capabilities.tools) {
console.log(`[MCP] ${name} does not support tools`);
return;
}

// 2. 列出工具
const tools = await connection.listTools();
console.log(`[MCP] ${name}: Found ${tools.length} tools`);

// 3. 注册工具
const mapper = new MCPToolMapper();
const internalTools = await mapper.mapTools(name, connection);

for (const tool of internalTools) {
this.toolRegistry.register(tool);
console.log(`[MCP] Registered: ${tool.name}`);
}

} catch (error) {
console.error(`[MCP] Failed to discover ${name}:`, error);
}
}

// 刷新工具列表
async refresh(serverName: string): Promise<void> {
// 取消注册旧工具
const oldTools = this.toolRegistry.list().filter(t => t.name.startsWith(`${serverName}.`));
for (const tool of oldTools) {
this.toolRegistry.unregister(tool.name);
}

// 重新发现
const connection = this.connectionManager.getConnection(serverName);
if (connection) {
await this.discoverServer(serverName, connection);
}
}
}

资源订阅

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
class MCPResourceSubscription {
private subscriptions: Map<string, Subscription> = new Map();

async subscribe(
serverName: string,
uri: string,
callback: (content: string) => void
): Promise<void> {
const key = `${serverName}:${uri}`;

// 检查是否已订阅
if (this.subscriptions.has(key)) {
console.log(`[MCP] Already subscribed: ${key}`);
return;
}

const connection = this.connectionManager.getConnection(serverName);

if (!connection) {
throw new Error(`Server not found: ${serverName}`);
}

// 创建订阅
await connection.subscribe(uri, (update) => {
callback(update.content);

// 发送通知
this.notifyUpdate(key, update);
});

this.subscriptions.set(key, {
server: serverName,
uri,
createdAt: new Date(),
});

console.log(`[MCP] Subscribed: ${key}`);
}

async unsubscribe(serverName: string, uri: string): Promise<void> {
const key = `${serverName}:${uri}`;
const subscription = this.subscriptions.get(key);

if (subscription) {
const connection = this.connectionManager.getConnection(serverName);
await connection.unsubscribe(uri);
this.subscriptions.delete(key);

console.log(`[MCP] Unsubscribed: ${key}`);
}
}

listSubscriptions(): Subscription[] {
return Array.from(this.subscriptions.values());
}
}

OpenClaw 最佳实践

实践 1:配置 MCP 服务器

文件: ~/.openclaw/config/mcp.yaml

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
servers:
# 官方 MCP 服务器
- name: github
description: GitHub API
transport:
type: http
url: https://mcp.github.com/rpc
auth:
type: bearer
token: ${GITHUB_TOKEN}

# 本地文件
- name: filesystem
description: 本地文件系统
transport:
type: stdio
command: npx
args:
- -y
- "@modelcontextprotocol/server-filesystem"
- /home/johnzok/workspace

# 数据库
- name: postgres
description: PostgreSQL 数据库
transport:
type: stdio
command: npx
args:
- -y
- "@modelcontextprotocol/server-postgres"
- "postgresql://localhost:5432/mydb"

# 工具别名
tool_aliases:
gh.list_issues: github.list_issues
fs.read: filesystem.read_file
db.query: postgres.query

实践 2:使用 MCP 工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列出所有 MCP 工具
openclaw run mcp --action list

# 调用工具
openclaw run mcp \
--server github \
--tool list_issues \
--args '{"repo": "openclaw/openclaw"}'

# 读取资源
openclaw run mcp \
--server filesystem \
--action read_resource \
--uri "file:///home/johnzok/workspace/test.txt"

实践 3:创建自定义 MCP 服务器

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
// my-mcp-server/index.ts
import { MCPServer } from '@modelcontextprotocol/sdk';

const server = new MCPServer({
name: 'my-custom-server',
version: '1.0.0',
});

// 注册工具
server.registerTool('greet', {
description: '打招呼',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: '名字' },
},
required: ['name'],
},
execute: async (args) => {
return `Hello, ${args.name}!`;
},
});

// 启动服务器
server.start();

SKILL.md:

1
2
3
4
5
6
7
8
9
10
11
---
name: my-custom-server
description: 自定义 MCP 服务器
---

## 安装

```bash
cd ~/my-mcp-server
npm install
npm run build

配置

1
2
3
4
5
6
7
servers:
- name: my-custom
transport:
type: stdio
command: node
args:
- /home/johnzok/my-mcp-server/dist/index.js
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

### 实践 4:工具组合

```yaml
# ~/.openclaw/config/mcp-compositions.yaml

compositions:
# GitHub + Slack 通知
- name: github-notify
description: GitHub 事件通知 Slack
steps:
- skill: mcp
params:
server: github
tool: list_issues
args:
repo: "{{repo}}"
- skill: mcp
params:
server: slack
tool: post_message
args:
channel: "#notifications"
text: "New issues: {{issues}}"

# 数据库 + 文件导出
- name: db-export
description: 数据库查询导出 CSV
steps:
- skill: mcp
params:
server: postgres
tool: query
args:
sql: "{{query}}"
- skill: mcp
params:
server: filesystem
tool: write_file
args:
path: "{{output_path}}"
content: "{{result}}"

实践 5:监控与调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看服务器状态
openclaw run mcp --action status

# 输出:
MCP 服务器状态:

┌───────────────────────────────────────────┐
│ 服务器 │ 状态 │ 工具数 │ 延迟 │
├───────────────────────────────────────────┤
│ github │ 🟢 在线 │ 15 │ 120ms │
│ filesystem │ 🟢 在线 │ 8 │ 5ms │
│ postgres │ 🟡 慢 │ 5 │ 2500ms │
│ slack │ 🔴 离线 │ - │ timeout
└───────────────────────────────────────────┘

# 测试服务器
openclaw run mcp --action test --server github

# 查看日志
openclaw logs mcp --server github --tail 50

总结

核心要点

  1. 协议标准化 - 统一工具接口
  2. 传输层抽象 - 支持多种通信方式
  3. 能力发现 - 自动发现服务器功能
  4. 统一认证 - 集中管理凭证
  5. 错误隔离 - 单点故障不影响全局

设计智慧

MCP 让工具集成像插 USB 一样简单。

Claude Code 的 MCP 集成设计告诉我们:

  • 标准化降低集成成本
  • 抽象层提升可扩展性
  • 能力发现改善用户体验
  • 统一认证增强安全性

MCP 生态

类别 官方服务器 社区服务器
代码托管 GitHub, GitLab Bitbucket
沟通协作 Slack Teams, Discord
数据库 PostgreSQL, MySQL MongoDB, Redis
云服务 AWS, GCP Azure, Cloudflare
文件系统 Local, S3 GCS, Azure Blob

下一步

  • 配置 MCP 服务器
  • 安装官方服务器
  • 创建自定义服务器
  • 发布到 MCP 市场

系列文章:

  • [1] Bash 命令执行的安全艺术 (已发布)
  • [2] 差异编辑的设计艺术 (已发布)
  • [3] 文件搜索的底层原理 (已发布)
  • [4] 多 Agent 协作的架构设计 (已发布)
  • [5] 技能系统的设计哲学 (已发布)
  • [6] MCP 协议集成的完整指南 (本文)
  • [7] 后台任务管理的完整方案 (待发布)

上一篇: Claude Code 源码解析 (5):技能系统的设计哲学


关于作者: John,OpenClaw 平台开发者,专注 AI 助手架构设计与实现。