项目基于https://github.com/firekula/mcp-bridge 进行讲述运行流程


. 总览:从配置到可用的完整时间线

1
2
3
4
5
6
7
8
时间线 ──────────────────────────────────────────────────────────────────────▶

┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 读取 │ │ 拉起 │ │ MCP │ │ 工具 │ │ 等待 │ │ 调用 │
│ mcpServers│→│ 子进程 │→ │ 握手 │→ │ 注入 │→ │ 用户 │→ │ 工具 │
│ 配置 │ │ │ │ │ │ 模型 │ │ 提问 │ │ │
└─────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
阶段一 阶段二 阶段三 阶段四 阶段五 阶段五

每个阶段的核心问题:

阶段 核心问题 答案
从哪里读配置? 各客户端有固定的配置文件路径
怎么拉起 MCP Server? 执行配置中的 command + args
怎么知道 Server 可用? 通过 initialize + tools/list 握手
模型怎么知道有工具? 客户端把工具列表注入模型上下文
模型怎么决定调不调用? 根据用户意图 + 工具描述匹配
会话结束后呢? 客户端杀死子进程

2. 阶段一:AI 客户端读取 mcpServers 配置

2.1 配置文件在哪里

当 AI 客户端(如 Claude Code、Cursor、Cline 等)启动时,它会去固定路径读取配置文件。

主流客户端配置路径:

客户端 macOS 路径 Windows 路径
Claude Code ~/.claude.json %USERPROFILE%\.claude.json
Claude Desktop ~/Library/Application Support/Claude/claude_desktop_config.json %APPDATA%\Claude\claude_desktop_config.json
Cursor ~/.cursor/mcp.json %USERPROFILE%\.cursor\mcp.json
Cline (VSCode) ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json %APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json
Roo Code ~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json 同上结构
Trae ~/.trae/mcp.json %USERPROFILE%\.trae\mcp.json
Trae CN ~/.trae-cn/mcp.json %USERPROFILE%\.trae-cn\mcp.json
Windsurf ~/.codeium/windsurf/mcp_config.json %USERPROFILE%\.codeium\windsurf\mcp_config.json
Gemini CLI ~/.gemini/mcp.json %USERPROFILE%\.gemini\mcp.json
GitHub Copilot CLI ~/.config/github-copilot/mcp.json %USERPROFILE%\.config\github-copilot\mcp.json
VSCode GitHub Copilot ~/Library/Application Support/Code/User/globalStorage/github.copilot/mcp.json 同上结构
Kiro ~/.kiro/mcp.json %USERPROFILE%\.kiro\mcp.json
Zed ~/.config/zed/mcp.json %USERPROFILE%\.config\zed\mcp.json
Cherry Studio ~/Library/Application Support/cherry-studio/mcp.json %APPDATA%\cherry-studio\mcp.json
Codex ~/.codex/mcp.json %USERPROFILE%\.codex\mcp.json
OpenCode ~/.opencode/mcp.json %USERPROFILE%\.opencode\mcp.json
Qwen Code ~/.qwen/mcp.json %USERPROFILE%\.qwen\mcp.json
Kilo Code ~/.kilo/mcp.json %USERPROFILE%\.kilo\mcp.json
CodeBuddy CLI ~/.codebuddy/mcp.json %USERPROFILE%\.codebuddy\mcp.json

2.2 配置内容解析

配置文件的标准格式:

1
2
3
4
5
6
7
8
{
"mcpServers": {
"mcp-bridge": {
"command": "node",
"args": ["/Users/yourname/project/packages/mcp-bridge-main/dist/mcp-proxy.js"]
}
}
}

各字段含义:

1
2
3
4
5
6
7
8
9
10
{
"mcpServers": { // ← 固定的顶层键名,所有 MCP Server 都写在这里
"mcp-bridge": { // ← Server 标识名(自定义,用于显示和管理)
"command": "node", // ← 启动命令(可执行程序的名称或路径)
"args": [ // ← 命令参数数组
"/absolute/path/to/dist/mcp-proxy.js" // ← mcp-proxy.js 的绝对路径
]
}
}
}

本质上等价于在终端执行:

1
node /absolute/path/to/dist/mcp-proxy.js

只不过这个命令不是你手动执行,而是 AI 客户端在启动时自动执行。

2.3 多个 MCP Server 共存

一个配置文件中可以同时配置多个 MCP Server:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"mcpServers": {
"mcp-bridge": {
"command": "node",
"args": ["/path/to/mcp-bridge/dist/mcp-proxy.js"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/yourname/Documents"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
}
}
}

AI 客户端会逐个启动所有配置的 Server,每个 Server 独立握手,工具列表合并后一起暴露给模型。


3. 阶段二:拉起子进程

3.1 子进程是怎么启动的

