chore(mcp): 换用 cocos-mcp-server 扩展, .mcp.json 改 HTTP 直连

旧扩展 @iflow-mcp/harady-cocos-creator-mcp(164 个细粒度工具 + stdio-bridge
转发)整个删除, 换成 cocos-mcp-server v1.7.9(16 个聚合工具, 靠 action 分派)。

新扩展自身起 streamable-http 服务, 不再需要 stdio 桥, 故 .mcp.json 从
node + stdio-bridge.js 改为 { type: "http", url: "http://127.0.0.1:3000/mcp" }。
原配置指向的 client/stdio-bridge.js 已随旧扩展删除, MCP 因此加载失败。

服务器名保留 cocos-creator-mcp(而非 INSTALL.md 建议的 cocos-creator), 以免
打断 CLAUDE.md、cocos-mcp 技能与 settings.local.json 里的
mcp__cocos-creator-mcp__* 允许规则。

gitignore: 新扩展是商业付费产品(vberai), 非 npm 可重装, dist/sidecar/static
共约 16M 发行产物+加密载荷+平台二进制不入库; 顺带忽略 Claude Code 个人本地
配置与编辑器运行时端口文件。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-19 08:01:07 +08:00
co-authored by Claude Opus 5
parent 4b4e2261b4
commit 42bc0722b8
67 changed files with 3553 additions and 10913 deletions
@@ -0,0 +1,94 @@
#!/usr/bin/env node
/**
* Batch auto_knowledge via MCP HTTP — gets runtime defaults from editor
* Requires: editor running with MCP on port 3000
*/
import http from 'http';
const MCP_URL = 'http://127.0.0.1:3000/mcp';
const ALL = [
'cc.Label','cc.Sprite','cc.Button','cc.Widget','cc.Layout',
'cc.UITransform','cc.RichText','cc.ScrollView','cc.Toggle',
'cc.ToggleContainer','cc.ProgressBar','cc.Slider','cc.PageView',
'cc.Mask','cc.Graphics','cc.UIOpacity','cc.SafeArea',
'cc.BlockInputEvents','cc.LabelOutline','cc.LabelShadow',
'cc.PageViewIndicator','cc.EditBox',
'cc.AudioSource','cc.VideoPlayer','cc.WebView',
'cc.ParticleSystem2D',
'cc.Camera','cc.Canvas','cc.Animation','cc.SkeletalAnimation',
'cc.MeshRenderer','cc.SkinnedMeshRenderer',
'cc.DirectionalLight','cc.PointLight','cc.SpotLight','cc.SphereLight',
'cc.RigidBody2D','cc.BoxCollider2D','cc.CircleCollider2D','cc.PolygonCollider2D',
'cc.RigidBody','cc.BoxCollider','cc.SphereCollider','cc.CapsuleCollider','cc.MeshCollider',
'cc.MotionStreak','cc.Billboard','cc.Line',
'cc.TiledMap','cc.TiledLayer',
'cc.HingeJoint2D','cc.DistanceJoint2D','cc.SpringJoint2D',
'cc.FixedJoint2D','cc.WheelJoint2D','cc.SliderJoint2D',
'cc.RelativeJoint2D','cc.MouseJoint2D',
'cc.HingeConstraint','cc.FixedConstraint','cc.PointToPointConstraint',
'cc.Terrain','cc.LODGroup','cc.ReflectionProbe',
'sp.Skeleton','dragonBones.ArmatureDisplay',
];
function callMcp(toolName, args) {
return new Promise((resolve, reject) => {
const body = JSON.stringify({
jsonrpc: '2.0', id: Date.now(), method: 'tools/call',
params: { name: toolName, arguments: args }
});
const req = http.request(MCP_URL, {
method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
timeout: 30000,
}, (res) => {
const chunks = [];
res.on('data', c => chunks.push(c));
res.on('end', () => {
try {
const data = JSON.parse(Buffer.concat(chunks).toString());
const content = data.result?.content || [];
const text = content.find(c => c.type === 'text')?.text;
resolve(text ? JSON.parse(text) : {});
} catch (e) { reject(e); }
});
});
req.on('error', reject);
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
req.write(body);
req.end();
});
}
// Check connectivity
try {
await callMcp('cocos_editor', { action: 'project_info' });
} catch {
console.error('❌ Cannot connect to MCP at', MCP_URL);
console.error(' Make sure editor is running and plugin is loaded.');
process.exit(1);
}
console.log(`Connected. Running auto_knowledge(save:true) for ${ALL.length} components...\n`);
let ok = 0, fail = 0, withDefaults = 0;
for (const comp of ALL) {
try {
const r = await callMcp('cocos_devtools', { action: 'auto_knowledge', componentType: comp, save: true });
if (r.success) {
ok++;
const hasRuntime = r.data?.hasRuntimeDefaults;
if (hasRuntime) withDefaults++;
const propCount = Object.keys(r.data?.recommendedKnowledge?.properties || {}).length;
console.log(` ✅ ${comp.padEnd(35)} ${propCount} props ${hasRuntime ? '(+defaults)' : '(dts only)'}`);
} else {
fail++;
console.log(` ❌ ${comp}: ${r.error}`);
}
} catch (e) {
fail++;
console.log(` ❌ ${comp}: ${e.message}`);
}
}
console.log(`\nDone: ${ok} ok, ${fail} fail, ${withDefaults} with runtime defaults`);
@@ -0,0 +1,83 @@
/**
* Batch auto_knowledge — generate complete COMPONENT_PROPERTIES from d.ts
* Output: knowledge JSON files + combined TS snippet for knowledge-handler.ts
*/
const path = require('path');
const fs = require('fs');
const mod = require(path.resolve(__dirname, '../dist/tools/cocos/utils/component-knowledge'));
const ALL_COMPONENTS = [
'cc.Label', 'cc.Sprite', 'cc.Button', 'cc.Widget', 'cc.Layout',
'cc.UITransform', 'cc.RichText', 'cc.ScrollView', 'cc.Toggle',
'cc.ToggleContainer', 'cc.ProgressBar', 'cc.Slider', 'cc.PageView',
'cc.Mask', 'cc.Graphics', 'cc.UIOpacity', 'cc.SafeArea',
'cc.BlockInputEvents', 'cc.LabelOutline', 'cc.LabelShadow',
'cc.PageViewIndicator', 'cc.EditBox',
'cc.AudioSource', 'cc.VideoPlayer', 'cc.WebView',
'cc.ParticleSystem2D', 'cc.ParticleSystem',
'cc.Camera', 'cc.Canvas', 'cc.Animation', 'cc.SkeletalAnimation',
'cc.MeshRenderer', 'cc.SkinnedMeshRenderer',
'cc.DirectionalLight', 'cc.PointLight', 'cc.SpotLight', 'cc.SphereLight',
'cc.RigidBody2D', 'cc.BoxCollider2D', 'cc.CircleCollider2D', 'cc.PolygonCollider2D',
'cc.RigidBody', 'cc.BoxCollider', 'cc.SphereCollider', 'cc.CapsuleCollider', 'cc.MeshCollider',
'cc.MotionStreak', 'cc.Billboard', 'cc.Line',
'cc.TiledMap', 'cc.TiledLayer',
'cc.HingeJoint2D', 'cc.DistanceJoint2D', 'cc.SpringJoint2D',
'cc.FixedJoint2D', 'cc.WheelJoint2D', 'cc.SliderJoint2D',
'cc.RelativeJoint2D', 'cc.MouseJoint2D',
'cc.HingeConstraint', 'cc.FixedConstraint', 'cc.PointToPointConstraint',
'cc.Terrain', 'cc.LODGroup', 'cc.ReflectionProbe',
'sp.Skeleton', 'dragonBones.ArmatureDisplay',
];
const snippets = [];
let saved = 0, failed = 0;
for (const comp of ALL_COMPONENTS) {
const className = mod.componentTypeToClassName(comp);
const dtsInfo = mod.parseDtsClass(className, { recursive: true });
if (!dtsInfo) {
console.log(` ❌ ${comp}: not found in d.ts`);
failed++;
continue;
}
const classified = mod.classifyProperties(dtsInfo, null);
const knowledge = mod.buildRecommendedKnowledge(comp, dtsInfo, classified);
// Save knowledge file
mod.saveKnowledgeFile(comp, knowledge);
// Generate TS snippet
const snippet = mod.generateTsSnippet(comp, knowledge);
snippets.push(snippet);
const propCount = Object.keys(knowledge.properties).length;
console.log(` ✅ ${comp}: ${propCount} props`);
saved++;
}
// Write combined snippets to file
const outputPath = path.resolve(__dirname, '../dev-knowledge/COMPONENT_PROPERTIES_GENERATED.ts');
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
const header = `/**
* AUTO-GENERATED by batch-auto-knowledge.js
* Date: ${new Date().toISOString()}
* Components: ${saved}
*
* Paste this into COMPONENT_PROPERTIES in knowledge-handler.ts
*/
const COMPONENT_PROPERTIES: Record<string, any> = {\n`;
const footer = `\n};\n`;
fs.writeFileSync(outputPath, header + snippets.join('\n') + footer, 'utf-8');
console.log(`\n Saved: ${saved} | Failed: ${failed}`);
console.log(` Knowledge files: dev-knowledge/components/`);
console.log(` TS output: ${outputPath}`);
@@ -0,0 +1,111 @@
const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const PATH = {
packageJSON: path.join(__dirname, "../package.json")
};
function checkCreatorTypesVersion(version) {
try {
// 根据平台选择合适的npm命令
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
// 检查npm命令是否可用
const npmCheck = spawnSync(npmCmd, ["--version"], {
stdio: 'pipe',
shell: process.platform === "win32"
});
if (npmCheck.error || npmCheck.status !== 0) {
console.warn("Warning: npm command not available, skipping version check");
return true; // 如果npm不可用,跳过检查
}
// 获取版本列表
const result = spawnSync(npmCmd, ["view", "@cocos/creator-types", "versions"], {
stdio: 'pipe',
shell: process.platform === "win32"
});
if (result.error || result.status !== 0) {
console.warn("Warning: Failed to fetch @cocos/creator-types versions, skipping check");
return true; // 如果获取失败,跳过检查
}
let output = result.stdout.toString().trim();
// 尝试解析JSON
try {
const versions = JSON.parse(output);
if (Array.isArray(versions)) {
return versions.includes(version);
} else if (typeof versions === 'string') {
return versions.includes(version);
}
} catch (parseError) {
// 如果JSON解析失败,尝试作为字符串处理
return output.includes(version);
}
return false;
} catch (error) {
console.warn("Warning: Version check failed:", error.message);
return true; // 出错时跳过检查
}
}
function getCreatorTypesVersion() {
try {
// 检查package.json文件是否存在
if (!fs.existsSync(PATH.packageJSON)) {
console.warn("Warning: package.json not found");
return null;
}
const packageContent = fs.readFileSync(PATH.packageJSON, "utf8");
const packageJson = JSON.parse(packageContent);
// 检查devDependencies是否存在
if (!packageJson.devDependencies || !packageJson.devDependencies["@cocos/creator-types"]) {
console.warn("Warning: @cocos/creator-types not found in devDependencies");
return null;
}
const versionString = packageJson.devDependencies["@cocos/creator-types"];
return versionString.replace(/^[^\d]+/, "");
} catch (error) {
console.warn("Warning: Failed to read package.json:", error.message);
return null;
}
}
function main() {
try {
const creatorTypesVersion = getCreatorTypesVersion();
if (!creatorTypesVersion) {
console.log("Skipping @cocos/creator-types version check");
return;
}
if (!checkCreatorTypesVersion(creatorTypesVersion)) {
console.log("\x1b[33mWarning:\x1b[0m");
console.log(" @en");
console.log(" Version check of @cocos/creator-types failed.");
console.log(` The definition of ${creatorTypesVersion} has not been released yet. Please export the definition to the ./node_modules directory by selecting "Developer -> Export Interface Definition" in the menu of the Creator editor.`);
console.log(" The definition of the corresponding version will be released on npm after the editor is officially released.");
console.log(" @zh");
console.log(" @cocos/creator-types 版本检查失败。");
console.log(` ${creatorTypesVersion} 定义还未发布,请先通过 Creator 编辑器菜单 "开发者 -> 导出接口定义",导出定义到 ./node_modules 目录。`);
console.log(" 对应版本的定义会在编辑器正式发布后同步发布到 npm 上。");
}
} catch (error) {
console.error("Preinstall script error:", error.message);
// 不要抛出错误,让安装继续进行
process.exit(0);
}
}
// 执行主函数
main();
@@ -0,0 +1,25 @@
/**
* Generate COMPONENT_PROPERTIES from saved dev-knowledge JSON files.
* Does NOT re-parse d.ts — uses the saved knowledge (which has runtime defaults).
*/
const path = require('path');
const fs = require('fs');
const mod = require(path.resolve(__dirname, '../dist/tools/cocos/utils/component-knowledge'));
const files = mod.listKnowledgeFiles();
const snippets = [];
for (const comp of files) {
const knowledge = mod.loadKnowledgeFile(comp);
if (!knowledge) continue;
snippets.push(mod.generateTsSnippet(comp, knowledge));
const propCount = Object.keys(knowledge.properties).length;
const defaultCount = Object.values(knowledge.properties).filter(p => p.default !== undefined).length;
console.log(` ${comp.padEnd(35)} ${propCount} props, ${defaultCount} with defaults`);
}
const outputPath = path.resolve(__dirname, '../dev-knowledge/COMPONENT_PROPERTIES_GENERATED.txt');
const header = `const COMPONENT_PROPERTIES: Record<string, any> = {\n`;
const footer = `\n};\n`;
fs.writeFileSync(outputPath, header + snippets.join('\n') + footer, 'utf-8');
console.log(`\nGenerated ${files.length} components → ${outputPath}`);
@@ -0,0 +1,126 @@
/**
* validate-schemas.js
* 构建后校验所有 MCP 工具的 inputSchema,检测常见 JSON Schema 错误:
* - array 类型缺少 items
* - object 类型缺少 properties
* - enum 为空数组
* - type 字段缺失
*/
const path = require('path');
const fs = require('fs');
// 颜色
const RED = '\x1b[31m';
const YELLOW = '\x1b[33m';
const GREEN = '\x1b[32m';
const RESET = '\x1b[0m';
let errors = 0;
let warnings = 0;
function validateSchema(schema, context, toolName) {
if (!schema || typeof schema !== 'object') return;
// 检查 array 缺少 items
if (schema.type === 'array' && !schema.items) {
console.error(`${RED}ERROR${RESET} [${toolName}] ${context}: type "array" missing "items"`);
errors++;
}
// 检查 items 但不是 array
if (schema.items && schema.type !== 'array') {
console.warn(`${YELLOW}WARN${RESET} [${toolName}] ${context}: has "items" but type is "${schema.type}", not "array"`);
warnings++;
}
// 检查 enum 为空
if (schema.enum && Array.isArray(schema.enum) && schema.enum.length === 0) {
console.warn(`${YELLOW}WARN${RESET} [${toolName}] ${context}: empty "enum" array`);
warnings++;
}
// 递归检查 properties
if (schema.properties && typeof schema.properties === 'object') {
for (const [key, value] of Object.entries(schema.properties)) {
validateSchema(value, `${context}.${key}`, toolName);
}
}
// 递归检查 items
if (schema.items && typeof schema.items === 'object') {
validateSchema(schema.items, `${context}.items`, toolName);
}
}
function main() {
const distDir = path.join(__dirname, '..', 'dist');
if (!fs.existsSync(distDir)) {
console.error(`${RED}ERROR${RESET} dist/ not found. Run "npm run build" first.`);
process.exit(1);
}
// 收集所有工具定义
const tools = [];
// 1. 加载 CocosTools
try {
const { CocosTools } = require(path.join(distDir, 'tools', 'cocos', 'cocos-tools.js'));
const cocosTools = new CocosTools();
tools.push(...cocosTools.getTools());
} catch (e) {
console.warn(`${YELLOW}WARN${RESET} Could not load CocosTools: ${e.message}`);
}
// 2. 加载独立工具集
const toolFiles = [
'tools/scene-tools.js',
'tools/node-tools.js',
'tools/component-tools.js',
'tools/prefab-tools.js',
'tools/project-tools.js',
'tools/scene-view-tools.js',
'tools/reference-image-tools.js',
'tools/preferences-tools.js',
'tools/asset-advanced-tools.js',
'tools/debug-tools.js',
'tools/server-tools.js',
];
for (const file of toolFiles) {
try {
const fullPath = path.join(distDir, file);
if (!fs.existsSync(fullPath)) continue;
const mod = require(fullPath);
// 找到导出的类并实例化
for (const [, ExportedClass] of Object.entries(mod)) {
if (typeof ExportedClass === 'function' && ExportedClass.prototype && typeof ExportedClass.prototype.getTools === 'function') {
const instance = new ExportedClass();
tools.push(...instance.getTools());
}
}
} catch (e) {
console.warn(`${YELLOW}WARN${RESET} Could not load ${file}: ${e.message}`);
}
}
console.log(`\nValidating ${tools.length} tool schemas...\n`);
for (const tool of tools) {
if (tool.inputSchema) {
validateSchema(tool.inputSchema, 'inputSchema', tool.name);
}
}
console.log('');
if (errors > 0) {
console.error(`${RED}FAILED${RESET}: ${errors} error(s), ${warnings} warning(s)`);
process.exit(1);
} else if (warnings > 0) {
console.log(`${YELLOW}PASSED${RESET} with ${warnings} warning(s)`);
} else {
console.log(`${GREEN}PASSED${RESET}: All schemas valid`);
}
}
main();