From 69ab5710f66a193e1608359fe2b085691aa6cb61 Mon Sep 17 00:00:00 2001 From: winlifes <110321103+Winlifes@users.noreply.github.com> Date: Mon, 11 May 2026 05:30:48 -0700 Subject: [PATCH] Release v0.1.3 --- CHANGELOG.md | 14 +++++++ README.md | 15 ++++---- README_CN.md | 15 ++++---- browser.js | 13 ++++++- lib/assets.js | 28 ++++++++++++++ lib/config.js | 7 ++++ lib/tool-registry.js | 87 +++++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- panel/index.js | 9 ++++- 9 files changed, 169 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8809ccd..7aab558 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ This project follows a simple changelog format inspired by [Keep a Changelog](ht ## [Unreleased] +## [0.1.3] - 2026-05-11 + +### Added + +- Added `get_editor_state` as a compact structured editor-state summary tool. +- Added `get_selection` and `set_selection` as explicit selection workflow tools for editor-side automation. +- Added persistence for the selected one-click MCP client target in the Cocos panel configuration. + +### Changed + +- Expanded the default `core` profile from 19 tools to 22 tools by promoting editor-state and selection workflows. +- Expanded the `full` profile from 67 tools to 70 tools. +- Updated panel config persistence so changing the selected MCP client target no longer restarts the server unnecessarily. + ## [0.1.2] - 2026-04-30 ### Added diff --git a/README.md b/README.md index 6e1fad6..8291e14 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ Try a higher-level prompt in your AI client: - This extension is **Editor-only**. It is meant to automate Cocos Creator, not to add runtime dependencies to your final game build. - The MCP server listens on `http://127.0.0.1:8765/` by default. - If the configured port is busy, the server automatically falls back to the next available port and the panel/client config use the actual running port. -- The default `core` profile exposes 19 high-signal tools. Switch to `full` in the panel if you want all 67 tools exposed. +- The default `core` profile exposes 22 high-signal tools. Switch to `full` in the panel if you want all 70 tools exposed. - All exposed MCP tools execute directly. There is no extra approval toggle inside the Cocos extension. - File tools and `cocos://asset/path/...` resources are restricted to the active Cocos project root. - The recommended workflow is `execute_javascript` first, then focused helper tools for screenshots, diagnostics, assets, and inspection. @@ -214,7 +214,7 @@ Try a higher-level prompt in your AI client: ## Highlights -- **67 Built-in Tools** — Scene hierarchy, assets, UI creation, components, files, script diagnostics, screenshots, runtime control, and input simulation +- **70 Built-in Tools** — Scene hierarchy, editor state, selection workflows, assets, UI creation, components, files, script diagnostics, screenshots, runtime control, and input simulation - **Primary Unified Tool** — `execute_javascript` supports both `scene` and `editor` contexts - **Resources & Prompts** — Live project resources plus reusable workflows like script fixing, scene validation, and playable prototype creation - **Cocos Panel UI** — A minimal `Funplay > MCP Server` panel for service management and MCP client setup @@ -231,20 +231,20 @@ Funplay MCP for Cocos follows the same design principles as Funplay MCP for Unit | Embedded server | Built-in HTTP MCP server | Built-in HTTP MCP server | | Primary execution tool | `execute_javascript` | `execute_code` | | Primary language | JavaScript in scene/editor contexts | C# in Unity editor/runtime contexts | -| Default profile | `core` with 19 tools | `core` focused tool profile | -| Full profile | 67 tools | 79 tools | +| Default profile | `core` with 22 tools | `core` focused tool profile | +| Full profile | 70 tools | 79 tools | | Client setup | One-click config panel | One-click config window | ## MCP Capabilities The current package exposes four capability layers: -- **Tools** — 19 tools in `core`, 67 tools in `full` +- **Tools** — 22 tools in `core`, 70 tools in `full` - **Primary execution** — `execute_javascript` for scene/runtime and editor/browser automation - **Prompts** — `fix_script_errors`, `create_playable_prototype`, `scene_validation`, and `auto_wire_scene` - **Resources** — project context, scene summaries, current selection, script diagnostics, asset selection, and MCP interaction history -The default `core` set is intentionally small: `execute_javascript`, `execute_scene_script`, `execute_editor_script`, `get_project_info`, `get_scene_info`, `get_hierarchy`, `list_scenes`, `open_scene`, `list_assets`, `inspect_asset`, `open_asset`, `select_asset`, `run_script_diagnostics`, `get_script_diagnostic_context`, `get_runtime_state`, `capture_editor_screenshot`, `capture_scene_screenshot`, `capture_preview_screenshot`, and `list_editor_windows`. +The default `core` set is intentionally small: `execute_javascript`, `execute_scene_script`, `execute_editor_script`, `get_editor_state`, `get_selection`, `set_selection`, `get_project_info`, `get_scene_info`, `get_hierarchy`, `list_scenes`, `open_scene`, `list_assets`, `inspect_asset`, `open_asset`, `select_asset`, `run_script_diagnostics`, `get_script_diagnostic_context`, `get_runtime_state`, `capture_editor_screenshot`, `capture_scene_screenshot`, `capture_preview_screenshot`, and `list_editor_windows`. ## Built-in Resources @@ -261,11 +261,12 @@ The default `core` set is intentionally small: `execute_javascript`, `execute_sc ## Built-in Tools -Funplay MCP for Cocos currently ships with **67 tool functions** in the `full` profile: +Funplay MCP for Cocos currently ships with **70 tool functions** in the `full` profile: | Category | Tools | |----------|-------| | **Script Execution** | `execute_javascript`, `execute_scene_script`, `execute_editor_script` | +| **Editor State** | `get_editor_state`, `get_selection`, `set_selection`, `get_editor_selection` | | **Project & Scene** | `get_project_info`, `get_scene_info`, `get_hierarchy`, `find_nodes`, `inspect_node`, `list_scenes`, `open_scene`, `run_scene_asset` | | **Node Editing** | `create_node`, `delete_node`, `set_node_transform` | | **Assets & Prefabs** | `list_assets`, `inspect_asset`, `open_asset`, `select_asset`, `delete_asset`, `list_prefabs`, `instantiate_prefab`, `get_editor_selection` | diff --git a/README_CN.md b/README_CN.md index 416762d..c77cd44 100644 --- a/README_CN.md +++ b/README_CN.md @@ -197,7 +197,7 @@ url = "http://127.0.0.1:8765/" - 这是一个 **仅限 Editor** 的扩展,用于自动化 Cocos Creator,不会给最终游戏包添加运行时依赖。 - MCP Server 默认监听 `http://127.0.0.1:8765/`。 - 如果配置端口被占用,服务会自动回退到下一个可用端口,面板与一键客户端配置会使用实际运行端口。 -- 默认 `core` profile 暴露 19 个高频工具;如果需要完整工具集,可在面板切到 `full`,暴露全部 67 个工具。 +- 默认 `core` profile 暴露 22 个高频工具;如果需要完整工具集,可在面板切到 `full`,暴露全部 70 个工具。 - 所有已暴露的 MCP 工具都会直接执行,Cocos 扩展里没有额外 approval 开关。 - 文件工具和 `cocos://asset/path/...` 资源默认只能访问当前 Cocos 项目根目录内的路径。 - 推荐工作流是优先使用 `execute_javascript`,再配合截图、诊断、资产、检查类工具。 @@ -214,7 +214,7 @@ url = "http://127.0.0.1:8765/" ## 核心特性 -- **67 个内置工具** — 覆盖场景层级、资产、UI 创建、组件、文件、脚本诊断、截图、运行态控制和输入模拟 +- **70 个内置工具** — 覆盖场景层级、编辑器状态、选择工作流、资产、UI 创建、组件、文件、脚本诊断、截图、运行态控制和输入模拟 - **统一主工具** — `execute_javascript` 同时支持 `scene` 和 `editor` 两种上下文 - **Resources 与 Prompts** — 实时项目资源,以及脚本修复、场景验证、可玩原型等可复用工作流 - **Cocos 图形面板** — `Funplay > MCP Server` 提供极简服务管理与 MCP 客户端配置 @@ -231,20 +231,20 @@ Funplay MCP for Cocos 延续 Funplay MCP for Unity 的设计原则,并针对 C | 内置服务 | 内嵌 HTTP MCP Server | 内嵌 HTTP MCP Server | | 主执行工具 | `execute_javascript` | `execute_code` | | 主语言 | 场景/编辑器上下文中的 JavaScript | Unity 编辑器/运行态中的 C# | -| 默认工具集 | `core`,19 个工具 | 聚焦版 `core` 工具集 | -| 完整工具集 | 67 个工具 | 79 个工具 | +| 默认工具集 | `core`,22 个工具 | 聚焦版 `core` 工具集 | +| 完整工具集 | 70 个工具 | 79 个工具 | | 客户端配置 | 一键配置面板 | 一键配置窗口 | ## MCP 能力结构 当前包提供四层能力: -- **Tools** — `core` 下 19 个工具,`full` 下 67 个工具 +- **Tools** — `core` 下 22 个工具,`full` 下 70 个工具 - **Primary execution** — `execute_javascript` 用于场景/运行态和编辑器/browser 自动化 - **Prompts** — `fix_script_errors`、`create_playable_prototype`、`scene_validation`、`auto_wire_scene` - **Resources** — 项目上下文、场景摘要、当前选择、脚本诊断、资产选择和 MCP 交互历史 -当前默认 `core` 工具集刻意保持精简,只包含:`execute_javascript`、`execute_scene_script`、`execute_editor_script`、`get_project_info`、`get_scene_info`、`get_hierarchy`、`list_scenes`、`open_scene`、`list_assets`、`inspect_asset`、`open_asset`、`select_asset`、`run_script_diagnostics`、`get_script_diagnostic_context`、`get_runtime_state`、`capture_editor_screenshot`、`capture_scene_screenshot`、`capture_preview_screenshot`、`list_editor_windows`。 +当前默认 `core` 工具集刻意保持精简,只包含:`execute_javascript`、`execute_scene_script`、`execute_editor_script`、`get_editor_state`、`get_selection`、`set_selection`、`get_project_info`、`get_scene_info`、`get_hierarchy`、`list_scenes`、`open_scene`、`list_assets`、`inspect_asset`、`open_asset`、`select_asset`、`run_script_diagnostics`、`get_script_diagnostic_context`、`get_runtime_state`、`capture_editor_screenshot`、`capture_scene_screenshot`、`capture_preview_screenshot`、`list_editor_windows`。 ## 内置 Resources @@ -261,11 +261,12 @@ Funplay MCP for Cocos 延续 Funplay MCP for Unity 的设计原则,并针对 C ## 内置工具 -Funplay MCP for Cocos 当前在 `full` profile 下提供 **67 个工具函数**: +Funplay MCP for Cocos 当前在 `full` profile 下提供 **70 个工具函数**: | 分类 | 工具 | |------|------| | **脚本执行** | `execute_javascript`, `execute_scene_script`, `execute_editor_script` | +| **编辑器状态** | `get_editor_state`, `get_selection`, `set_selection`, `get_editor_selection` | | **项目与场景** | `get_project_info`, `get_scene_info`, `get_hierarchy`, `find_nodes`, `inspect_node`, `list_scenes`, `open_scene`, `run_scene_asset` | | **节点编辑** | `create_node`, `delete_node`, `set_node_transform` | | **资产与 Prefab** | `list_assets`, `inspect_asset`, `open_asset`, `select_asset`, `delete_asset`, `list_prefabs`, `instantiate_prefab`, `get_editor_selection` | diff --git a/browser.js b/browser.js index fbf3dca..d2e975f 100644 --- a/browser.js +++ b/browser.js @@ -87,6 +87,7 @@ class ExtensionService { this.toolRegistry = createToolRegistry({ getRuntimeContext: runtimeContext, + getStatus: () => this.getStatus(), interactionLog: this.interactionLog, sceneBridge, editorExecutor: async (payload) => await this.executeEditorScript(payload, runtimeContext), @@ -315,16 +316,24 @@ class ExtensionService { maxInteractionLogEntries: Number.isInteger(nextMaxEntries) ? Math.max(10, Math.min(500, nextMaxEntries)) : this.config.maxInteractionLogEntries, + lastClientTargetId: partialConfig && partialConfig.lastClientTargetId + ? String(partialConfig.lastClientTargetId) + : this.config.lastClientTargetId, }; const configPath = this.config.configPath; fs.writeFileSync(configPath, JSON.stringify(nextConfig, null, 2) + '\n', 'utf8'); const wasRunning = Boolean(this.server && this.server.isRunning()); - if (wasRunning) { + const requiresRestart = wasRunning && ( + nextConfig.host !== this.config.host || + nextConfig.port !== this.config.port || + nextConfig.toolProfile !== this.config.toolProfile + ); + if (requiresRestart) { await this.stopServer(); } this.reloadRuntime(); - if (wasRunning) { + if (requiresRestart) { await this.startServer(); } return this.getPanelState(); diff --git a/lib/assets.js b/lib/assets.js index dc8ebd2..b997f80 100644 --- a/lib/assets.js +++ b/lib/assets.js @@ -157,6 +157,32 @@ function selectAsset(uuid) { return { selected: true, uuid }; } +function selectNode(uuid) { + if (!global.Editor || !Editor.Selection || typeof Editor.Selection.select !== 'function') { + throw new Error('Editor.Selection.select is unavailable in this Cocos environment.'); + } + + Editor.Selection.clear('node'); + Editor.Selection.select('node', uuid); + return { selected: true, uuid }; +} + +function clearSelection(type) { + if (!global.Editor || !Editor.Selection || typeof Editor.Selection.clear !== 'function') { + throw new Error('Editor.Selection.clear is unavailable in this Cocos environment.'); + } + + const normalized = String(type || 'all').trim().toLowerCase(); + if (normalized === 'asset' || normalized === 'node') { + Editor.Selection.clear(normalized); + return { cleared: true, type: normalized }; + } + + Editor.Selection.clear('asset'); + Editor.Selection.clear('node'); + return { cleared: true, type: 'all' }; +} + function getCurrentSelection() { if (!global.Editor || !Editor.Selection || typeof Editor.Selection.getSelected !== 'function') { throw new Error('Editor.Selection API is unavailable in this Cocos environment.'); @@ -170,6 +196,7 @@ function getCurrentSelection() { } module.exports = { + clearSelection, deleteAsset, getCurrentSelection, listAssets, @@ -179,4 +206,5 @@ module.exports = { queryAssetMeta, queryAssetUrl, selectAsset, + selectNode, }; diff --git a/lib/config.js b/lib/config.js index 7dc21ea..32a9a99 100644 --- a/lib/config.js +++ b/lib/config.js @@ -9,6 +9,7 @@ const DEFAULTS = { toolProfile: 'core', autostart: true, maxInteractionLogEntries: 50, + lastClientTargetId: 'claude_code', }; function getProjectPath() { @@ -60,6 +61,11 @@ function normalizeProfile(value) { return String(value || DEFAULTS.toolProfile).toLowerCase() === 'full' ? 'full' : 'core'; } +function normalizeClientTargetId(value) { + const normalized = String(value || '').trim(); + return normalized || DEFAULTS.lastClientTargetId; +} + function loadConfig() { const projectPath = getProjectPath(); const configPath = path.join(projectPath, 'funplay-cocos-mcp.config.json'); @@ -75,6 +81,7 @@ function loadConfig() { maxInteractionLogEntries: Number.isInteger(fileConfig.maxInteractionLogEntries) ? Math.max(10, Math.min(500, fileConfig.maxInteractionLogEntries)) : DEFAULTS.maxInteractionLogEntries, + lastClientTargetId: normalizeClientTargetId(fileConfig.lastClientTargetId), configPath, configError: fileConfig.__error || '', }; diff --git a/lib/tool-registry.js b/lib/tool-registry.js index 092c2e3..08c70aa 100644 --- a/lib/tool-registry.js +++ b/lib/tool-registry.js @@ -3,6 +3,7 @@ const fs = require('fs'); const path = require('path'); const { + clearSelection, deleteAsset, getCurrentSelection, listAssets, @@ -11,6 +12,7 @@ const { queryAssetInfo, queryAssetMeta, selectAsset, + selectNode, } = require('./assets'); const { runScriptDiagnostics } = require('./diagnostics'); const { listWindows, sendKeyCombo, sendKeyPress, sendMouseClick, sendMouseDrag } = require('./input'); @@ -121,7 +123,7 @@ async function refreshAssets(projectPath, targetPath) { return 'File written outside assets directory; no asset-db refresh was needed.'; } -function createToolRegistry({ getRuntimeContext, interactionLog, sceneBridge, editorExecutor }) { +function createToolRegistry({ getRuntimeContext, getStatus, interactionLog, sceneBridge, editorExecutor }) { const tools = [ { name: 'execute_javascript', @@ -180,6 +182,87 @@ function createToolRegistry({ getRuntimeContext, interactionLog, sceneBridge, ed return await editorExecutor({ code: args.code, args: args.args || {} }); }, }, + { + name: 'get_editor_state', + profile: 'core', + description: '[specialist] Return a structured editor-state snapshot including project info, runtime server status, current selection, and visible Electron windows. Prefer this when you want one compact editor summary.', + inputSchema: createSchema({}, []), + handler: async () => { + const runtimeContext = getRuntimeContext(); + const status = typeof getStatus === 'function' ? getStatus() : null; + let scene = null; + try { + const sceneInfo = await sceneBridge.call('getSceneInfo', { maxDepth: 1, includeComponents: false }); + scene = sceneInfo + ? { + sceneName: sceneInfo.sceneName, + uuid: sceneInfo.uuid, + childCount: sceneInfo.childCount, + } + : null; + } catch (error) { + scene = { error: error.message }; + } + + let windows = []; + try { + windows = listWindows(); + } catch (error) { + windows = [{ error: error.message }]; + } + + return { + extensionName: runtimeContext.extensionName, + version: runtimeContext.version, + projectName: runtimeContext.projectName, + projectPath: runtimeContext.projectPath, + cocosVersion: runtimeContext.cocosVersion, + toolProfile: runtimeContext.config ? runtimeContext.config.toolProfile : 'core', + status, + selection: getCurrentSelection(), + scene, + windows, + }; + }, + }, + { + name: 'get_selection', + profile: 'core', + description: '[specialist] Return the current editor selection in a compact structured form. Prefer this when selection state matters for the next action.', + inputSchema: createSchema({}, []), + handler: async () => getCurrentSelection(), + }, + { + name: 'set_selection', + profile: 'core', + description: '[specialist] Set or clear the current editor selection for an asset or node. Use this when downstream editor workflows depend on selection state.', + inputSchema: createSchema( + { + type: { type: 'string', description: 'Selection target type: asset, node, or clear.' }, + target: { type: 'string', description: 'Asset uuid/path/db url, or node uuid when type=node.' }, + clearMode: { type: 'string', description: 'When type=clear, choose asset, node, or all.' }, + }, + ['type'] + ), + handler: async (args) => { + const type = String(args.type || '').trim().toLowerCase(); + if (type === 'clear') { + return clearSelection(args.clearMode || 'all'); + } + if (type === 'asset') { + const info = await queryAssetInfo(args.target); + return selectAsset(info.uuid || args.target); + } + if (type === 'node') { + const target = String(args.target || '').trim(); + if (!target) { + throw new Error('target is required when type=node.'); + } + return selectNode(target); + } + throw new Error(`Unknown selection type '${args.type}'. Expected asset, node, or clear.`); + }, + }, { name: 'get_scene_info', profile: 'core', @@ -441,7 +524,7 @@ function createToolRegistry({ getRuntimeContext, interactionLog, sceneBridge, ed { name: 'get_editor_selection', profile: 'full', - description: '[core] Return the current node and asset selection in the Cocos editor.', + description: '[compat] Return the current node and asset selection in the Cocos editor. Prefer get_selection as the primary structured selection read tool.', inputSchema: createSchema({}, []), handler: async () => getCurrentSelection(), }, diff --git a/package.json b/package.json index 9d09042..0c8e5a7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "funplay-cocos-mcp", "package_version": 2, - "version": "0.1.2", + "version": "0.1.3", "description": "Embedded MCP server for Cocos Creator with scene script execution, project resources, prompts, and file/scene tools.", "author": "Funplay", "license": "MIT", diff --git a/panel/index.js b/panel/index.js index 589f5b5..0741a12 100644 --- a/panel/index.js +++ b/panel/index.js @@ -248,7 +248,8 @@ module.exports = Editor.Panel.define({ }, renderClientTargets() { const targets = (this.state && this.state.clientTargets) || []; - const selected = this.$.clientTargetSelect.value || (targets[0] && targets[0].id); + const preferred = this.state && this.state.config ? this.state.config.lastClientTargetId : ''; + const selected = this.$.clientTargetSelect.value || preferred || (targets[0] && targets[0].id); this.$.clientTargetSelect.innerHTML = targets .map((target) => ``) .join(''); @@ -300,6 +301,7 @@ module.exports = Editor.Panel.define({ toolProfile: this.$.profileSelect.value || 'core', autostart: Boolean(this.$.enabledInput.value), maxInteractionLogEntries: this.state && this.state.config ? this.state.config.maxInteractionLogEntries : 50, + lastClientTargetId: this.$.clientTargetSelect.value || 'claude_code', }; }, async handleEnableToggle() { @@ -333,7 +335,10 @@ module.exports = Editor.Panel.define({ this.$.portInput.addEventListener('change', () => this.persistConfig({ showOutput: true })); this.$.profileSelect.addEventListener('change', () => this.persistConfig({ showOutput: true })); this.$.clientTargetSelect.addEventListener('confirm', () => this.renderClientTargetStatus()); - this.$.clientTargetSelect.addEventListener('change', () => this.renderClientTargetStatus()); + this.$.clientTargetSelect.addEventListener('change', () => { + this.renderClientTargetStatus(); + this.persistConfig(); + }); this.$.configureClientBtn.addEventListener('click', () => { const targetId = this.$.clientTargetSelect.value; if (!targetId) {