diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 2234dea..0400c54 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -37,26 +37,26 @@ mcp-bridge/
```json
{
- "name": "mcp-bridge",
- "version": "1.0.0",
- "description": "MCP Bridge for Cocos Creator",
- "main": "main.js",
- "panel": {
- "main": "panel/index.html",
- "type": "dockable",
- "title": "MCP Bridge",
- "width": 800,
- "height": 600
- },
- "contributions": {
- "menu": [
- {
- "path": "Packages/MCP Bridge",
- "label": "Open Test Panel",
- "message": "open-test-panel"
- }
- ]
- }
+ "name": "mcp-bridge",
+ "version": "1.0.0",
+ "description": "MCP Bridge for Cocos Creator",
+ "main": "main.js",
+ "panel": {
+ "main": "panel/index.html",
+ "type": "dockable",
+ "title": "MCP Bridge",
+ "width": 800,
+ "height": 600
+ },
+ "contributions": {
+ "menu": [
+ {
+ "path": "Packages/MCP Bridge",
+ "label": "Open Test Panel",
+ "message": "open-test-panel"
+ }
+ ]
+ }
}
```
@@ -116,18 +116,23 @@ startServer(port) {
- **协议修正**: 经过测试对比,最终确认使用 `scene:enter-prefab-edit-mode` 消息并配合 `Editor.Ipc.sendToAll` 是进入预制体编辑模式的最佳方案,解决了 `scene:open-by-uuid` 无法触发编辑状态的问题。
- **文档深度补全**: 遵循全局开发规范,同步更新了所有技术文档,确保 100% 简体中文覆盖及详尽的 JSDoc 注释。
+### 2026-02-25: 修复 manage_script 路径引用错误与强制生成 Meta
+
+- **缺陷修正**: 修复了 `main.js` 中 `manageScript` 处理 `create` 动作时由于变量名解构导致 `path.dirname` 找不到 `path` 引用的问题。现已改为使用预设的 `pathModule` 模块。
+- **规范强化**: 将 `manage_script` 的工具提示(Prompt)更新为强制要求调用 `refresh_editor` 生成脚本的 `.meta` 文件,以确保新创建的脚本能够被正确挂载为组件,同时不增加整体 Token 消耗。
+
### 3.2 MCP 工具注册
在 `/list-tools` 接口中注册工具:
```javascript
const tools = [
- {
- name: "get_selected_node",
- description: "获取当前选中的节点",
- parameters: [],
- },
- // 其他工具...
+ {
+ name: "get_selected_node",
+ description: "获取当前选中的节点",
+ parameters: [],
+ },
+ // 其他工具...
];
```
@@ -137,13 +142,13 @@ const tools = [
```javascript
const sceneScript = {
- "create-node"(params, callback) {
- // 创建节点逻辑...
- },
- "set-property"(params, callback) {
- // 设置属性逻辑...
- },
- // 其他操作...
+ "create-node"(params, callback) {
+ // 创建节点逻辑...
+ },
+ "set-property"(params, callback) {
+ // 设置属性逻辑...
+ },
+ // 其他操作...
};
```
@@ -245,33 +250,33 @@ manageAsset(args, callback) {
```html
-
-
- Main
- Tool Test
-
+
+
+ Main
+ Tool Test
+
-
-
-
-
+
+
+
+
-
-
```
diff --git a/README.md b/README.md
index 5af1c1d..4a6dcb0 100644
--- a/README.md
+++ b/README.md
@@ -71,12 +71,12 @@ Args: [你的项目所在盘符]:/[项目路径]/packages/mcp-bridge/mcp-proxy.j
```json
{
- "mcpServers": {
- "cocos-creator": {
- "command": "node",
- "args": ["[Cocos Creator 项目的绝对路径]/packages/mcp-bridge/mcp-proxy.js"]
- }
- }
+ "mcpServers": {
+ "cocos-creator": {
+ "command": "node",
+ "args": ["[Cocos Creator 项目的绝对路径]/packages/mcp-bridge/mcp-proxy.js"]
+ }
+ }
}
```
@@ -171,7 +171,7 @@ Args: [你的项目所在盘符]:/[项目路径]/packages/mcp-bridge/mcp-proxy.j
### 9. manage_script
-- **描述**: 管理脚本文件,默认创建 TypeScript 脚本
+- **描述**: 管理脚本文件,默认创建 TypeScript 脚本。**注意**:创建或修改脚本后,此工具没有主动刷新资源库。如果将要作为组件挂载到节点,创建后**必须**接着显式调用 `refresh_editor` 工具(传递精准路径参数)以便编辑器生成 `.meta` 文件并分配 UUID,否则无法作为组件添加。
- **参数**:
- `action`: 操作类型(`create`, `delete`, `read`, `write`)
- `path`: 脚本路径,如 `db://assets/scripts/NewScript.ts`
diff --git a/UPDATE_LOG.md b/UPDATE_LOG.md
index afb70c4..f1fb361 100644
--- a/UPDATE_LOG.md
+++ b/UPDATE_LOG.md
@@ -126,26 +126,47 @@
## 八、 Token 消耗深度优化 (2026-02-24)
### 1. 工具描述精简 (`main.js`)
+
- **问题**: `globalPrecautions` (AI 安全守则) 被硬编码到所有工具的 `description` 中,导致每次环境初始化或查阅工具列表时浪费约 2200 个 CJK Token。
- **优化**: 收束安全守则的广播范围。目前仅针对高风险的**写操作**(如 `manage_components`, `update_node_transform`, `manage_material`, `create_node` 等)保留警告,低风险或只读分析类工具(如 `get_scene_hierarchy`, `get_selected_node`)已悉数移除该文本。
- **效果**: `/list-tools` 整体负载字符数缩减近 40%。
### 2. 长数据截断保护 (`scene-script.js`)
+
- **问题**: `manage_components(get)` 会完整序列化多边形坐标集、曲线数据数组以及 Base64 图片,产生极其庞大且对 AI 无用的 JSON 负载。
-- **优化**:
- - **数组截断**: 长度超过 10 的数组直接返回 `[Array(length)]`,彻底杜绝数据雪崩。
- - **字符串截断**: 长度超过 200 的字符串限制为截断显示并附带 `...[Truncated, total length: X]` 提示。
+- **优化**:
+ - **数组截断**: 长度超过 10 的数组直接返回 `[Array(length)]`,彻底杜绝数据雪崩。
+ - **字符串截断**: 长度超过 200 的字符串限制为截断显示并附带 `...[Truncated, total length: X]` 提示。
### 3. 层级树获取瘦身与分页 (`get_scene_hierarchy`)
+
- **问题**: 请求场景层级时会一次性返回完整 1000+ 节点的深层结构,包括所有变换矩阵。
-- **优化**:
- - 支持 `depth` 深度限制(默认 2 层)。
- - 支持 `nodeId` 参数,允许 AI 缩小作用域,从指定根节点向下探测。
- - 添加 `includeDetails` 参数。默认关闭,此时剥离坐标、缩放与尺寸指标,且将冗长的组件详细结构浓缩成简化的名称数组(如 `["Sprite", "Button"]`)。
+- **优化**:
+ - 支持 `depth` 深度限制(默认 2 层)。
+ - 支持 `nodeId` 参数,允许 AI 缩小作用域,从指定根节点向下探测。
+ - 添加 `includeDetails` 参数。默认关闭,此时剥离坐标、缩放与尺寸指标,且将冗长的组件详细结构浓缩成简化的名称数组(如 `["Sprite", "Button"]`)。
### 4. 查找结果精简 (`find_gameobjects`)
+
- **优化**: 将原本包含 Transform(位移/缩放/尺寸)全量数据的匹配回传,精简为仅包含核心识别特征的基础集 (`uuid`, `name`, `active`, `components`, `childrenCount`),极大释放了同名大批量查找时的 Token 压力。
### 5. 底层鲁棒性大修
+
- **问题**: 上述优化在应用过程中暴露出遍历未命名根节点(如 `cc.Scene`)时遭遇 `undefined.startsWith` 报错并引发 IPC 悬挂的致命隐患。
- **修复**: 在 `dumpNodes` 与 `searchNode` 中增设前置安全屏障,并修复 `cc.js.getClassName(c)` 替代底层的 `__typename` 来兼容 2.4 获取有效类名。修复了 `main.js` 中关于 `get_scene_hierarchy` 的参数传递脱节问题。
+
+---
+
+## 九、 脚本管理修复与强化 (2026-02-25)
+
+### 1. `manage_script` 路径引用错误修复
+
+- **问题**: AI 在调用 `manage_script` 工具执行 `create` 创建脚本时,出现 `path is not defined` 报错。
+- **原因**: 传入的变量 `path` 已经被解构重命名为 `scriptPath`,而在后续获取物理路径时,错误地调用了 `path.dirname()`,导致引用错误。
+- **修复**: 将 `path.dirname` 修正为全局正确引入的 `pathModule.dirname`,彻底解决了使用此工具生成自定义脚本库时的崩溃问题。
+
+### 2. 强制生成 Script Meta 文件的提示词 (Prompt) 优化
+
+- **问题**: AI 助手创建或修改脚本后,若不主动触发系统刷新,后续试图通过 `manage_components` 将该新脚本挂载为组件时,会由于缺乏有效的 `.meta` 扫描和 UUID 索引而失败。
+- **优化**: 在 `main.js` 中的 `manage_script` 工具 `description` 提示词中,将原本建议性质的刷新语气,修改为严格指令:“**创建后必须调用 refresh_editor (务必指定 path) 生成 meta 文件,否则无法作为组件添加**”。
+- **效益**: 在不增加 Token 开销的前提下,强制规范了大语言模型的行为,保障了脚本创建到组件挂载工作流的健壮性。
diff --git a/main.js b/main.js
index 74b5b3d..025b5ef 100644
--- a/main.js
+++ b/main.js
@@ -10,8 +10,8 @@ let logBuffer = []; // 存储所有日志
let mcpServer = null;
let isSceneBusy = false;
let serverConfig = {
- port: 3456,
- active: false,
+ port: 3456,
+ active: false,
};
/**
@@ -28,32 +28,32 @@ let isProcessingCommand = false;
* @returns {Promise} 操作完成后 resolve
*/
function enqueueCommand(fn) {
- return new Promise((resolve) => {
- commandQueue.push({ fn, resolve });
- processNextCommand();
- });
+ return new Promise((resolve) => {
+ commandQueue.push({ fn, resolve });
+ processNextCommand();
+ });
}
/**
* 从队列中取出下一个指令并执行
*/
function processNextCommand() {
- if (isProcessingCommand || commandQueue.length === 0) return;
- isProcessingCommand = true;
- const { fn, resolve } = commandQueue.shift();
- try {
- fn(() => {
- isProcessingCommand = false;
- resolve();
- processNextCommand();
- });
- } catch (e) {
- // 防止队列因未捕获异常永久阻塞
- addLog("error", `[CommandQueue] 指令执行异常: ${e.message}`);
- isProcessingCommand = false;
- resolve();
- processNextCommand();
- }
+ if (isProcessingCommand || commandQueue.length === 0) return;
+ isProcessingCommand = true;
+ const { fn, resolve } = commandQueue.shift();
+ try {
+ fn(() => {
+ isProcessingCommand = false;
+ resolve();
+ processNextCommand();
+ });
+ } catch (e) {
+ // 防止队列因未捕获异常永久阻塞
+ addLog("error", `[CommandQueue] 指令执行异常: ${e.message}`);
+ isProcessingCommand = false;
+ resolve();
+ processNextCommand();
+ }
}
/**
@@ -66,29 +66,29 @@ function processNextCommand() {
* @param {number} timeout 超时毫秒数,默认 15000
*/
function callSceneScriptWithTimeout(pluginName, method, args, callback, timeout = 15000) {
- let settled = false;
- const timer = setTimeout(() => {
- if (!settled) {
- settled = true;
- addLog("error", `[超时] callSceneScript "${method}" 超过 ${timeout}ms 未响应`);
- callback(`操作超时: ${method} (${timeout}ms)`);
- }
- }, timeout);
+ let settled = false;
+ const timer = setTimeout(() => {
+ if (!settled) {
+ settled = true;
+ addLog("error", `[超时] callSceneScript "${method}" 超过 ${timeout}ms 未响应`);
+ callback(`操作超时: ${method} (${timeout}ms)`);
+ }
+ }, timeout);
- // callSceneScript 支持 3 参数(无 args)和 4 参数两种调用形式
- const wrappedCallback = (err, result) => {
- if (!settled) {
- settled = true;
- clearTimeout(timer);
- callback(err, result);
- }
- };
+ // callSceneScript 支持 3 参数(无 args)和 4 参数两种调用形式
+ const wrappedCallback = (err, result) => {
+ if (!settled) {
+ settled = true;
+ clearTimeout(timer);
+ callback(err, result);
+ }
+ };
- if (args === null || args === undefined) {
- Editor.Scene.callSceneScript(pluginName, method, wrappedCallback);
- } else {
- Editor.Scene.callSceneScript(pluginName, method, args, wrappedCallback);
- }
+ if (args === null || args === undefined) {
+ Editor.Scene.callSceneScript(pluginName, method, wrappedCallback);
+ } else {
+ Editor.Scene.callSceneScript(pluginName, method, args, wrappedCallback);
+ }
}
/**
@@ -97,21 +97,21 @@ function callSceneScriptWithTimeout(pluginName, method, args, callback, timeout
* @param {string} message 日志内容
*/
function addLog(type, message) {
- const logEntry = {
- time: new Date().toLocaleTimeString(),
- type: type,
- content: message,
- };
- logBuffer.push(logEntry);
- Editor.Ipc.sendToPanel("mcp-bridge", "mcp-bridge:on-log", logEntry);
+ const logEntry = {
+ time: new Date().toLocaleTimeString(),
+ type: type,
+ content: message,
+ };
+ logBuffer.push(logEntry);
+ Editor.Ipc.sendToPanel("mcp-bridge", "mcp-bridge:on-log", logEntry);
- // 【修改】确保所有日志都输出到编辑器控制台,以便用户查看
- if (type === "error") {
- Editor.error(`[MCP] ${message}`);
- } else if (type === "warn") {
- Editor.warn(`[MCP] ${message}`);
- } else {
- }
+ // 【修改】确保所有日志都输出到编辑器控制台,以便用户查看
+ if (type === "error") {
+ Editor.error(`[MCP] ${message}`);
+ } else if (type === "warn") {
+ Editor.warn(`[MCP] ${message}`);
+ } else {
+ }
}
/**
@@ -119,7 +119,7 @@ function addLog(type, message) {
* @returns {string} 拼接后的日志字符串
*/
function getLogContent() {
- return logBuffer.map((entry) => `[${entry.time}] [${entry.type}] ${entry.content}`).join("\n");
+ return logBuffer.map((entry) => `[${entry.time}] [${entry.type}] ${entry.content}`).join("\n");
}
/**
@@ -127,40 +127,40 @@ function getLogContent() {
* @returns {string} 场景数据的 JSON 字符串
*/
const getNewSceneTemplate = () => {
- // 尝试获取 UUID 生成函数
- let newId = "";
- if (Editor.Utils && Editor.Utils.uuid) {
- newId = Editor.Utils.uuid();
- } else if (Editor.Utils && Editor.Utils.UuidUtils && Editor.Utils.UuidUtils.uuid) {
- newId = Editor.Utils.UuidUtils.uuid();
- } else {
- // 兜底方案:如果找不到编辑器 API,生成一个随机字符串
- newId = Math.random().toString(36).substring(2, 15);
- }
+ // 尝试获取 UUID 生成函数
+ let newId = "";
+ if (Editor.Utils && Editor.Utils.uuid) {
+ newId = Editor.Utils.uuid();
+ } else if (Editor.Utils && Editor.Utils.UuidUtils && Editor.Utils.UuidUtils.uuid) {
+ newId = Editor.Utils.UuidUtils.uuid();
+ } else {
+ // 兜底方案:如果找不到编辑器 API,生成一个随机字符串
+ newId = Math.random().toString(36).substring(2, 15);
+ }
- const sceneData = [
- {
- __type__: "cc.SceneAsset",
- _name: "",
- _objFlags: 0,
- _native: "",
- scene: { __id__: 1 },
- },
- {
- __id__: 1,
- __type__: "cc.Scene",
- _name: "",
- _objFlags: 0,
- _parent: null,
- _children: [],
- _active: true,
- _level: 0,
- _components: [],
- autoReleaseAssets: false,
- _id: newId,
- },
- ];
- return JSON.stringify(sceneData);
+ const sceneData = [
+ {
+ __type__: "cc.SceneAsset",
+ _name: "",
+ _objFlags: 0,
+ _native: "",
+ scene: { __id__: 1 },
+ },
+ {
+ __id__: 1,
+ __type__: "cc.Scene",
+ _name: "",
+ _objFlags: 0,
+ _parent: null,
+ _children: [],
+ _active: true,
+ _level: 0,
+ _components: [],
+ autoReleaseAssets: false,
+ _id: newId,
+ },
+ ];
+ return JSON.stringify(sceneData);
};
/**
@@ -168,1058 +168,1062 @@ const getNewSceneTemplate = () => {
* @returns {Array