AI 客户端读取到配置后,使用类似 Node.js child_process.spawn() 的方式启动子进程:

1
2
3
4
5
6
7
8
9
10
11
12
AI 客户端内部伪代码:

for (const [name, config] of Object.entries(mcpServers)) {
const child = spawn(config.command, config.args, {
stdio: ['pipe', 'pipe', 'pipe']
// stdin stdout stderr
});

// child.stdin → 客户端可以往里写数据(发请求)
// child.stdout → 客户端可以从里读数据(收响应)
// child.stderr → 调试日志,客户端通常忽略或记录
}

关键点: stdio: ['pipe', 'pipe', 'pipe'] 意味着父进程(AI 客户端)和子进程(mcp-proxy.js)之间通过管道直连。

3.2 进程的父子关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────┐
│ AI 客户端进程(父进程) │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ MCP Manager │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Server A │ │ Server B │ │ │
│ │ │ stdin ──▶│ │ stdin ──▶ │ │ │
│ │ │ ◀── stdout│ │ ◀── stdout │ │ │
│ │ └──────────┘ └──────────────┘ │ │
│ │ node mcp-proxy node other-server │ │
│ └─────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ AI 模型 │ │
│ │ 可以使用 Server A + B 的所有工具 │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
  • 子进程的生命周期完全由父进程管理
  • 父进程退出时,子进程也会被终止
  • 每个 MCP Server 是独立的子进程,互不影响

3.3 启动失败的情况

失败原因 表现 排查方法
command 不存在 进程无法启动 确认 node 在 PATH 中
args 路径错误 进程启动后立刻崩溃 检查 mcp-proxy.js 路径是否正确
权限不足 进程无法启动 检查文件执行权限
依赖缺失 进程启动后报错退出 先执行 npm install && npm run build
端口已被非 Cocos 占用 进程启动成功但无法连接 Cocos 检查 3456-3466 端口占用

4. 阶段三:MCP 协议握手

子进程成功启动后,AI 客户端和 MCP Server 之间开始 JSON-RPC 握手。

4.1 initialize 初始化

AI 客户端发送(写入子进程 stdin):

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "claude-code",
"version": "1.0.0"
}
}
}

mcp-proxy.js 回复(写入 stdout):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "cocos-bridge",
"version": "1.0.0"
}
}
}

这一步的含义:

  • 客户端问:你是 MCP Server 吗?你支持什么?
  • 服务端答:是的,我支持 tools 能力,协议版本是 2024-11-05

对应源码位置: src/mcp-proxy.ts 第 82-93 行

1
2
3
4
5
6
7
8
9
10
11
12
if (method === "initialize") {
sendToAI({
jsonrpc: "2.0",
id: id,
result: {
protocolVersion: "2024-11-05",
capabilities: { tools: {} },
serverInfo: { name: "cocos-bridge", version: "1.0.0" },
},
});
return;
}

4.2 tools/list 获取工具列表

AI 客户端发送:

1
2
3
4
5
6
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}

mcp-proxy.js 的处理流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
收到 tools/list

├─ 1. 扫描本机 3456-3466 端口
│ 对每个端口发 GET /mcp-status
│ 收集所有在线 Cocos 实例信息

├─ 2. 选择目标实例端口
│ 已绑定 → 用绑定的端口
│ 未绑定且只有一个实例 → 用那个实例
│ 未绑定且没有实例 → 只返回管理工具

├─ 3. 向目标 Cocos 实例请求内部工具列表
│ GET http://127.0.0.1:{port}/list-tools

├─ 4. 拼接管理工具
│ get_active_instances + set_active_instance

└─ 5. 返回完整工具列表给 AI 客户端

返回示例(简化):

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
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "get_scene_hierarchy",
"description": "获取当前场景的节点树结构",
"inputSchema": {
"type": "object",
"properties": {
"depth": { "type": "number", "description": "遍历深度,默认 2" }
}
}
},
{
"name": "create_node",
"description": "在当前场景中创建一个新节点",
"inputSchema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"type": { "type": "string", "enum": ["empty", "sprite", "label", "button"] }
},
"required": ["name"]
}
},
{
"name": "get_active_instances",
"description": "Scan local ports (3456-3466) to find all running Cocos Creator instances",
"inputSchema": { "type": "object", "properties": {} }
},
{
"name": "set_active_instance",
"description": "Manually bind the MCP client to a specific Cocos Creator instance's port",
"inputSchema": {
"type": "object",
"properties": { "port": { "type": "number" } },
"required": ["port"]
}
}
]
}
}

对应源码位置: src/mcp-proxy.ts 第 96-129 行

4.3 握手完成标志

当 AI 客户端成功收到 initializetools/list 的响应后:

