import { ToolDefinition, ToolResponse, ToolExecutor, PrefabInfo } from '../types'; export class PrefabTools implements ToolExecutor { getTools(): ToolDefinition[] { return [ { name: 'get_prefab_list', description: 'Get all prefabs in the project', inputSchema: { type: 'object', properties: { folder: { type: 'string', description: 'Folder path to search (optional)', default: 'db://assets' } } } }, { name: 'load_prefab', description: 'Load a prefab by path', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' } }, required: ['prefabPath'] } }, { name: 'instantiate_prefab', description: 'Instantiate a prefab in the scene', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' }, parentUuid: { type: 'string', description: 'Parent node UUID (optional)' }, position: { type: 'object', description: 'Initial position', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } } } }, required: ['prefabPath'] } }, { name: 'create_prefab', description: 'Create a prefab from a node', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Source node UUID' }, savePath: { type: 'string', description: 'Path to save the prefab' }, prefabName: { type: 'string', description: 'Prefab name' } }, required: ['nodeUuid', 'savePath', 'prefabName'] } }, { name: 'create_prefab_from_node', description: 'Create a prefab from a node (alias for create_prefab)', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Source node UUID' }, prefabPath: { type: 'string', description: 'Path to save the prefab' } }, required: ['nodeUuid', 'prefabPath'] } }, { name: 'update_prefab', description: 'Update an existing prefab', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' }, nodeUuid: { type: 'string', description: 'Node UUID with changes' } }, required: ['prefabPath', 'nodeUuid'] } }, { name: 'revert_prefab', description: 'Revert prefab instance to original', inputSchema: { type: 'object', properties: { nodeUuid: { type: 'string', description: 'Prefab instance node UUID' } }, required: ['nodeUuid'] } }, { name: 'get_prefab_info', description: 'Get detailed prefab information', inputSchema: { type: 'object', properties: { prefabPath: { type: 'string', description: 'Prefab asset path' } }, required: ['prefabPath'] } } ]; } async execute(toolName: string, args: any): Promise { switch (toolName) { case 'get_prefab_list': return await this.getPrefabList(args.folder); case 'load_prefab': return await this.loadPrefab(args.prefabPath); case 'instantiate_prefab': return await this.instantiatePrefab(args); case 'create_prefab': return await this.createPrefab(args); case 'create_prefab_from_node': return await this.createPrefabFromNode(args); case 'update_prefab': return await this.updatePrefab(args.prefabPath, args.nodeUuid); case 'revert_prefab': return await this.revertPrefab(args.nodeUuid); case 'get_prefab_info': return await this.getPrefabInfo(args.prefabPath); default: throw new Error(`Unknown tool: ${toolName}`); } } private async getPrefabList(folder: string = 'db://assets'): Promise { return new Promise((resolve) => { const pattern = folder.endsWith('/') ? `${folder}**/*.prefab` : `${folder}/**/*.prefab`; Editor.Message.request('asset-db', 'query-assets', { pattern: pattern }).then((results: any[]) => { const prefabs: PrefabInfo[] = results.map(asset => ({ name: asset.name, path: asset.url, uuid: asset.uuid, folder: asset.url.substring(0, asset.url.lastIndexOf('/')) })); resolve({ success: true, data: prefabs }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async loadPrefab(prefabPath: string): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('Prefab not found'); } return Editor.Message.request('scene', 'load-asset', { uuid: assetInfo.uuid }); }).then((prefabData: any) => { resolve({ success: true, data: { uuid: prefabData.uuid, name: prefabData.name, message: 'Prefab loaded successfully' } }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async instantiatePrefab(args: any): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('Prefab not found'); } const instantiateData: any = { prefab: assetInfo.uuid }; if (args.parentUuid) { instantiateData.parent = args.parentUuid; } if (args.position) { instantiateData.position = args.position; } return Editor.Message.request('scene', 'instantiate-prefab', instantiateData); }).then((result: any) => { resolve({ success: true, data: { nodeUuid: result.uuid, name: result.name, message: 'Prefab instantiated successfully' } }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async createPrefab(args: any): Promise { return new Promise((resolve) => { // 支持 prefabPath 和 savePath 两种参数名 const pathParam = args.prefabPath || args.savePath; if (!pathParam) { resolve({ success: false, error: 'Missing prefab path parameter. Please provide either prefabPath or savePath.' }); return; } const fullPath = pathParam.endsWith('.prefab') ? pathParam : `${pathParam}/${args.prefabName || 'NewPrefab'}.prefab`; // 预制体创建需要特殊的Editor API支持 // 目前Cocos Creator 3.8的MCP插件环境下,预制体创建功能受限 resolve({ success: false, error: 'Prefab creation is not supported in the current MCP plugin environment', instruction: 'Please create prefabs manually by dragging nodes from the scene to the assets folder in the Cocos Creator editor', data: { nodeUuid: args.nodeUuid, requestedPath: fullPath, suggestion: 'You can manually drag the node from the scene to the assets folder to create a prefab.' } }); }); } private async updatePrefab(prefabPath: string, nodeUuid: string): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('Prefab not found'); } return Editor.Message.request('scene', 'apply-prefab', { node: nodeUuid, prefab: assetInfo.uuid }); }).then(() => { resolve({ success: true, message: 'Prefab updated successfully' }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async revertPrefab(nodeUuid: string): Promise { return new Promise((resolve) => { Editor.Message.request('scene', 'revert-prefab', { node: nodeUuid }).then(() => { resolve({ success: true, message: 'Prefab instance reverted successfully' }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async getPrefabInfo(prefabPath: string): Promise { return new Promise((resolve) => { Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { if (!assetInfo) { throw new Error('Prefab not found'); } return Editor.Message.request('asset-db', 'query-asset-meta', assetInfo.uuid); }).then((metaInfo: any) => { const info: PrefabInfo = { name: metaInfo.name, uuid: metaInfo.uuid, path: prefabPath, folder: prefabPath.substring(0, prefabPath.lastIndexOf('/')), createTime: metaInfo.createTime, modifyTime: metaInfo.modifyTime, dependencies: metaInfo.depends || [] }; resolve({ success: true, data: info }); }).catch((err: Error) => { resolve({ success: false, error: err.message }); }); }); } private async createPrefabFromNode(args: any): Promise { // 从 prefabPath 提取名称 const prefabPath = args.prefabPath; const prefabName = prefabPath.split('/').pop()?.replace('.prefab', '') || 'NewPrefab'; // 调用原来的 createPrefab 方法 return await this.createPrefab({ nodeUuid: args.nodeUuid, savePath: prefabPath, prefabName: prefabName }); } }