Files
cocos-mcp/source/tools/prefab-tools.ts
2025-07-17 18:12:56 +08:00

359 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<ToolResponse> {
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<ToolResponse> {
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<ToolResponse> {
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<ToolResponse> {
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<ToolResponse> {
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<ToolResponse> {
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<ToolResponse> {
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<ToolResponse> {
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<ToolResponse> {
// 从 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
});
}
}