1
2
3
✅ MCP Server "mcp-bridge" 握手成功
✅ 发现 N 个可用工具
✅ 标记为"已连接"状态

如果任一步骤失败(超时、进程崩溃、JSON 格式错误):

1
2
❌ MCP Server "mcp-bridge" 连接失败
❌ 标记为"不可用"状态

5. 阶段四:工具注入模型上下文

5.1 模型看到了什么

握手完成后,AI 客户端把工具列表转化为模型能理解的格式,注入到模型的系统提示(system prompt)或工具定义中。

模型接收到的信息(概念性表示):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
你现在可以使用以下工具:

工具 1: get_scene_hierarchy
描述: 获取当前场景的节点树结构
参数:
- depth (number, 可选): 遍历深度,默认 2
- nodeId (string, 可选): 指定根节点 UUID
- includeDetails (boolean, 可选): 是否包含详情

工具 2: create_node
描述: 在当前场景中创建一个新节点
参数:
- name (string, 必需): 节点名称
- type (string, 可选): empty/sprite/label/button
- parentId (string, 可选): 父节点 UUID

工具 3: update_node_transform
描述: 修改节点的坐标、缩放、颜色或显隐状态
参数:
- id (string, 必需): 节点 UUID
- x, y, width, height, scaleX, scaleY... (可选)

... 更多工具 ...

5.2 description 的重要性

工具的 description 字段是模型决定是否调用工具的最关键依据

本项目在 description 中嵌入了 AI 安全守则:

1
2
3
4
5
【AI 安全守则】:
1. 执行任何写操作前必须先通过 get_scene_hierarchy 验证主体存在
2. 严禁基于假设盲目猜测属性名
3. 资源属性必须通过 UUID 进行赋值
4. 严禁频繁刷新全局资源

这些规则会影响模型的行为,比如模型在创建节点前会先调用 get_scene_hierarchy 确认父节点存在。


6. 阶段五:用户提问与工具调用

6.1 模型如何判断是否调用工具

模型的决策过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
用户输入


模型理解用户意图

├─ 是否需要外部信息或操作?
│ │
│ ├─ 不需要(知识问答、闲聊等)
│ │ → 直接回答,不调用任何工具
│ │
│ └─ 需要(读取场景、创建节点等)
│ │
│ ▼
│ 扫描可用工具列表
│ │
│ ├─ 有匹配的工具
│ │ → 构造参数,发起工具调用
│ │
│ └─ 没有匹配的工具
│ → 告诉用户"我无法执行这个操作"


生成响应

具体示例:

用户说的话 模型判断 匹配工具 原因
“MCP 是什么?” 不调用 通用知识,直接回答
“看看场景里有哪些节点” 调用 get_scene_hierarchy 需要读取 Cocos 实时状态
“创建一个叫 Player 的精灵节点” 调用 create_node 需要操作 Cocos 编辑器
“把 Canvas 移到 (100, 200)” 先调用 get_scene_hierarchy 再调用 update_node_transform 两个工具 先确认节点存在,再修改
“帮我写一个 TypeScript 工具函数” 不调用 MCP 可能用文件工具 不涉及 Cocos 操作
“保存当前场景” 调用 save_scene 需要操作编辑器

6.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
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
用户: "在 Canvas 下创建一个叫 StartButton 的按钮"

模型决策: 需要调用 create_node

┌─ AI 客户端 ──────────────────────────────────────────────────┐
│ │
│ 模型输出: │
│ { │
│ "tool": "create_node", │
│ "arguments": { │
│ "name": "StartButton", │
│ "type": "button", │
│ "parentId": "xxxx-xxxx-xxxx" │
│ } │
│ } │
│ │
│ 客户端将其转为 MCP JSON-RPC: │
│ {"jsonrpc":"2.0","id":5,"method":"tools/call", │
│ "params":{"name":"create_node", │
│ "arguments":{"name":"StartButton", │
│ "type":"button", │
│ "parentId":"xxxx"}}} │
│ │
│ 写入 mcp-proxy.js 的 stdin ──────────────────────────────▶ │
└──────────────────────────────────────────────────────────────┘

│ stdin 管道

┌─ mcp-proxy.js ───────────────────────────────────────────────┐
│ │
│ process.stdin.on("data") 收到 JSON │
│ handleRequest() 识别 method === "tools/call" │
│ │
│ 发起 HTTP 请求: │
│ POST http://127.0.0.1:3456/call-tool │
│ Body: {"name":"create_node", │
│ "arguments":{"name":"StartButton", │
│ "type":"button", │
│ "parentId":"xxxx"}} │
│ │
└──────────────────────────┬───────────────────────────────────┘
│ HTTP

