Skip to main content
Glama
DaxianLee

Cocos Creator MCP Server Plugin

by DaxianLee
scene-tools.ts18 kB
import { ToolDefinition, ToolResponse, ToolExecutor, SceneInfo } from '../types'; export class SceneTools implements ToolExecutor { getTools(): ToolDefinition[] { return [ { name: 'get_current_scene', description: 'Get current scene information', inputSchema: { type: 'object', properties: {} } }, { name: 'get_scene_list', description: 'Get all scenes in the project', inputSchema: { type: 'object', properties: {} } }, { name: 'open_scene', description: 'Open a scene by path', inputSchema: { type: 'object', properties: { scenePath: { type: 'string', description: 'The scene file path' } }, required: ['scenePath'] } }, { name: 'save_scene', description: 'Save current scene', inputSchema: { type: 'object', properties: {} } }, { name: 'create_scene', description: 'Create a new scene asset', inputSchema: { type: 'object', properties: { sceneName: { type: 'string', description: 'Name of the new scene' }, savePath: { type: 'string', description: 'Path to save the scene (e.g., db://assets/scenes/NewScene.scene)' } }, required: ['sceneName', 'savePath'] } }, { name: 'save_scene_as', description: 'Save scene as new file', inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Path to save the scene' } }, required: ['path'] } }, { name: 'close_scene', description: 'Close current scene', inputSchema: { type: 'object', properties: {} } }, { name: 'get_scene_hierarchy', description: 'Get the complete hierarchy of current scene', inputSchema: { type: 'object', properties: { includeComponents: { type: 'boolean', description: 'Include component information', default: false } } } } ]; } async execute(toolName: string, args: any): Promise<ToolResponse> { switch (toolName) { case 'get_current_scene': return await this.getCurrentScene(); case 'get_scene_list': return await this.getSceneList(); case 'open_scene': return await this.openScene(args.scenePath); case 'save_scene': return await this.saveScene(); case 'create_scene': return await this.createScene(args.sceneName, args.savePath); case 'save_scene_as': return await this.saveSceneAs(args.path); case 'close_scene': return await this.closeScene(); case 'get_scene_hierarchy': return await this.getSceneHierarchy(args.includeComponents); default: throw new Error(`Unknown tool: ${toolName}`); } } private async getCurrentScene(): Promise<ToolResponse> { return new Promise((resolve) => { // 直接使用 query-node-tree 来获取场景信息(这个方法已经验证可用) Editor.Message.request('scene', 'query-node-tree').then((tree: any) => { if (tree && tree.uuid) { resolve({ success: true, data: { name: tree.name || 'Current Scene', uuid: tree.uuid, type: tree.type || 'cc.Scene', active: tree.active !== undefined ? tree.active : true, nodeCount: tree.children ? tree.children.length : 0 } }); } else { resolve({ success: false, error: 'No scene data available' }); } }).catch((err: Error) => { // 备用方案:使用场景脚本 const options = { name: 'cocos-mcp-server', method: 'getCurrentSceneInfo', args: [] }; Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { resolve(result); }).catch((err2: Error) => { resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); }); }); }); } private async getSceneList(): Promise<ToolResponse> { return new Promise((resolve) => { // Note: query-assets API corrected with proper parameters Editor.Message.request('asset-db', 'query-assets', { pattern: 'db://assets/**/*.scene' }).then((results: any[]) => { const scenes: SceneInfo[] = results.map(asset => ({ name: asset.name, path: asset.url, uuid: asset.uuid })); resolve({ success: true, data: scenes }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async openScene(scenePath: string): Promise<ToolResponse> { return new Promise((resolve) => { // 首先获取场景的UUID Editor.Message.request('asset-db', 'query-uuid', scenePath).then((uuid: string | null) => { if (!uuid) { throw new Error('Scene not found'); } // 使用正确的 scene API 打开场景 (需要UUID) return Editor.Message.request('scene', 'open-scene', uuid); }).then(() => { resolve({ success: true, message: `Scene opened: ${scenePath}` }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async saveScene(): Promise<ToolResponse> { return new Promise((resolve) => { Editor.Message.request('scene', 'save-scene').then(() => { resolve({ success: true, message: 'Scene saved successfully' }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async createScene(sceneName: string, savePath: string): Promise<ToolResponse> { return new Promise((resolve) => { // 确保路径以.scene结尾 const fullPath = savePath.endsWith('.scene') ? savePath : `${savePath}/${sceneName}.scene`; // 使用正确的Cocos Creator 3.8场景格式 const sceneContent = JSON.stringify([ { "__type__": "cc.SceneAsset", "_name": sceneName, "_objFlags": 0, "__editorExtras__": {}, "_native": "", "scene": { "__id__": 1 } }, { "__type__": "cc.Scene", "_name": sceneName, "_objFlags": 0, "__editorExtras__": {}, "_parent": null, "_children": [], "_active": true, "_components": [], "_prefab": null, "_lpos": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 }, "_lrot": { "__type__": "cc.Quat", "x": 0, "y": 0, "z": 0, "w": 1 }, "_lscale": { "__type__": "cc.Vec3", "x": 1, "y": 1, "z": 1 }, "_mobility": 0, "_layer": 1073741824, "_euler": { "__type__": "cc.Vec3", "x": 0, "y": 0, "z": 0 }, "autoReleaseAssets": false, "_globals": { "__id__": 2 }, "_id": "scene" }, { "__type__": "cc.SceneGlobals", "ambient": { "__id__": 3 }, "skybox": { "__id__": 4 }, "fog": { "__id__": 5 }, "octree": { "__id__": 6 } }, { "__type__": "cc.AmbientInfo", "_skyColorHDR": { "__type__": "cc.Vec4", "x": 0.2, "y": 0.5, "z": 0.8, "w": 0.520833 }, "_skyColor": { "__type__": "cc.Vec4", "x": 0.2, "y": 0.5, "z": 0.8, "w": 0.520833 }, "_skyIllumHDR": 20000, "_skyIllum": 20000, "_groundAlbedoHDR": { "__type__": "cc.Vec4", "x": 0.2, "y": 0.2, "z": 0.2, "w": 1 }, "_groundAlbedo": { "__type__": "cc.Vec4", "x": 0.2, "y": 0.2, "z": 0.2, "w": 1 } }, { "__type__": "cc.SkyboxInfo", "_envLightingType": 0, "_envmapHDR": null, "_envmap": null, "_envmapLodCount": 0, "_diffuseMapHDR": null, "_diffuseMap": null, "_enabled": false, "_useHDR": true, "_editableMaterial": null, "_reflectionHDR": null, "_reflectionMap": null, "_rotationAngle": 0 }, { "__type__": "cc.FogInfo", "_type": 0, "_fogColor": { "__type__": "cc.Color", "r": 200, "g": 200, "b": 200, "a": 255 }, "_enabled": false, "_fogDensity": 0.3, "_fogStart": 0.5, "_fogEnd": 300, "_fogAtten": 5, "_fogTop": 1.5, "_fogRange": 1.2, "_accurate": false }, { "__type__": "cc.OctreeInfo", "_enabled": false, "_minPos": { "__type__": "cc.Vec3", "x": -1024, "y": -1024, "z": -1024 }, "_maxPos": { "__type__": "cc.Vec3", "x": 1024, "y": 1024, "z": 1024 }, "_depth": 8 } ], null, 2); Editor.Message.request('asset-db', 'create-asset', fullPath, sceneContent).then((result: any) => { // Verify scene creation by checking if it exists this.getSceneList().then((sceneList) => { const createdScene = sceneList.data?.find((scene: any) => scene.uuid === result.uuid); resolve({ success: true, data: { uuid: result.uuid, url: result.url, name: sceneName, message: `Scene '${sceneName}' created successfully`, sceneVerified: !!createdScene }, verificationData: createdScene }); }).catch(() => { resolve({ success: true, data: { uuid: result.uuid, url: result.url, name: sceneName, message: `Scene '${sceneName}' created successfully (verification failed)` } }); }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async getSceneHierarchy(includeComponents: boolean = false): Promise<ToolResponse> { return new Promise((resolve) => { // 优先尝试使用 Editor API 查询场景节点树 Editor.Message.request('scene', 'query-node-tree').then((tree: any) => { if (tree) { const hierarchy = this.buildHierarchy(tree, includeComponents); resolve({ success: true, data: hierarchy }); } else { resolve({ success: false, error: 'No scene hierarchy available' }); } }).catch((err: Error) => { // 备用方案:使用场景脚本 const options = { name: 'cocos-mcp-server', method: 'getSceneHierarchy', args: [includeComponents] }; Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { resolve(result); }).catch((err2: Error) => { resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); }); }); }); } private buildHierarchy(node: any, includeComponents: boolean): any { const nodeInfo: any = { uuid: node.uuid, name: node.name, type: node.type, active: node.active, children: [] }; if (includeComponents && node.__comps__) { nodeInfo.components = node.__comps__.map((comp: any) => ({ type: comp.__type__ || 'Unknown', enabled: comp.enabled !== undefined ? comp.enabled : true })); } if (node.children) { nodeInfo.children = node.children.map((child: any) => this.buildHierarchy(child, includeComponents) ); } return nodeInfo; } private async saveSceneAs(path: string): Promise<ToolResponse> { return new Promise((resolve) => { // save-as-scene API 不接受路径参数,会弹出对话框让用户选择 (Editor.Message.request as any)('scene', 'save-as-scene').then(() => { resolve({ success: true, data: { path: path, message: `Scene save-as dialog opened` } }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async closeScene(): Promise<ToolResponse> { return new Promise((resolve) => { Editor.Message.request('scene', 'close-scene').then(() => { resolve({ success: true, message: 'Scene closed successfully' }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DaxianLee/cocos-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server