feat(mcp-bridge): 添加 MCP 桥接插件实现 Cocos Creator 与外部工具集成

- 新增 main.js 实现 MCP 服务器,提供 HTTP 接口供外部工具调用
- 实现 5 个核心工具接口:获取选中节点、修改节点名称、保存场景、
  获取场景层级结构、更新节点变换属性
- 添加 panel 面板用于测试 MCP 功能,包含节点 ID 获取和名称修改功能
- 实现场景脚本 scene-script.js 处理节点属性修改和层级数据导出
- 配置 package.json 定义插件入口文件和菜单项
- 支持跨域请求便于调试,返回符合 MCP 规范的工具定义格式
```
This commit is contained in:
火焰库拉
2026-01-29 13:47:38 +08:00
commit 1af6f08c94
5 changed files with 373 additions and 0 deletions

17
panel/index.html Normal file
View File

@@ -0,0 +1,17 @@
<div style="padding: 10px;">
<ui-prop name="Node ID">
<ui-input id="nodeId" placeholder="Click 'Get' to fetch ID"></ui-input>
</ui-prop>
<ui-prop name="New Name">
<ui-input id="newName" placeholder="Enter new name"></ui-input>
</ui-prop>
<div style="margin-top: 10px; display: flex; flex-direction: column; gap: 5px;">
<ui-button id="btn-get" class="green">获取选中节点 ID</ui-button>
<ui-button id="btn-set" class="blue">修改节点名称</ui-button>
</div>
<div id="log" style="margin-top: 15px; font-size: 12px; color: #66ccff; border-top: 1px solid #555; padding-top: 5px;">
Status: Ready
</div>
</div>

59
panel/index.js Normal file
View File

@@ -0,0 +1,59 @@
"use strict";
const fs = require("fs");
const path = require("path");
Editor.Panel.extend({
// 读取样式和模板
style: fs.readFileSync(Editor.url("packages://mcp-bridge/panel/index.html"), "utf-8"),
template: fs.readFileSync(Editor.url("packages://mcp-bridge/panel/index.html"), "utf-8"),
// 面板渲染成功后的回调
ready() {
// 使用 querySelector 确保能拿到元素,避免依赖可能为 undefined 的 this.$
const btnGet = this.shadowRoot.querySelector("#btn-get");
const btnSet = this.shadowRoot.querySelector("#btn-set");
const nodeIdInput = this.shadowRoot.querySelector("#nodeId");
const newNameInput = this.shadowRoot.querySelector("#newName");
const logDiv = this.shadowRoot.querySelector("#log");
if (!btnGet || !btnSet) {
Editor.error("Failed to find UI elements. Check if IDs in HTML match.");
return;
}
// 测试获取信息
btnGet.addEventListener("confirm", () => {
Editor.Ipc.sendToMain("mcp-bridge:get-selected-info", (err, ids) => {
if (ids && ids.length > 0) {
nodeIdInput.value = ids[0];
logDiv.innerText = "Status: Selected Node " + ids[0];
} else {
logDiv.innerText = "Status: No node selected";
}
});
});
// 测试修改信息
btnSet.addEventListener("confirm", () => {
let data = {
id: nodeIdInput.value,
path: "name",
value: newNameInput.value,
};
if (!data.id) {
logDiv.innerText = "Error: Please get Node ID first";
return;
}
Editor.Ipc.sendToMain("mcp-bridge:set-node-property", data, (err, res) => {
if (err) {
logDiv.innerText = "Error: " + err;
} else {
logDiv.innerText = "Success: " + res;
}
});
});
},
});