chore: track Cocos Creator project for future migration
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# Cocos MCP Pro Quick Start
|
||||
|
||||
Version: 0.2.0
|
||||
|
||||
## 中文:第一次连接
|
||||
|
||||
### 1. 安装并打开面板
|
||||
|
||||
1. 保留Cocos Store下载的ZIP,不要重新打包或增加外层目录。
|
||||
2. 在Creator中打开`扩展 -> 扩展管理器`,切到“项目”或“全局”扩展页并点击“导入”。
|
||||
3. 直接选择正式ZIP;ZIP根目录已经包含`package.json`,无需先解压。
|
||||
4. 确认扩展版本为`0.2.0`且已启用,等待Creator完成首次资源导入。
|
||||
5. 从`扩展 -> Cocos MCP -> Open MCP Panel`打开控制台。
|
||||
|
||||
正式包已经包含`dist`和运行依赖。购买用户不需要执行`npm install`。
|
||||
|
||||
### 2. 一键检测并配置客户端
|
||||
|
||||
1. 确认面板显示MCP服务“运行中”。
|
||||
2. 点击“一键检测”。Node、Bridge、WebSocket和Creator Scene均通过后再继续。
|
||||
3. 只配置你实际使用的客户端。v0.2.0支持Cursor、Windsurf、Trae CN、Trae国际版、Zoo Code、Cline、Roo Code和Codex。
|
||||
4. 完全退出并重新打开AI客户端,使它重新加载MCP配置。
|
||||
5. 调用`cocos_ping`和`cocos_get_capabilities`。两项返回真实结果才算连接成功。
|
||||
|
||||
v0.2.0不再让客户端执行临时`.cmd`或`.sh`启动器。配置会直接记录Node和`bridge-cli.js`的绝对路径,并通过`--project`传入当前工程。写配置前会备份原文件,写完后再次校验;重新点“一键配置”也会迁移旧版启动器配置。
|
||||
|
||||
### 3. 完成第一个闭环任务
|
||||
|
||||
打开测试场景后,可以直接让AI执行:
|
||||
|
||||
```text
|
||||
先读取场景,在Canvas下创建一个红色圆形和一个蓝色方形,横向排列。
|
||||
完成后验证运行态结果并保存场景;如果失败,回滚全部改动,不要让我手动挂脚本。
|
||||
```
|
||||
|
||||
这个任务应由`cocos_apply_ui_spec`或对应任务级工具一次完成。成功回执包含`status`、`changed`、`verified`、`undoAvailable`、`diagnostics`和`nextAction`。调用一次`cocos_mcp_undo_last`应撤销整个任务。
|
||||
|
||||
仍建议保持这个节奏:
|
||||
|
||||
```text
|
||||
Inspect -> Mutate -> Verify -> Undo or Continue
|
||||
```
|
||||
|
||||
### 4. 调试网页预览或构建错误
|
||||
|
||||
- 网页预览:使用`cocos_preview_debug_start`启动插件托管的Chrome/Edge会话,再用`read`读取JavaScript异常、Promise异常和失败网络请求,必要时用`capture`截图,最后`stop`。
|
||||
- Creator构建:使用`cocos_get_build_diagnostics`整理现有构建日志。它只提供证据,不代表Android或iOS构建一定不受影响。
|
||||
|
||||
### 需要帮助
|
||||
|
||||
先查看同目录的[TROUBLESHOOTING.md](./TROUBLESHOOTING.md)。反馈问题时请附:Creator版本、插件版本、系统、AI客户端、一键检测结果和完整错误信息。请勿发送API Key或未脱敏项目资产。
|
||||
|
||||
联系邮箱:jimmymjing@gmail.com
|
||||
|
||||
## English: First connection
|
||||
|
||||
1. In Extension Manager, choose Project or Global, import the Store ZIP directly, enable v0.2.0, and open `Extensions -> Cocos MCP -> Open MCP Panel`. The ZIP already has `package.json` at its root and does not need to be extracted first.
|
||||
2. Run the panel self-test. Node, Bridge, WebSocket, and Creator Scene must pass.
|
||||
3. Auto-configure only the client you use, fully restart it, then call `cocos_ping` and `cocos_get_capabilities`.
|
||||
4. v0.2.0 launches the absolute Node executable with `bridge-cli.js --project <project>`. It backs up, validates, and atomically replaces client config files; rerunning Auto Config migrates old launcher entries.
|
||||
5. Try a transactional task such as a red circle and blue square. It must create, verify, save, and undo without requiring a generated script or manual component attachment.
|
||||
|
||||
The Store package bundles its runtime dependencies. Store customers do not need to run `npm install`.
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
# Cocos MCP Pro Troubleshooting
|
||||
|
||||
Version: 0.2.0
|
||||
|
||||
## 先运行“一键检测”
|
||||
|
||||
面板会分别检查Node、Bridge、WebSocket、Creator Scene和客户端配置。先处理第一个失败项,不要连续重装或盲目重试写操作。
|
||||
|
||||
## 面板打不开或提示`methods`不存在
|
||||
|
||||
确认正式ZIP的根目录直接包含`package.json`、`dist/main.js`、`dist/scene.js`和`dist/panels/default/index.js`,没有多嵌一层扩展目录。移除不完整或旧版扩展后重新导入正式ZIP,并重启Creator。不要把未构建源码目录当作商店扩展安装。
|
||||
|
||||
Creator 3.8.8在Windows上若提示`unzip: No zipfiles found`后又报`ENOENT scandir`,先把ZIP复制到只含英文和数字的本地路径(例如`C:\CocosMCP\cocos-mcp.zip`)再导入。这个报错可能来自Creator内置解压器无法读取ZIP来源路径中的中文;项目目录本身仍可保留中文、空格和`&`。
|
||||
|
||||
## 客户端提示`Connection closed`
|
||||
|
||||
1. 保持Creator和当前工程打开,确认面板服务正在运行。
|
||||
2. 在面板里重新配置对应客户端。v0.2.0会迁移旧的`.cocos-mcp-launcher.cmd`或`.sh`配置。
|
||||
3. 完全退出并重启AI客户端,然后调用`cocos_ping`。
|
||||
4. 若仍失败,运行“一键检测”并查看客户端MCP日志。
|
||||
|
||||
正确配置应直接执行Node,参数依次包含`bridge-cli.js`、`--project`和工程目录。不要手抄他人的绝对路径。
|
||||
|
||||
## 提示`bridge cli missing`或路径乱码
|
||||
|
||||
这通常是旧版启动器或扩展目录不完整。卸载旧扩展,重新导入完整v0.2.0目录,再执行一键配置。路径中包含中文、空格或`&`时,不需要额外加转义字符;让面板生成配置即可。
|
||||
|
||||
## 提示`node not found`
|
||||
|
||||
安装Node.js 18至24,再回到面板重新运行一键配置。v0.2.0会把实际Node可执行文件的绝对路径写入客户端配置,不依赖GUI应用的`PATH`。
|
||||
|
||||
## 配置文件原有内容丢失了吗
|
||||
|
||||
不会直接覆盖。插件会解析现有JSON/TOML,更新`cocos-creator`条目,写入临时文件并校验后原子替换。原文件存在时会保留同目录`.cocos-mcp.bak`备份;格式损坏时会停止并明确报错。
|
||||
|
||||
## MCP连上了,但简单UI任务仍要求手动挂脚本
|
||||
|
||||
先调用`cocos_get_capabilities`,确认版本为0.2.0。圆形、矩形、文本、图片和按钮应使用`cocos_create_ui_*`或`cocos_apply_ui_spec`,不应临时生成组件脚本让用户手动挂载。若客户端仍这样规划,把这句话补给它:
|
||||
|
||||
```text
|
||||
使用Cocos MCP Pro任务级UI工具直接完成;失败时返回诊断并回滚,不生成临时脚本,不要求我手动操作Creator。
|
||||
```
|
||||
|
||||
## UI任务失败后留下半成品
|
||||
|
||||
停止继续写入,立即保存`diagnostics`并提交问题。`cocos_apply_ui_spec`设计为事务操作,任一步失败都应自动移除本次创建的节点。不要在残留状态上继续让AI“补救”。
|
||||
|
||||
## 图片或字体资源写入失败
|
||||
|
||||
`set_component_property`可接受资源UUID,也可直接接受`assets/...`路径。路径必须存在且资源类型与目标属性匹配;插件会在写入前解析并校验,不匹配时拒绝修改。
|
||||
|
||||
## 网页报错怎么读
|
||||
|
||||
使用`cocos_preview_debug_start`打开托管Chrome/Edge预览,不要把普通浏览器页面来回复制给AI。复现后调用`cocos_preview_debug_read`;可读取JavaScript异常、未处理Promise、控制台错误和失败网络请求。结束时调用`cocos_preview_debug_stop`。
|
||||
|
||||
## Android或iOS构建是否一定不受影响
|
||||
|
||||
不能只凭编辑器或网页错误下结论。`cocos_get_build_diagnostics`只整理Creator已有构建日志;仍需实际完成目标平台构建和设备测试。
|
||||
|
||||
## 反馈问题
|
||||
|
||||
邮箱:jimmymjing@gmail.com
|
||||
|
||||
请附Creator版本、插件版本、系统、AI客户端、一键检测结果、复现步骤和脱敏诊断。不要发送项目密钥、API Key或完整商业项目。
|
||||
+28014
File diff suppressed because it is too large
Load Diff
+5387
File diff suppressed because it is too large
Load Diff
Vendored
Executable
+390
@@ -0,0 +1,390 @@
|
||||
"use strict";
|
||||
|
||||
// packages/cocos-ext/src/panels/default/index.ts
|
||||
var LOCALE_STORAGE_KEY = "cocos-mcp-panel-locale";
|
||||
var STR = {
|
||||
zh: {
|
||||
panelTitle: "Cocos MCP \u63A7\u5236\u53F0",
|
||||
langLabel: "\u754C\u9762\u8BED\u8A00",
|
||||
server: "\u670D\u52A1",
|
||||
running: "\u8FD0\u884C\u4E2D",
|
||||
stopped: "\u5DF2\u505C\u6B62",
|
||||
port: "\u7AEF\u53E3",
|
||||
connections: "\u8FDE\u63A5\u6570",
|
||||
url: "\u9762\u677F URL\uFF08\u4EC5\u4F9B\u53C2\u8003\uFF09",
|
||||
startServer: "\u542F\u52A8\u670D\u52A1",
|
||||
stopServer: "\u505C\u6B62\u670D\u52A1",
|
||||
refresh: "\u5237\u65B0",
|
||||
runSelfTest: "\u4E00\u952E\u68C0\u6D4B",
|
||||
selfTestTitle: "\u8FDE\u63A5\u8BCA\u65AD",
|
||||
selfTestNever: "\u5C1A\u672A\u8FD0\u884C\u68C0\u6D4B",
|
||||
quickStartTitle: "\u9996\u6B21\u4F7F\u7528",
|
||||
quickStartStepServer: "\u542F\u52A8MCP\u670D\u52A1",
|
||||
quickStartStepConfig: "\u914D\u7F6E\u81F3\u5C11\u4E00\u4E2AMCP\u5BA2\u6237\u7AEF",
|
||||
quickStartStepConnect: "\u91CD\u542F\u5BA2\u6237\u7AEF\u5E76\u5EFA\u7ACB\u8FDE\u63A5",
|
||||
quickStartDone: "\u5B8C\u6210",
|
||||
quickStartPending: "\u5F85\u5B8C\u6210",
|
||||
openGuide: "\u6253\u5F00Quick Start",
|
||||
firstPromptLabel: "\u8FDE\u63A5\u6210\u529F\u540E\uFF0C\u5148\u8BA9AI\u8BFB\u53D6\u573A\u666F\uFF0C\u518D\u505A\u4E00\u4E2A\u5C0F\u6539\u52A8\u3002",
|
||||
autoConfigTitle: "\u4E00\u952E\u914D\u7F6E MCP \u5BA2\u6237\u7AEF",
|
||||
serverNamePh: "MCP \u670D\u52A1\u540D\uFF08\u5199\u5165\u5404\u5BA2\u6237\u7AEF\u914D\u7F6E\uFF09",
|
||||
autoConfigAll: "\u4E00\u952E\u914D\u7F6E\u5168\u90E8\u5BA2\u6237\u7AEF",
|
||||
configured: "\u5DF2\u914D\u7F6E",
|
||||
notConfigured: "\u672A\u914D\u7F6E",
|
||||
autoConfig: "\u4E00\u952E\u914D\u7F6E",
|
||||
openConfig: "\u6253\u5F00\u914D\u7F6E",
|
||||
cliTitle: "\u547D\u4EE4\u884C\u53C2\u8003\uFF08Codex / Gemini CLI\uFF09",
|
||||
operationLogs: "\u64CD\u4F5C\u65E5\u5FD7",
|
||||
clear: "\u6E05\u7A7A",
|
||||
logLevelInfo: "\u4FE1\u606F",
|
||||
logLevelWarn: "\u8B66\u544A",
|
||||
logLevelError: "\u9519\u8BEF"
|
||||
},
|
||||
en: {
|
||||
panelTitle: "Cocos MCP Console",
|
||||
langLabel: "Language",
|
||||
server: "Server",
|
||||
running: "Running",
|
||||
stopped: "Stopped",
|
||||
port: "port",
|
||||
connections: "Connections",
|
||||
url: "Panel URL (reference)",
|
||||
startServer: "Start Server",
|
||||
stopServer: "Stop Server",
|
||||
refresh: "Refresh",
|
||||
runSelfTest: "Run Self-Test",
|
||||
selfTestTitle: "Connection Diagnostics",
|
||||
selfTestNever: "Self-test has not run yet",
|
||||
quickStartTitle: "First Run",
|
||||
quickStartStepServer: "Start the MCP service",
|
||||
quickStartStepConfig: "Configure at least one MCP client",
|
||||
quickStartStepConnect: "Restart the client and connect",
|
||||
quickStartDone: "done",
|
||||
quickStartPending: "pending",
|
||||
openGuide: "Open Quick Start",
|
||||
firstPromptLabel: "After connecting, inspect the scene before one small mutation.",
|
||||
autoConfigTitle: "Auto Config MCP Clients",
|
||||
serverNamePh: "server name (written to each client config)",
|
||||
autoConfigAll: "Auto Config All",
|
||||
configured: "configured",
|
||||
notConfigured: "not configured",
|
||||
autoConfig: "Auto Config",
|
||||
openConfig: "Open Config",
|
||||
cliTitle: "CLI reference (Codex / Gemini CLI)",
|
||||
operationLogs: "Operation Logs",
|
||||
clear: "Clear",
|
||||
logLevelInfo: "info",
|
||||
logLevelWarn: "warn",
|
||||
logLevelError: "error"
|
||||
}
|
||||
};
|
||||
function readLocale() {
|
||||
try {
|
||||
const v = localStorage.getItem(LOCALE_STORAGE_KEY);
|
||||
if (v === "en" || v === "zh") {
|
||||
return v;
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
return "zh";
|
||||
}
|
||||
function writeLocale(l) {
|
||||
try {
|
||||
localStorage.setItem(LOCALE_STORAGE_KEY, l);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
var locale = readLocale();
|
||||
function t(key) {
|
||||
return STR[locale][key] ?? STR.en[key] ?? key;
|
||||
}
|
||||
var TEMPLATE = `
|
||||
<div class="wrap">
|
||||
<div class="row lang-bar">
|
||||
<span id="langLabel" class="lang-label"></span>
|
||||
<select id="langSelect" class="lang-select">
|
||||
<option value="zh">\u4E2D\u6587</option>
|
||||
<option value="en">English</option>
|
||||
</select>
|
||||
</div>
|
||||
<h2 id="panelTitle"></h2>
|
||||
<div class="card guide-card">
|
||||
<div class="row guide-heading">
|
||||
<h3 id="quickStartTitle"></h3>
|
||||
<ui-button id="openGuideBtn"></ui-button>
|
||||
</div>
|
||||
<div id="quickStartSteps" class="guide-steps"></div>
|
||||
<div id="firstPromptLabel" class="guide-note"></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div id="serverLine">-</div>
|
||||
<div id="connLine">-</div>
|
||||
<div id="urlLine">-</div>
|
||||
<div class="row">
|
||||
<ui-button id="startBtn"></ui-button>
|
||||
<ui-button id="stopBtn"></ui-button>
|
||||
<ui-button id="refreshBtn"></ui-button>
|
||||
<ui-button id="selfTestBtn"></ui-button>
|
||||
</div>
|
||||
<h3 id="selfTestTitle" class="self-test-title"></h3>
|
||||
<div id="selfTestResults" class="self-test-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3 id="autoConfigTitle"></h3>
|
||||
<div class="row">
|
||||
<ui-input id="serverNameInput"></ui-input>
|
||||
<ui-button id="addAllBtn"></ui-button>
|
||||
</div>
|
||||
<div id="clientList"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3 id="cliTitle"></h3>
|
||||
<div class="mono" id="claudeCmd"></div>
|
||||
<div class="mono" id="geminiCmd"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<h3 id="logsTitle"></h3>
|
||||
<ui-button id="clearLogsBtn"></ui-button>
|
||||
</div>
|
||||
<div id="logs" class="logs"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
var STYLE = `
|
||||
.wrap { padding: 12px; color: #d4d4d4; font-size: 13px; }
|
||||
.lang-bar { margin-bottom: 10px; }
|
||||
.lang-label { margin-right: 8px; color: #aaa; }
|
||||
.lang-select { background: #2d2d2d; color: #d4d4d4; border: 1px solid #444; border-radius: 4px; padding: 4px 8px; font-size: 13px; }
|
||||
h2 { margin: 0 0 10px 0; font-size: 18px; }
|
||||
h3 { margin: 0 0 8px 0; font-size: 14px; }
|
||||
.card { border: 1px solid #333; border-radius: 6px; padding: 10px; margin-bottom: 10px; background: #1e1e1e; }
|
||||
.row { display: flex; gap: 8px; align-items: center; margin-top: 8px; margin-bottom: 6px; }
|
||||
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #111; padding: 8px; border-radius: 4px; margin-bottom: 6px; word-break: break-all; }
|
||||
.logs { max-height: 220px; overflow: auto; background: #111; border-radius: 4px; padding: 8px; }
|
||||
.log { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; margin-bottom: 4px; white-space: pre-wrap; word-break: break-word; }
|
||||
.log.error { color: #ff8a8a; }
|
||||
.log.warn { color: #f7d774; }
|
||||
.log.info { color: #9bd4ff; }
|
||||
.client-row { display: grid; grid-template-columns: 110px 1fr auto auto; gap: 8px; align-items: center; margin: 6px 0; }
|
||||
.tag { padding: 2px 6px; border-radius: 4px; font-size: 12px; background: #333; }
|
||||
.ok { background: #1f5f2f; }
|
||||
.bad { background: #6a2a2a; }
|
||||
.guide-card { border-color: #315b4f; }
|
||||
.guide-heading { justify-content: space-between; margin-top: 0; }
|
||||
.guide-heading h3 { margin: 0; }
|
||||
.guide-steps { display: grid; gap: 6px; }
|
||||
.guide-step { display: grid; grid-template-columns: 68px 1fr; gap: 8px; align-items: center; }
|
||||
.guide-status { padding: 2px 6px; border-radius: 4px; font-size: 12px; text-align: center; background: #5c4622; }
|
||||
.guide-status.done { background: #1f5f2f; }
|
||||
.guide-note { margin-top: 8px; color: #a8c9bd; }
|
||||
.self-test-title { margin-top: 12px; }
|
||||
.self-test-results { display: grid; gap: 5px; }
|
||||
.self-test-row { display: grid; grid-template-columns: 22px 92px 1fr; gap: 6px; align-items: start; }
|
||||
.self-test-icon.ok-text { color: #63d28b; }
|
||||
.self-test-icon.bad-text { color: #ff8a8a; }
|
||||
.self-test-message { color: #aaa; word-break: break-word; }
|
||||
`;
|
||||
module.exports = Editor.Panel.define({
|
||||
template: TEMPLATE,
|
||||
style: STYLE,
|
||||
$: {
|
||||
langLabel: "#langLabel",
|
||||
langSelect: "#langSelect",
|
||||
panelTitle: "#panelTitle",
|
||||
quickStartTitle: "#quickStartTitle",
|
||||
quickStartSteps: "#quickStartSteps",
|
||||
openGuideBtn: "#openGuideBtn",
|
||||
firstPromptLabel: "#firstPromptLabel",
|
||||
serverLine: "#serverLine",
|
||||
connLine: "#connLine",
|
||||
urlLine: "#urlLine",
|
||||
startBtn: "#startBtn",
|
||||
stopBtn: "#stopBtn",
|
||||
refreshBtn: "#refreshBtn",
|
||||
selfTestBtn: "#selfTestBtn",
|
||||
selfTestTitle: "#selfTestTitle",
|
||||
selfTestResults: "#selfTestResults",
|
||||
autoConfigTitle: "#autoConfigTitle",
|
||||
serverNameInput: "#serverNameInput",
|
||||
addAllBtn: "#addAllBtn",
|
||||
clientList: "#clientList",
|
||||
cliTitle: "#cliTitle",
|
||||
claudeCmd: "#claudeCmd",
|
||||
geminiCmd: "#geminiCmd",
|
||||
logs: "#logs",
|
||||
logsTitle: "#logsTitle",
|
||||
clearLogsBtn: "#clearLogsBtn"
|
||||
},
|
||||
ready() {
|
||||
const pluginName = "cocos-mcp";
|
||||
locale = readLocale();
|
||||
const readServerName = () => {
|
||||
const input = this.$.serverNameInput;
|
||||
const v = (input?.value ?? "").trim();
|
||||
return v || "cocos-creator";
|
||||
};
|
||||
const applyStaticLabels = () => {
|
||||
this.$.langLabel.textContent = t("langLabel");
|
||||
this.$.panelTitle.textContent = t("panelTitle");
|
||||
this.$.quickStartTitle.textContent = t("quickStartTitle");
|
||||
this.$.openGuideBtn.textContent = t("openGuide");
|
||||
this.$.firstPromptLabel.textContent = t("firstPromptLabel");
|
||||
this.$.autoConfigTitle.textContent = t("autoConfigTitle");
|
||||
this.$.cliTitle.textContent = t("cliTitle");
|
||||
this.$.logsTitle.textContent = t("operationLogs");
|
||||
this.$.startBtn.textContent = t("startServer");
|
||||
this.$.stopBtn.textContent = t("stopServer");
|
||||
this.$.refreshBtn.textContent = t("refresh");
|
||||
this.$.selfTestBtn.textContent = t("runSelfTest");
|
||||
this.$.selfTestTitle.textContent = t("selfTestTitle");
|
||||
this.$.addAllBtn.textContent = t("autoConfigAll");
|
||||
this.$.clearLogsBtn.textContent = t("clear");
|
||||
const input = this.$.serverNameInput;
|
||||
if (input) {
|
||||
input.placeholder = t("serverNamePh");
|
||||
}
|
||||
const sel = this.$.langSelect;
|
||||
if (sel) {
|
||||
sel.value = locale;
|
||||
}
|
||||
};
|
||||
const logLevelLabel = (level) => {
|
||||
if (level === "error") {
|
||||
return t("logLevelError");
|
||||
}
|
||||
if (level === "warn") {
|
||||
return t("logLevelWarn");
|
||||
}
|
||||
return t("logLevelInfo");
|
||||
};
|
||||
const render = (state) => {
|
||||
const run = state.server.running ? t("running") : t("stopped");
|
||||
this.$.serverLine.textContent = `${t("server")}: ${run} (${t("port")}: ${state.server.port ?? "-"})`;
|
||||
this.$.connLine.textContent = `${t("connections")}: ${state.server.connections}`;
|
||||
this.$.urlLine.textContent = `${t("url")}: ${state.server.url ?? "-"}`;
|
||||
this.$.claudeCmd.textContent = state.cliCommands.claude;
|
||||
this.$.geminiCmd.textContent = state.cliCommands.gemini;
|
||||
const selfTestResults = this.$.selfTestResults;
|
||||
selfTestResults.innerHTML = "";
|
||||
if (!state.selfTest) {
|
||||
selfTestResults.textContent = t("selfTestNever");
|
||||
} else {
|
||||
for (const check of state.selfTest.checks) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "self-test-row";
|
||||
const icon = document.createElement("span");
|
||||
icon.className = `self-test-icon ${check.ok ? "ok-text" : "bad-text"}`;
|
||||
icon.textContent = check.ok ? "OK" : "X";
|
||||
const id = document.createElement("strong");
|
||||
id.textContent = check.id;
|
||||
const message = document.createElement("span");
|
||||
message.className = "self-test-message";
|
||||
message.textContent = check.message;
|
||||
row.append(icon, id, message);
|
||||
selfTestResults.appendChild(row);
|
||||
}
|
||||
}
|
||||
const guideSteps = this.$.quickStartSteps;
|
||||
guideSteps.innerHTML = "";
|
||||
const onboardingSteps = [
|
||||
{ label: t("quickStartStepServer"), done: state.server.running },
|
||||
{ label: t("quickStartStepConfig"), done: state.clients.some((client) => client.configured) },
|
||||
{ label: t("quickStartStepConnect"), done: state.server.connections > 0 }
|
||||
];
|
||||
for (const step of onboardingSteps) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "guide-step";
|
||||
const status = document.createElement("span");
|
||||
status.className = `guide-status${step.done ? " done" : ""}`;
|
||||
status.textContent = step.done ? t("quickStartDone") : t("quickStartPending");
|
||||
const label = document.createElement("span");
|
||||
label.textContent = step.label;
|
||||
row.append(status, label);
|
||||
guideSteps.appendChild(row);
|
||||
}
|
||||
const list = this.$.clientList;
|
||||
list.innerHTML = "";
|
||||
for (const c of state.clients) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "client-row";
|
||||
const tagText = c.configured ? t("configured") : t("notConfigured");
|
||||
const tagClass = c.configured ? "ok" : "bad";
|
||||
row.innerHTML = `
|
||||
<strong>${c.name}</strong>
|
||||
<span title="${c.configPath}">${c.configPath}</span>
|
||||
<span class="tag ${tagClass}">${tagText}</span>
|
||||
<div>
|
||||
<ui-button class="cfg-btn" data-client="${c.clientType}">${t("autoConfig")}</ui-button>
|
||||
<ui-button class="open-btn" data-client="${c.clientType}">${t("openConfig")}</ui-button>
|
||||
</div>
|
||||
`;
|
||||
list.appendChild(row);
|
||||
}
|
||||
list.querySelectorAll(".cfg-btn").forEach((el) => {
|
||||
el.addEventListener("confirm", async () => {
|
||||
const clientType = el.getAttribute("data-client");
|
||||
await Editor.Message.request(pluginName, "add-mcp-to-client", clientType, readServerName());
|
||||
await refresh();
|
||||
});
|
||||
});
|
||||
list.querySelectorAll(".open-btn").forEach((el) => {
|
||||
el.addEventListener("confirm", async () => {
|
||||
const clientType = el.getAttribute("data-client");
|
||||
await Editor.Message.request(pluginName, "open-client-config", clientType);
|
||||
});
|
||||
});
|
||||
const logs = this.$.logs;
|
||||
logs.innerHTML = "";
|
||||
for (const l of state.logs.slice().reverse()) {
|
||||
const line = document.createElement("div");
|
||||
line.className = `log ${l.level}`;
|
||||
line.textContent = `${l.ts} [${logLevelLabel(l.level)}] ${l.message}`;
|
||||
logs.appendChild(line);
|
||||
}
|
||||
};
|
||||
const refresh = async () => {
|
||||
applyStaticLabels();
|
||||
const state = await Editor.Message.request(pluginName, "get-mcp-panel-state", readServerName());
|
||||
render(state);
|
||||
};
|
||||
this.$.langSelect?.addEventListener("change", () => {
|
||||
const sel = this.$.langSelect;
|
||||
const v = sel?.value === "en" ? "en" : "zh";
|
||||
locale = v;
|
||||
writeLocale(v);
|
||||
refresh().catch((e) => console.error("[cocos-mcp panel] refresh after locale", e));
|
||||
});
|
||||
this.$.startBtn?.addEventListener("confirm", async () => {
|
||||
await Editor.Message.request(pluginName, "start-mcp-server");
|
||||
await refresh();
|
||||
});
|
||||
this.$.stopBtn?.addEventListener("confirm", async () => {
|
||||
await Editor.Message.request(pluginName, "stop-mcp-server");
|
||||
await refresh();
|
||||
});
|
||||
this.$.refreshBtn?.addEventListener("confirm", refresh);
|
||||
this.$.selfTestBtn?.addEventListener("confirm", async () => {
|
||||
await Editor.Message.request(pluginName, "run-mcp-self-test", readServerName());
|
||||
await refresh();
|
||||
});
|
||||
this.$.openGuideBtn?.addEventListener("confirm", async () => {
|
||||
await Editor.Message.request(pluginName, "open-user-guide");
|
||||
});
|
||||
this.$.addAllBtn?.addEventListener("confirm", async () => {
|
||||
await Editor.Message.request(pluginName, "add-mcp-to-all-clients", readServerName());
|
||||
await refresh();
|
||||
});
|
||||
this.$.clearLogsBtn?.addEventListener("confirm", async () => {
|
||||
await Editor.Message.request(pluginName, "clear-panel-logs");
|
||||
await refresh();
|
||||
});
|
||||
this.$.serverNameInput?.addEventListener("change", refresh);
|
||||
refresh().catch((e) => {
|
||||
console.error("[cocos-mcp panel] initial refresh failed", e);
|
||||
});
|
||||
}
|
||||
});
|
||||
+3053
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
description: 'stdio MCP bridge: WebSocket on Main, scene script for scene read/write (local-first)',
|
||||
panel_title: 'Cocos MCP Server',
|
||||
menu_path: 'Extension/Cocos MCP',
|
||||
open_panel: 'Open MCP Panel',
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
description: 'stdio MCP 桥接:Main 进程 WebSocket,Scene 脚本执行场景读写(本机优先)',
|
||||
panel_title: 'Cocos MCP 服务器',
|
||||
menu_path: '扩展/Cocos MCP',
|
||||
open_panel: '打开 MCP 面板',
|
||||
};
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "cocos-mcp",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cocos-mcp",
|
||||
"version": "0.2.0",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"ws": "^8.21.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.21.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
|
||||
"integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"$schema": "./@types/schema/package/index.json",
|
||||
"package_version": 2,
|
||||
"name": "cocos-mcp",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"author": "",
|
||||
"editor": ">=3.8.0",
|
||||
"description": "i18n:cocos-mcp.description",
|
||||
"main": "./dist/main.js",
|
||||
"panels": {
|
||||
"default": {
|
||||
"title": "i18n:cocos-mcp.panel_title",
|
||||
"type": "dockable",
|
||||
"main": "dist/panels/default/index",
|
||||
"size": {
|
||||
"min-width": 560,
|
||||
"min-height": 420,
|
||||
"width": 920,
|
||||
"height": 760
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": "^8.21.1"
|
||||
},
|
||||
"contributions": {
|
||||
"menu": [
|
||||
{
|
||||
"path": "i18n:cocos-mcp.menu_path",
|
||||
"label": "i18n:cocos-mcp.open_panel",
|
||||
"message": "open-panel"
|
||||
}
|
||||
],
|
||||
"messages": {
|
||||
"open-panel": {
|
||||
"methods": [
|
||||
"openPanel"
|
||||
]
|
||||
},
|
||||
"open-user-guide": {
|
||||
"methods": [
|
||||
"openUserGuide"
|
||||
]
|
||||
},
|
||||
"start-mcp-server": {
|
||||
"methods": [
|
||||
"startMcpServer"
|
||||
]
|
||||
},
|
||||
"stop-mcp-server": {
|
||||
"methods": [
|
||||
"stopMcpServer"
|
||||
]
|
||||
},
|
||||
"get-mcp-server-status": {
|
||||
"methods": [
|
||||
"getMcpServerStatus"
|
||||
]
|
||||
},
|
||||
"get-mcp-panel-state": {
|
||||
"methods": [
|
||||
"getMcpPanelState"
|
||||
]
|
||||
},
|
||||
"run-mcp-self-test": {
|
||||
"methods": [
|
||||
"runMcpSelfTest"
|
||||
]
|
||||
},
|
||||
"add-mcp-to-client": {
|
||||
"methods": [
|
||||
"addMcpToClient"
|
||||
]
|
||||
},
|
||||
"add-mcp-to-all-clients": {
|
||||
"methods": [
|
||||
"addMcpToAllClients"
|
||||
]
|
||||
},
|
||||
"open-client-config": {
|
||||
"methods": [
|
||||
"openClientConfig"
|
||||
]
|
||||
},
|
||||
"clear-panel-logs": {
|
||||
"methods": [
|
||||
"clearPanelLogs"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scene": {
|
||||
"script": "./dist/scene.js",
|
||||
"methods": [
|
||||
"ping",
|
||||
"getCurrentSceneMeta",
|
||||
"getHierarchy",
|
||||
"searchNodes",
|
||||
"inspectNode",
|
||||
"settleUiNodes",
|
||||
"verifyUiNode",
|
||||
"prepareSetNodeActive",
|
||||
"prepareSetNodeTransform",
|
||||
"prepareSetNodeName",
|
||||
"prepareCreateNode",
|
||||
"prepareDeleteNode",
|
||||
"prepareReparentNode",
|
||||
"prepareSetNodeSiblingIndex",
|
||||
"prepareInstantiatePrefab",
|
||||
"prepareAddComponent",
|
||||
"prepareRemoveComponent",
|
||||
"prepareDuplicateNode",
|
||||
"listChildUuidsUnderParent",
|
||||
"duplicateNodeFallback",
|
||||
"removeLastComponentByType",
|
||||
"addLastComponentByType",
|
||||
"getComponentStatsByType",
|
||||
"getComponentUuidState",
|
||||
"setSiblingIndexFallback",
|
||||
"setComponentScalarPropertyDirect",
|
||||
"getComponentAssetRefUuid",
|
||||
"setComponentAssetRefDirect",
|
||||
"setComponentAssetRefFromScene",
|
||||
"clearComponentAssetRef",
|
||||
"getNodeActiveState",
|
||||
"registerNidForNewNode",
|
||||
"forgetNodeNid",
|
||||
"forgetNidsInSubtree",
|
||||
"snapshotNidMappingsInSubtree",
|
||||
"detachNodeForMcpUndo",
|
||||
"restoreDetachedNodeForMcpUndo",
|
||||
"discardDetachedNodesForMcpUndo",
|
||||
"removeCreatedNodesForMcpUndo",
|
||||
"forgetNidMappingsByUuids",
|
||||
"restoreNidMapping",
|
||||
"prepareSetComponentProperty"
|
||||
]
|
||||
}
|
||||
},
|
||||
"_storeId": "9854cb1796e2aa8cffeeacfc506ab17d"
|
||||
}
|
||||
Reference in New Issue
Block a user