┌─ Cocos 插件主进程 ────────────────────────────────────────────┐
│ │
│ McpRouter.handleRequest() │
│ → url === "/call-tool" │
│ → CommandQueue.enqueue() │
│ → ToolDispatcher.handleMcpCall("create_node", args, cb) │
│ → Editor.Scene.callSceneScript("mcp-bridge", │
│ "create-node", args, callback) │
│ │
└──────────────────────────┬───────────────────────────────────┘
│ IPC (Editor.Scene.callSceneScript)

┌─ Cocos 场景进程 ──────────────────────────────────────────────┐
│ │
│ scene-script.ts ["create-node"] │
│ │
│ const node = new cc.Node("StartButton"); │
│ node.parent = findNode("xxxx"); // 挂到 Canvas 下 │
│ // 添加 Button 组件、设置尺寸等 │
│ │
│ Editor.Ipc.sendToMain("scene:dirty"); // 标记需保存 │
│ Editor.Ipc.sendToAll("scene:node-changed"); // 刷新面板 │
│ │
│ event.reply(null, node.uuid); // 返回新节点 UUID │
│ │
└──────────────────────────┬───────────────────────────────────┘

▼ 逐层返回
scene-script
→ 主进程 callback
→ HTTP 200 JSON
→ mcp-proxy.js
→ stdout 写出 MCP JSON-RPC
→ AI 客户端接收
→ 模型拿到结果
→ 组织回答给用户

用户看到: "已在 Canvas 下创建按钮节点 StartButton (UUID: xxxx)"

6.3 工具结果如何返回给用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mcp-proxy.js 写入 stdout:

{
"jsonrpc": "2.0",
"id": 5,
"result": {
"content": [
{
"type": "text",
"text": "{\"uuid\":\"xxxx-xxxx\",\"name\":\"StartButton\"}"
}
]
}
}

AI 客户端收到后交给模型。模型根据工具返回的原始数据,组织成自然语言回复给用户。


7. 阶段六:会话结束与进程退出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
用户关闭 AI 客户端 / 结束会话


AI 客户端向子进程发送终止信号

├─ 方式 1: 关闭 stdin 管道 → 子进程检测到 stdin 关闭后自行退出
├─ 方式 2: 发送 SIGTERM 信号 → 子进程收到信号后退出
└─ 方式 3: 发送 SIGKILL 信号 → 强制杀死子进程


mcp-proxy.js 进程退出


注意: Cocos Creator 插件的 HTTP 服务不受影响
它仍然在运行,等待下次 AI 客户端连接

重要区别:

组件 生命周期
mcp-proxy.js 随 AI 客户端启停,每次会话重新启动
Cocos 插件 HTTP 服务 随 Cocos Creator 编辑器启停,持续运行

8. 故障排查指南

8.1 MCP Server 显示不可用

症状: AI 客户端中 mcp-bridge 显示为断开/不可用状态。

排查步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 检查配置文件路径是否正确
→ 打开对应客户端的配置文件,确认 mcpServers.mcp-bridge 存在

2. 检查 mcp-proxy.js 是否存在
→ ls /你的路径/dist/mcp-proxy.js
→ 如果不存在,执行 npm run build

3. 手动测试子进程能否启动
→ 在终端执行: node /你的路径/dist/mcp-proxy.js
→ 输入: {"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}
→ 应该看到 JSON 响应输出

4. 检查 Node.js 版本
→ node --version
→ 需要 Node.js 14+

8.2 工具列表为空

症状: MCP Server 已连接,但没有任何 Cocos 相关工具。

排查步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. 检查 Cocos Creator 是否已打开
→ 必须先打开 Cocos Creator 编辑器

2. 检查 MCP 服务是否已启动
→ 在 Cocos Creator 菜单: MCP 桥接器 → 开启MCP设置面板
→ 确认服务状态为"运行中"

3. 检查端口是否可达
→ 在终端执行: curl http://127.0.0.1:3456/mcp-status
→ 应该返回项目信息 JSON

4. 如果端口不是 3456
→ 查看 MCP 设置面板中显示的实际端口
→ 或者 curl 尝试 3457、3458 等

8.3 工具调用超时

症状: 调用工具后长时间无响应,最终返回超时错误。

可能原因:

原因 解决方案
Cocos 编辑器正在执行耗时操作 等待编辑器空闲后重试
场景脚本未加载 重新打开场景或重启插件
指令队列堆积 等待队列消化,或重启 MCP 服务
编辑器正在切换场景 等待场景加载完成

8.4 多实例冲突

症状: 打开多个 Cocos 项目后,MCP 操作了错误的项目。

解决方案:

1
2
3
4
5
6
7
1. 调用 get_active_instances 查看所有在线实例
→ 返回每个实例的端口和项目路径

2. 调用 set_active_instance 绑定目标实例
→ 参数: {"port": 3457} (填你要操作的项目端口)

3. 之后的所有操作都会发到绑定的端口