#
tokens: 23217/50000 1/37 files (page 4/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 4 of 5. Use http://codebase.md/daxianlee/cocos-mcp-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── @types
│   └── schema
│       └── package
│           ├── base
│           │   └── panels.json
│           ├── contributions
│           │   └── index.json
│           └── index.json
├── base.tsconfig.json
├── dist
│   ├── examples
│   │   └── prefab-instantiation-example.js
│   ├── main.js
│   ├── mcp-server.js
│   ├── panels
│   │   ├── default
│   │   │   └── index.js
│   │   └── tool-manager
│   │       └── index.js
│   ├── scene.js
│   ├── settings.js
│   ├── test
│   │   ├── manual-test.js
│   │   ├── mcp-tool-tester.js
│   │   ├── prefab-tools-test.js
│   │   └── tool-tester.js
│   ├── tools
│   │   ├── asset-advanced-tools.js
│   │   ├── broadcast-tools.js
│   │   ├── component-tools.js
│   │   ├── debug-tools.js
│   │   ├── node-tools.js
│   │   ├── prefab-tools.js
│   │   ├── preferences-tools.js
│   │   ├── project-tools.js
│   │   ├── reference-image-tools.js
│   │   ├── scene-advanced-tools.js
│   │   ├── scene-tools.js
│   │   ├── scene-view-tools.js
│   │   ├── server-tools.js
│   │   ├── tool-manager.js
│   │   └── validation-tools.js
│   └── types
│       └── index.js
├── FEATURE_GUIDE_CN.md
├── FEATURE_GUIDE_EN.md
├── i18n
│   ├── en.js
│   └── zh.js
├── image
│   ├── iamge2.png
│   └── image-20250717174157957.png
├── package-lock.json
├── package.json
├── README.EN.md
├── README.md
├── scripts
│   └── preinstall.js
├── source
│   ├── main.ts
│   ├── mcp-server.ts
│   ├── panels
│   │   ├── default
│   │   │   └── index.ts
│   │   └── tool-manager
│   │       └── index.ts
│   ├── scene.ts
│   ├── settings.ts
│   ├── test
│   │   ├── manual-test.ts
│   │   ├── mcp-tool-tester.ts
│   │   ├── prefab-tools-test.ts
│   │   └── tool-tester.ts
│   ├── tools
│   │   ├── asset-advanced-tools.ts
│   │   ├── broadcast-tools.ts
│   │   ├── component-tools.ts
│   │   ├── debug-tools.ts
│   │   ├── node-tools.ts
│   │   ├── prefab-tools.ts
│   │   ├── preferences-tools.ts
│   │   ├── project-tools.ts
│   │   ├── reference-image-tools.ts
│   │   ├── scene-advanced-tools.ts
│   │   ├── scene-tools.ts
│   │   ├── scene-view-tools.ts
│   │   ├── server-tools.ts
│   │   ├── tool-manager.ts
│   │   └── validation-tools.ts
│   └── types
│       └── index.ts
├── static
│   ├── icon.png
│   ├── style
│   │   └── default
│   │       └── index.css
│   └── template
│       ├── default
│       │   ├── index.html
│       │   └── tool-manager.html
│       └── vue
│           └── mcp-server-app.html
├── TestScript.js
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/source/tools/component-tools.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { ToolDefinition, ToolResponse, ToolExecutor, ComponentInfo } from '../types';
   2 | 
   3 | export class ComponentTools implements ToolExecutor {
   4 |     getTools(): ToolDefinition[] {
   5 |         return [
   6 |             {
   7 |                 name: 'add_component',
   8 |                 description: 'Add a component to a specific node. IMPORTANT: You must provide the nodeUuid parameter to specify which node to add the component to.',
   9 |                 inputSchema: {
  10 |                     type: 'object',
  11 |                     properties: {
  12 |                         nodeUuid: {
  13 |                             type: 'string',
  14 |                             description: 'Target node UUID. REQUIRED: You must specify the exact node to add the component to. Use get_all_nodes or find_node_by_name to get the UUID of the desired node.'
  15 |                         },
  16 |                         componentType: {
  17 |                             type: 'string',
  18 |                             description: 'Component type (e.g., cc.Sprite, cc.Label, cc.Button)'
  19 |                         }
  20 |                     },
  21 |                     required: ['nodeUuid', 'componentType']
  22 |                 }
  23 |             },
  24 |             {
  25 |                 name: 'remove_component',
  26 |                 description: 'Remove a component from a node. componentType must be the component\'s classId (cid, i.e. the type field from getComponents), not the script name or class name. Use getComponents to get the correct cid.',
  27 |                 inputSchema: {
  28 |                     type: 'object',
  29 |                     properties: {
  30 |                         nodeUuid: {
  31 |                             type: 'string',
  32 |                             description: 'Node UUID'
  33 |                         },
  34 |                         componentType: {
  35 |                             type: 'string',
  36 |                             description: 'Component cid (type field from getComponents). Do NOT use script name or class name. Example: "cc.Sprite" or "9b4a7ueT9xD6aRE+AlOusy1"'
  37 |                         }
  38 |                     },
  39 |                     required: ['nodeUuid', 'componentType']
  40 |                 }
  41 |             },
  42 |             {
  43 |                 name: 'get_components',
  44 |                 description: 'Get all components of a node',
  45 |                 inputSchema: {
  46 |                     type: 'object',
  47 |                     properties: {
  48 |                         nodeUuid: {
  49 |                             type: 'string',
  50 |                             description: 'Node UUID'
  51 |                         }
  52 |                     },
  53 |                     required: ['nodeUuid']
  54 |                 }
  55 |             },
  56 |             {
  57 |                 name: 'get_component_info',
  58 |                 description: 'Get specific component information',
  59 |                 inputSchema: {
  60 |                     type: 'object',
  61 |                     properties: {
  62 |                         nodeUuid: {
  63 |                             type: 'string',
  64 |                             description: 'Node UUID'
  65 |                         },
  66 |                         componentType: {
  67 |                             type: 'string',
  68 |                             description: 'Component type to get info for'
  69 |                         }
  70 |                     },
  71 |                     required: ['nodeUuid', 'componentType']
  72 |                 }
  73 |             },
  74 |             {
  75 |                 name: 'set_component_property',
  76 |                 description: 'Set component property values for UI components or custom script components. Supports setting properties of built-in UI components (e.g., cc.Label, cc.Sprite) and custom script components. Note: For node basic properties (name, active, layer, etc.), use set_node_property. For node transform properties (position, rotation, scale, etc.), use set_node_transform.',
  77 |                 inputSchema: {
  78 |                     type: 'object',
  79 |                     properties: {
  80 |                         nodeUuid: {
  81 |                             type: 'string',
  82 |                             description: 'Target node UUID - Must specify the node to operate on'
  83 |                         },
  84 |                         componentType: {
  85 |                             type: 'string',
  86 |                             description: 'Component type - Can be built-in components (e.g., cc.Label) or custom script components (e.g., MyScript). If unsure about component type, use get_components first to retrieve all components on the node.',
  87 |                             // 移除enum限制,允许任意组件类型包括自定义脚本
  88 |                         },
  89 |                         property: {
  90 |                             type: 'string',
  91 |                             description: 'Property name - The property to set. Common properties include:\n' +
  92 |                                 '• cc.Label: string (text content), fontSize (font size), color (text color)\n' +
  93 |                                 '• cc.Sprite: spriteFrame (sprite frame), color (tint color), sizeMode (size mode)\n' +
  94 |                                 '• cc.Button: normalColor (normal color), pressedColor (pressed color), target (target node)\n' +
  95 |                                 '• cc.UITransform: contentSize (content size), anchorPoint (anchor point)\n' +
  96 |                                 '• Custom Scripts: Based on properties defined in the script'
  97 |                         },
  98 |                         propertyType: {
  99 |                             type: 'string',
 100 |                             description: 'Property type - Must explicitly specify the property data type for correct value conversion and validation',
 101 |                             enum: [
 102 |                                 'string', 'number', 'boolean', 'integer', 'float',
 103 |                                 'color', 'vec2', 'vec3', 'size',
 104 |                                 'node', 'component', 'spriteFrame', 'prefab', 'asset',
 105 |                                 'nodeArray', 'colorArray', 'numberArray', 'stringArray'
 106 |                             ]
 107 |                                                 },
 108 | 
 109 |                         value: {
 110 |                             description: 'Property value - Use the corresponding data format based on propertyType:\n\n' +
 111 |                                 '📝 Basic Data Types:\n' +
 112 |                                 '• string: "Hello World" (text string)\n' +
 113 |                                 '• number/integer/float: 42 or 3.14 (numeric value)\n' +
 114 |                                 '• boolean: true or false (boolean value)\n\n' +
 115 |                                 '🎨 Color Type:\n' +
 116 |                                 '• color: {"r":255,"g":0,"b":0,"a":255} (RGBA values, range 0-255)\n' +
 117 |                                 '  - Alternative: "#FF0000" (hexadecimal format)\n' +
 118 |                                 '  - Transparency: a value controls opacity, 255 = fully opaque, 0 = fully transparent\n\n' +
 119 |                                 '📐 Vector and Size Types:\n' +
 120 |                                 '• vec2: {"x":100,"y":50} (2D vector)\n' +
 121 |                                 '• vec3: {"x":1,"y":2,"z":3} (3D vector)\n' +
 122 |                                 '• size: {"width":100,"height":50} (size dimensions)\n\n' +
 123 |                                 '🔗 Reference Types (using UUID strings):\n' +
 124 |                                 '• node: "target-node-uuid" (node reference)\n' +
 125 |                                 '  How to get: Use get_all_nodes or find_node_by_name to get node UUIDs\n' +
 126 |                                 '• component: "target-node-uuid" (component reference)\n' +
 127 |                                 '  How it works: \n' +
 128 |                                 '    1. Provide the UUID of the NODE that contains the target component\n' +
 129 |                                 '    2. System auto-detects required component type from property metadata\n' +
 130 |                                 '    3. Finds the component on target node and gets its scene __id__\n' +
 131 |                                 '    4. Sets reference using the scene __id__ (not node UUID)\n' +
 132 |                                 '  Example: value="label-node-uuid" will find cc.Label and use its scene ID\n' +
 133 |                                 '• spriteFrame: "spriteframe-uuid" (sprite frame asset)\n' +
 134 |                                 '  How to get: Check asset database or use asset browser\n' +
 135 |                                 '• prefab: "prefab-uuid" (prefab asset)\n' +
 136 |                                 '  How to get: Check asset database or use asset browser\n' +
 137 |                                 '• asset: "asset-uuid" (generic asset reference)\n' +
 138 |                                 '  How to get: Check asset database or use asset browser\n\n' +
 139 |                                 '📋 Array Types:\n' +
 140 |                                 '• nodeArray: ["uuid1","uuid2"] (array of node UUIDs)\n' +
 141 |                                 '• colorArray: [{"r":255,"g":0,"b":0,"a":255}] (array of colors)\n' +
 142 |                                 '• numberArray: [1,2,3,4,5] (array of numbers)\n' +
 143 |                                 '• stringArray: ["item1","item2"] (array of strings)'
 144 |                         }
 145 |                     },
 146 |                     required: ['nodeUuid', 'componentType', 'property', 'propertyType', 'value']
 147 |                 }
 148 |             },
 149 |             {
 150 |                 name: 'attach_script',
 151 |                 description: 'Attach a script component to a node',
 152 |                 inputSchema: {
 153 |                     type: 'object',
 154 |                     properties: {
 155 |                         nodeUuid: {
 156 |                             type: 'string',
 157 |                             description: 'Node UUID'
 158 |                         },
 159 |                         scriptPath: {
 160 |                             type: 'string',
 161 |                             description: 'Script asset path (e.g., db://assets/scripts/MyScript.ts)'
 162 |                         }
 163 |                     },
 164 |                     required: ['nodeUuid', 'scriptPath']
 165 |                 }
 166 |             },
 167 |             {
 168 |                 name: 'get_available_components',
 169 |                 description: 'Get list of available component types',
 170 |                 inputSchema: {
 171 |                     type: 'object',
 172 |                     properties: {
 173 |                         category: {
 174 |                             type: 'string',
 175 |                             description: 'Component category filter',
 176 |                             enum: ['all', 'renderer', 'ui', 'physics', 'animation', 'audio'],
 177 |                             default: 'all'
 178 |                         }
 179 |                     }
 180 |                 }
 181 |             }
 182 |         ];
 183 |     }
 184 | 
 185 |     async execute(toolName: string, args: any): Promise<ToolResponse> {
 186 |         switch (toolName) {
 187 |             case 'add_component':
 188 |                 return await this.addComponent(args.nodeUuid, args.componentType);
 189 |             case 'remove_component':
 190 |                 return await this.removeComponent(args.nodeUuid, args.componentType);
 191 |             case 'get_components':
 192 |                 return await this.getComponents(args.nodeUuid);
 193 |             case 'get_component_info':
 194 |                 return await this.getComponentInfo(args.nodeUuid, args.componentType);
 195 |             case 'set_component_property':
 196 |                 return await this.setComponentProperty(args);
 197 |             case 'attach_script':
 198 |                 return await this.attachScript(args.nodeUuid, args.scriptPath);
 199 |             case 'get_available_components':
 200 |                 return await this.getAvailableComponents(args.category);
 201 |             default:
 202 |                 throw new Error(`Unknown tool: ${toolName}`);
 203 |         }
 204 |     }
 205 | 
 206 |     private async addComponent(nodeUuid: string, componentType: string): Promise<ToolResponse> {
 207 |         return new Promise(async (resolve) => {
 208 |             // 先查找节点上是否已存在该组件
 209 |             const allComponentsInfo = await this.getComponents(nodeUuid);
 210 |             if (allComponentsInfo.success && allComponentsInfo.data?.components) {
 211 |                 const existingComponent = allComponentsInfo.data.components.find((comp: any) => comp.type === componentType);
 212 |                 if (existingComponent) {
 213 |                     resolve({
 214 |                         success: true,
 215 |                         message: `Component '${componentType}' already exists on node`,
 216 |                         data: {
 217 |                             nodeUuid: nodeUuid,
 218 |                             componentType: componentType,
 219 |                             componentVerified: true,
 220 |                             existing: true
 221 |                         }
 222 |                     });
 223 |                     return;
 224 |                 }
 225 |             }
 226 |             // 尝试直接使用 Editor API 添加组件
 227 |             Editor.Message.request('scene', 'create-component', {
 228 |                 uuid: nodeUuid,
 229 |                 component: componentType
 230 |             }).then(async (result: any) => {
 231 |                 // 等待一段时间让Editor完成组件添加
 232 |                 await new Promise(resolve => setTimeout(resolve, 100));
 233 |                 // 重新查询节点信息验证组件是否真的添加成功
 234 |                 try {
 235 |                     const allComponentsInfo2 = await this.getComponents(nodeUuid);
 236 |                     if (allComponentsInfo2.success && allComponentsInfo2.data?.components) {
 237 |                         const addedComponent = allComponentsInfo2.data.components.find((comp: any) => comp.type === componentType);
 238 |                         if (addedComponent) {
 239 |                             resolve({
 240 |                                 success: true,
 241 |                                 message: `Component '${componentType}' added successfully`,
 242 |                                 data: {
 243 |                                     nodeUuid: nodeUuid,
 244 |                                     componentType: componentType,
 245 |                                     componentVerified: true,
 246 |                                     existing: false
 247 |                                 }
 248 |                             });
 249 |                         } else {
 250 |                             resolve({
 251 |                                 success: false,
 252 |                                 error: `Component '${componentType}' was not found on node after addition. Available components: ${allComponentsInfo2.data.components.map((c: any) => c.type).join(', ')}`
 253 |                             });
 254 |                         }
 255 |                     } else {
 256 |                         resolve({
 257 |                             success: false,
 258 |                             error: `Failed to verify component addition: ${allComponentsInfo2.error || 'Unable to get node components'}`
 259 |                         });
 260 |                     }
 261 |                 } catch (verifyError: any) {
 262 |                     resolve({
 263 |                         success: false,
 264 |                         error: `Failed to verify component addition: ${verifyError.message}`
 265 |                     });
 266 |                 }
 267 |             }).catch((err: Error) => {
 268 |                 // 备用方案:使用场景脚本
 269 |                 const options = {
 270 |                     name: 'cocos-mcp-server',
 271 |                     method: 'addComponentToNode',
 272 |                     args: [nodeUuid, componentType]
 273 |                 };
 274 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
 275 |                     resolve(result);
 276 |                 }).catch((err2: Error) => {
 277 |                     resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` });
 278 |                 });
 279 |             });
 280 |         });
 281 |     }
 282 | 
 283 |     private async removeComponent(nodeUuid: string, componentType: string): Promise<ToolResponse> {
 284 |         return new Promise(async (resolve) => {
 285 |             // 1. 查找节点上的所有组件
 286 |             const allComponentsInfo = await this.getComponents(nodeUuid);
 287 |             if (!allComponentsInfo.success || !allComponentsInfo.data?.components) {
 288 |                 resolve({ success: false, error: `Failed to get components for node '${nodeUuid}': ${allComponentsInfo.error}` });
 289 |                 return;
 290 |             }
 291 |             // 2. 只查找type字段等于componentType的组件(即cid)
 292 |             const exists = allComponentsInfo.data.components.some((comp: any) => comp.type === componentType);
 293 |             if (!exists) {
 294 |                 resolve({ success: false, error: `Component cid '${componentType}' not found on node '${nodeUuid}'. 请用getComponents获取type字段(cid)作为componentType。` });
 295 |                 return;
 296 |             }
 297 |             // 3. 官方API直接移除
 298 |             try {
 299 |                 await Editor.Message.request('scene', 'remove-component', {
 300 |                     uuid: nodeUuid,
 301 |                     component: componentType
 302 |                 });
 303 |                 // 4. 再查一次确认是否移除
 304 |                 const afterRemoveInfo = await this.getComponents(nodeUuid);
 305 |                 const stillExists = afterRemoveInfo.success && afterRemoveInfo.data?.components?.some((comp: any) => comp.type === componentType);
 306 |                 if (stillExists) {
 307 |                     resolve({ success: false, error: `Component cid '${componentType}' was not removed from node '${nodeUuid}'.` });
 308 |                 } else {
 309 |                     resolve({
 310 |                         success: true,
 311 |                         message: `Component cid '${componentType}' removed successfully from node '${nodeUuid}'`,
 312 |                         data: { nodeUuid, componentType }
 313 |                     });
 314 |                 }
 315 |             } catch (err: any) {
 316 |                 resolve({ success: false, error: `Failed to remove component: ${err.message}` });
 317 |             }
 318 |         });
 319 |     }
 320 | 
 321 |     private async getComponents(nodeUuid: string): Promise<ToolResponse> {
 322 |         return new Promise((resolve) => {
 323 |             // 优先尝试直接使用 Editor API 查询节点信息
 324 |             Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData: any) => {
 325 |                 if (nodeData && nodeData.__comps__) {
 326 |                     const components = nodeData.__comps__.map((comp: any) => ({
 327 |                         type: comp.__type__ || comp.cid || comp.type || 'Unknown',
 328 |                         uuid: comp.uuid?.value || comp.uuid || null,
 329 |                         enabled: comp.enabled !== undefined ? comp.enabled : true,
 330 |                         properties: this.extractComponentProperties(comp)
 331 |                     }));
 332 |                     
 333 |                     resolve({
 334 |                         success: true,
 335 |                         data: {
 336 |                             nodeUuid: nodeUuid,
 337 |                             components: components
 338 |                         }
 339 |                     });
 340 |                 } else {
 341 |                     resolve({ success: false, error: 'Node not found or no components data' });
 342 |                 }
 343 |             }).catch((err: Error) => {
 344 |                 // 备用方案:使用场景脚本
 345 |                 const options = {
 346 |                     name: 'cocos-mcp-server',
 347 |                     method: 'getNodeInfo',
 348 |                     args: [nodeUuid]
 349 |                 };
 350 |                 
 351 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
 352 |                     if (result.success) {
 353 |                         resolve({
 354 |                             success: true,
 355 |                             data: result.data.components
 356 |                         });
 357 |                     } else {
 358 |                         resolve(result);
 359 |                     }
 360 |                 }).catch((err2: Error) => {
 361 |                     resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` });
 362 |                 });
 363 |             });
 364 |         });
 365 |     }
 366 | 
 367 |     private async getComponentInfo(nodeUuid: string, componentType: string): Promise<ToolResponse> {
 368 |         return new Promise((resolve) => {
 369 |             // 优先尝试直接使用 Editor API 查询节点信息
 370 |             Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData: any) => {
 371 |                 if (nodeData && nodeData.__comps__) {
 372 |                     const component = nodeData.__comps__.find((comp: any) => {
 373 |                         const compType = comp.__type__ || comp.cid || comp.type;
 374 |                         return compType === componentType;
 375 |                     });
 376 |                     
 377 |                     if (component) {
 378 |                         resolve({
 379 |                             success: true,
 380 |                             data: {
 381 |                                 nodeUuid: nodeUuid,
 382 |                                 componentType: componentType,
 383 |                                 enabled: component.enabled !== undefined ? component.enabled : true,
 384 |                                 properties: this.extractComponentProperties(component)
 385 |                             }
 386 |                         });
 387 |                     } else {
 388 |                         resolve({ success: false, error: `Component '${componentType}' not found on node` });
 389 |                     }
 390 |                 } else {
 391 |                     resolve({ success: false, error: 'Node not found or no components data' });
 392 |                 }
 393 |             }).catch((err: Error) => {
 394 |                 // 备用方案:使用场景脚本
 395 |                 const options = {
 396 |                     name: 'cocos-mcp-server',
 397 |                     method: 'getNodeInfo',
 398 |                     args: [nodeUuid]
 399 |                 };
 400 |                 
 401 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
 402 |                     if (result.success && result.data.components) {
 403 |                         const component = result.data.components.find((comp: any) => comp.type === componentType);
 404 |                         if (component) {
 405 |                             resolve({
 406 |                                 success: true,
 407 |                                 data: {
 408 |                                     nodeUuid: nodeUuid,
 409 |                                     componentType: componentType,
 410 |                                     ...component
 411 |                                 }
 412 |                             });
 413 |                         } else {
 414 |                             resolve({ success: false, error: `Component '${componentType}' not found on node` });
 415 |                         }
 416 |                     } else {
 417 |                         resolve({ success: false, error: result.error || 'Failed to get component info' });
 418 |                     }
 419 |                 }).catch((err2: Error) => {
 420 |                     resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` });
 421 |                 });
 422 |             });
 423 |         });
 424 |     }
 425 | 
 426 |     private extractComponentProperties(component: any): Record<string, any> {
 427 |         console.log(`[extractComponentProperties] Processing component:`, Object.keys(component));
 428 |         
 429 |         // 检查组件是否有 value 属性,这通常包含实际的组件属性
 430 |         if (component.value && typeof component.value === 'object') {
 431 |             console.log(`[extractComponentProperties] Found component.value with properties:`, Object.keys(component.value));
 432 |             return component.value; // 直接返回 value 对象,它包含所有组件属性
 433 |         }
 434 |         
 435 |         // 备用方案:从组件对象中直接提取属性
 436 |         const properties: Record<string, any> = {};
 437 |         const excludeKeys = ['__type__', 'enabled', 'node', '_id', '__scriptAsset', 'uuid', 'name', '_name', '_objFlags', '_enabled', 'type', 'readonly', 'visible', 'cid', 'editor', 'extends'];
 438 |         
 439 |         for (const key in component) {
 440 |             if (!excludeKeys.includes(key) && !key.startsWith('_')) {
 441 |                 console.log(`[extractComponentProperties] Found direct property '${key}':`, typeof component[key]);
 442 |                 properties[key] = component[key];
 443 |             }
 444 |         }
 445 |         
 446 |         console.log(`[extractComponentProperties] Final extracted properties:`, Object.keys(properties));
 447 |         return properties;
 448 |     }
 449 | 
 450 |     private async findComponentTypeByUuid(componentUuid: string): Promise<string | null> {
 451 |         console.log(`[findComponentTypeByUuid] Searching for component type with UUID: ${componentUuid}`);
 452 |         if (!componentUuid) {
 453 |             return null;
 454 |         }
 455 |         try {
 456 |             const nodeTree = await Editor.Message.request('scene', 'query-node-tree');
 457 |             if (!nodeTree) {
 458 |                 console.warn('[findComponentTypeByUuid] Failed to query node tree.');
 459 |                 return null;
 460 |             }
 461 | 
 462 |             const queue: any[] = [nodeTree];
 463 |             
 464 |             while (queue.length > 0) {
 465 |                 const currentNodeInfo = queue.shift();
 466 |                 if (!currentNodeInfo || !currentNodeInfo.uuid) {
 467 |                     continue;
 468 |                 }
 469 | 
 470 |                 try {
 471 |                     const fullNodeData = await Editor.Message.request('scene', 'query-node', currentNodeInfo.uuid);
 472 |                     if (fullNodeData && fullNodeData.__comps__) {
 473 |                         for (const comp of fullNodeData.__comps__) {
 474 |                             const compAny = comp as any; // Cast to any to access dynamic properties
 475 |                             // The component UUID is nested in the 'value' property
 476 |                             if (compAny.uuid && compAny.uuid.value === componentUuid) {
 477 |                                 const componentType = compAny.__type__;
 478 |                                 console.log(`[findComponentTypeByUuid] Found component type '${componentType}' for UUID ${componentUuid} on node ${fullNodeData.name?.value}`);
 479 |                                 return componentType;
 480 |                             }
 481 |                         }
 482 |                     }
 483 |                 } catch (e) {
 484 |                     console.warn(`[findComponentTypeByUuid] Could not query node ${currentNodeInfo.uuid}:`, e);
 485 |                 }
 486 | 
 487 |                 if (currentNodeInfo.children) {
 488 |                     for (const child of currentNodeInfo.children) {
 489 |                         queue.push(child);
 490 |                     }
 491 |                 }
 492 |             }
 493 | 
 494 |             console.warn(`[findComponentTypeByUuid] Component with UUID ${componentUuid} not found in scene tree.`);
 495 |             return null;
 496 |         } catch (error) {
 497 |             console.error(`[findComponentTypeByUuid] Error while searching for component type:`, error);
 498 |             return null;
 499 |         }
 500 |     }
 501 | 
 502 |     private async setComponentProperty(args: any): Promise<ToolResponse> {
 503 |                         const { nodeUuid, componentType, property, propertyType, value } = args;
 504 |         
 505 |         return new Promise(async (resolve) => {
 506 |             try {
 507 |                 console.log(`[ComponentTools] Setting ${componentType}.${property} (type: ${propertyType}) = ${JSON.stringify(value)} on node ${nodeUuid}`);
 508 |                 
 509 |                 // Step 0: 检测是否为节点属性,如果是则重定向到对应的节点方法
 510 |                 const nodeRedirectResult = await this.checkAndRedirectNodeProperties(args);
 511 |                 if (nodeRedirectResult) {
 512 |                     resolve(nodeRedirectResult);
 513 |                     return;
 514 |                 }
 515 |                 
 516 |                 // Step 1: 获取组件信息,使用与getComponents相同的方法
 517 |                 const componentsResponse = await this.getComponents(nodeUuid);
 518 |                 if (!componentsResponse.success || !componentsResponse.data) {
 519 |                     resolve({
 520 |                         success: false,
 521 |                         error: `Failed to get components for node '${nodeUuid}': ${componentsResponse.error}`,
 522 |                         instruction: `Please verify that node UUID '${nodeUuid}' is correct. Use get_all_nodes or find_node_by_name to get the correct node UUID.`
 523 |                     });
 524 |                     return;
 525 |                 }
 526 |                 
 527 |                 const allComponents = componentsResponse.data.components;
 528 |                 
 529 |                 // Step 2: 查找目标组件
 530 |                 let targetComponent = null;
 531 |                 const availableTypes: string[] = [];
 532 |                 
 533 |                 for (let i = 0; i < allComponents.length; i++) {
 534 |                     const comp = allComponents[i];
 535 |                     availableTypes.push(comp.type);
 536 |                     
 537 |                     if (comp.type === componentType) {
 538 |                         targetComponent = comp;
 539 |                         break;
 540 |                     }
 541 |                 }
 542 |                 
 543 |                 if (!targetComponent) {
 544 |                     // 提供更详细的错误信息和建议
 545 |                     const instruction = this.generateComponentSuggestion(componentType, availableTypes, property);
 546 |                     resolve({
 547 |                         success: false,
 548 |                         error: `Component '${componentType}' not found on node. Available components: ${availableTypes.join(', ')}`,
 549 |                         instruction: instruction
 550 |                     });
 551 |                     return;
 552 |                 }
 553 |                 
 554 |                 // Step 3: 自动检测和转换属性值
 555 |                 let propertyInfo;
 556 |                 try {
 557 |                     console.log(`[ComponentTools] Analyzing property: ${property}`);
 558 |                     propertyInfo = this.analyzeProperty(targetComponent, property);
 559 |                 } catch (analyzeError: any) {
 560 |                     console.error(`[ComponentTools] Error in analyzeProperty:`, analyzeError);
 561 |                     resolve({
 562 |                         success: false,
 563 |                         error: `Failed to analyze property '${property}': ${analyzeError.message}`
 564 |                     });
 565 |                     return;
 566 |                 }
 567 |                 
 568 |                 if (!propertyInfo.exists) {
 569 |                     resolve({
 570 |                         success: false,
 571 |                         error: `Property '${property}' not found on component '${componentType}'. Available properties: ${propertyInfo.availableProperties.join(', ')}`
 572 |                     });
 573 |                     return;
 574 |                 }
 575 |                 
 576 |                 // Step 4: 处理属性值和设置
 577 |                 const originalValue = propertyInfo.originalValue;
 578 |                 let processedValue: any;
 579 |                 
 580 |                 // 根据明确的propertyType处理属性值
 581 |                 switch (propertyType) {
 582 |                     case 'string':
 583 |                         processedValue = String(value);
 584 |                         break;
 585 |                     case 'number':
 586 |                     case 'integer':
 587 |                     case 'float':
 588 |                         processedValue = Number(value);
 589 |                         break;
 590 |                     case 'boolean':
 591 |                         processedValue = Boolean(value);
 592 |                         break;
 593 |                     case 'color':
 594 |                         if (typeof value === 'string') {
 595 |                             // 字符串格式:支持十六进制、颜色名称、rgb()/rgba()
 596 |                             processedValue = this.parseColorString(value);
 597 |                         } else if (typeof value === 'object' && value !== null) {
 598 |                             // 对象格式:验证并转换RGBA值
 599 |                             processedValue = {
 600 |                                 r: Math.min(255, Math.max(0, Number(value.r) || 0)),
 601 |                                 g: Math.min(255, Math.max(0, Number(value.g) || 0)),
 602 |                                 b: Math.min(255, Math.max(0, Number(value.b) || 0)),
 603 |                                 a: value.a !== undefined ? Math.min(255, Math.max(0, Number(value.a))) : 255
 604 |                             };
 605 |                         } else {
 606 |                             throw new Error('Color value must be an object with r, g, b properties or a hexadecimal string (e.g., "#FF0000")');
 607 |                         }
 608 |                         break;
 609 |                     case 'vec2':
 610 |                         if (typeof value === 'object' && value !== null) {
 611 |                             processedValue = {
 612 |                                 x: Number(value.x) || 0,
 613 |                                 y: Number(value.y) || 0
 614 |                             };
 615 |                         } else {
 616 |                             throw new Error('Vec2 value must be an object with x, y properties');
 617 |                         }
 618 |                         break;
 619 |                     case 'vec3':
 620 |                         if (typeof value === 'object' && value !== null) {
 621 |                             processedValue = {
 622 |                                 x: Number(value.x) || 0,
 623 |                                 y: Number(value.y) || 0,
 624 |                                 z: Number(value.z) || 0
 625 |                             };
 626 |                         } else {
 627 |                             throw new Error('Vec3 value must be an object with x, y, z properties');
 628 |                         }
 629 |                         break;
 630 |                     case 'size':
 631 |                         if (typeof value === 'object' && value !== null) {
 632 |                             processedValue = {
 633 |                                 width: Number(value.width) || 0,
 634 |                                 height: Number(value.height) || 0
 635 |                             };
 636 |                         } else {
 637 |                             throw new Error('Size value must be an object with width, height properties');
 638 |                         }
 639 |                         break;
 640 |                     case 'node':
 641 |                         if (typeof value === 'string') {
 642 |                             processedValue = { uuid: value };
 643 |                         } else {
 644 |                             throw new Error('Node reference value must be a string UUID');
 645 |                         }
 646 |                         break;
 647 |                     case 'component':
 648 |                         if (typeof value === 'string') {
 649 |                             // 组件引用需要特殊处理:通过节点UUID找到组件的__id__
 650 |                             processedValue = value; // 先保存节点UUID,后续会转换为__id__
 651 |                         } else {
 652 |                             throw new Error('Component reference value must be a string (node UUID containing the target component)');
 653 |                         }
 654 |                         break;
 655 |                     case 'spriteFrame':
 656 |                     case 'prefab':
 657 |                     case 'asset':
 658 |                         if (typeof value === 'string') {
 659 |                             processedValue = { uuid: value };
 660 |                         } else {
 661 |                             throw new Error(`${propertyType} value must be a string UUID`);
 662 |                         }
 663 |                         break;
 664 |                     case 'nodeArray':
 665 |                         if (Array.isArray(value)) {
 666 |                             processedValue = value.map((item: any) => {
 667 |                                 if (typeof item === 'string') {
 668 |                                     return { uuid: item };
 669 |                                 } else {
 670 |                                     throw new Error('NodeArray items must be string UUIDs');
 671 |                                 }
 672 |                             });
 673 |                         } else {
 674 |                             throw new Error('NodeArray value must be an array');
 675 |                         }
 676 |                         break;
 677 |                     case 'colorArray':
 678 |                         if (Array.isArray(value)) {
 679 |                             processedValue = value.map((item: any) => {
 680 |                                 if (typeof item === 'object' && item !== null && 'r' in item) {
 681 |                                     return {
 682 |                                         r: Math.min(255, Math.max(0, Number(item.r) || 0)),
 683 |                                         g: Math.min(255, Math.max(0, Number(item.g) || 0)),
 684 |                                         b: Math.min(255, Math.max(0, Number(item.b) || 0)),
 685 |                                         a: item.a !== undefined ? Math.min(255, Math.max(0, Number(item.a))) : 255
 686 |                                     };
 687 |                                 } else {
 688 |                                     return { r: 255, g: 255, b: 255, a: 255 };
 689 |                                 }
 690 |                             });
 691 |                         } else {
 692 |                             throw new Error('ColorArray value must be an array');
 693 |                         }
 694 |                         break;
 695 |                     case 'numberArray':
 696 |                         if (Array.isArray(value)) {
 697 |                             processedValue = value.map((item: any) => Number(item));
 698 |                         } else {
 699 |                             throw new Error('NumberArray value must be an array');
 700 |                         }
 701 |                         break;
 702 |                     case 'stringArray':
 703 |                         if (Array.isArray(value)) {
 704 |                             processedValue = value.map((item: any) => String(item));
 705 |                         } else {
 706 |                             throw new Error('StringArray value must be an array');
 707 |                         }
 708 |                         break;
 709 |                     default:
 710 |                         throw new Error(`Unsupported property type: ${propertyType}`);
 711 |                 }
 712 |                 
 713 |                 console.log(`[ComponentTools] Converting value: ${JSON.stringify(value)} -> ${JSON.stringify(processedValue)} (type: ${propertyType})`);
 714 |                 console.log(`[ComponentTools] Property analysis result: propertyInfo.type="${propertyInfo.type}", propertyType="${propertyType}"`);
 715 |                 console.log(`[ComponentTools] Will use color special handling: ${propertyType === 'color' && processedValue && typeof processedValue === 'object'}`);
 716 |                 
 717 |                 // 用于验证的实际期望值(对于组件引用需要特殊处理)
 718 |                 let actualExpectedValue = processedValue;
 719 |                 
 720 |                 // Step 5: 获取原始节点数据来构建正确的路径
 721 |                 const rawNodeData = await Editor.Message.request('scene', 'query-node', nodeUuid);
 722 |                 if (!rawNodeData || !rawNodeData.__comps__) {
 723 |                     resolve({
 724 |                         success: false,
 725 |                         error: `Failed to get raw node data for property setting`
 726 |                     });
 727 |                     return;
 728 |                 }
 729 |                 
 730 |                 // 找到原始组件的索引
 731 |                 let rawComponentIndex = -1;
 732 |                 for (let i = 0; i < rawNodeData.__comps__.length; i++) {
 733 |                     const comp = rawNodeData.__comps__[i] as any;
 734 |                     const compType = comp.__type__ || comp.cid || comp.type || 'Unknown';
 735 |                     if (compType === componentType) {
 736 |                         rawComponentIndex = i;
 737 |                         break;
 738 |                     }
 739 |                 }
 740 |                 
 741 |                 if (rawComponentIndex === -1) {
 742 |                     resolve({
 743 |                         success: false,
 744 |                         error: `Could not find component index for setting property`
 745 |                     });
 746 |                     return;
 747 |                 }
 748 |                 
 749 |                 // 构建正确的属性路径
 750 |                 let propertyPath = `__comps__.${rawComponentIndex}.${property}`;
 751 |                 
 752 |                 // 特殊处理资源类属性
 753 |                 if (propertyType === 'asset' || propertyType === 'spriteFrame' || propertyType === 'prefab' || 
 754 |                     (propertyInfo.type === 'asset' && propertyType === 'string')) {
 755 |                     
 756 |                     console.log(`[ComponentTools] Setting asset reference:`, {
 757 |                         value: processedValue,
 758 |                         property: property,
 759 |                         propertyType: propertyType,
 760 |                         path: propertyPath
 761 |                     });
 762 |                     
 763 |                     // Determine asset type based on property name
 764 |                     let assetType = 'cc.SpriteFrame'; // default
 765 |                     if (property.toLowerCase().includes('texture')) {
 766 |                         assetType = 'cc.Texture2D';
 767 |                     } else if (property.toLowerCase().includes('material')) {
 768 |                         assetType = 'cc.Material';
 769 |                     } else if (property.toLowerCase().includes('font')) {
 770 |                         assetType = 'cc.Font';
 771 |                     } else if (property.toLowerCase().includes('clip')) {
 772 |                         assetType = 'cc.AudioClip';
 773 |                     } else if (propertyType === 'prefab') {
 774 |                         assetType = 'cc.Prefab';
 775 |                     }
 776 |                     
 777 |                     await Editor.Message.request('scene', 'set-property', {
 778 |                         uuid: nodeUuid,
 779 |                         path: propertyPath,
 780 |                         dump: { 
 781 |                             value: processedValue,
 782 |                             type: assetType
 783 |                         }
 784 |                     });
 785 |                 } else if (componentType === 'cc.UITransform' && (property === '_contentSize' || property === 'contentSize')) {
 786 |                     // Special handling for UITransform contentSize - set width and height separately
 787 |                     const width = Number(value.width) || 100;
 788 |                     const height = Number(value.height) || 100;
 789 |                     
 790 |                     // Set width first
 791 |                     await Editor.Message.request('scene', 'set-property', {
 792 |                         uuid: nodeUuid,
 793 |                         path: `__comps__.${rawComponentIndex}.width`,
 794 |                         dump: { value: width }
 795 |                     });
 796 |                     
 797 |                     // Then set height
 798 |                     await Editor.Message.request('scene', 'set-property', {
 799 |                         uuid: nodeUuid,
 800 |                         path: `__comps__.${rawComponentIndex}.height`,
 801 |                         dump: { value: height }
 802 |                     });
 803 |                 } else if (componentType === 'cc.UITransform' && (property === '_anchorPoint' || property === 'anchorPoint')) {
 804 |                     // Special handling for UITransform anchorPoint - set anchorX and anchorY separately
 805 |                     const anchorX = Number(value.x) || 0.5;
 806 |                     const anchorY = Number(value.y) || 0.5;
 807 |                     
 808 |                     // Set anchorX first
 809 |                     await Editor.Message.request('scene', 'set-property', {
 810 |                         uuid: nodeUuid,
 811 |                         path: `__comps__.${rawComponentIndex}.anchorX`,
 812 |                         dump: { value: anchorX }
 813 |                     });
 814 |                     
 815 |                     // Then set anchorY  
 816 |                     await Editor.Message.request('scene', 'set-property', {
 817 |                         uuid: nodeUuid,
 818 |                         path: `__comps__.${rawComponentIndex}.anchorY`,
 819 |                         dump: { value: anchorY }
 820 |                     });
 821 |                 } else if (propertyType === 'color' && processedValue && typeof processedValue === 'object') {
 822 |                     // 特殊处理颜色属性,确保RGBA值正确
 823 |                     // Cocos Creator颜色值范围是0-255
 824 |                     const colorValue = {
 825 |                         r: Math.min(255, Math.max(0, Number(processedValue.r) || 0)),
 826 |                         g: Math.min(255, Math.max(0, Number(processedValue.g) || 0)),
 827 |                         b: Math.min(255, Math.max(0, Number(processedValue.b) || 0)),
 828 |                         a: processedValue.a !== undefined ? Math.min(255, Math.max(0, Number(processedValue.a))) : 255
 829 |                     };
 830 |                     
 831 |                     console.log(`[ComponentTools] Setting color value:`, colorValue);
 832 |                     
 833 |                     await Editor.Message.request('scene', 'set-property', {
 834 |                         uuid: nodeUuid,
 835 |                         path: propertyPath,
 836 |                         dump: { 
 837 |                             value: colorValue,
 838 |                             type: 'cc.Color'
 839 |                         }
 840 |                     });
 841 |                 } else if (propertyType === 'vec3' && processedValue && typeof processedValue === 'object') {
 842 |                     // 特殊处理Vec3属性
 843 |                     const vec3Value = {
 844 |                         x: Number(processedValue.x) || 0,
 845 |                         y: Number(processedValue.y) || 0,
 846 |                         z: Number(processedValue.z) || 0
 847 |                     };
 848 |                     
 849 |                     await Editor.Message.request('scene', 'set-property', {
 850 |                         uuid: nodeUuid,
 851 |                         path: propertyPath,
 852 |                         dump: { 
 853 |                             value: vec3Value,
 854 |                             type: 'cc.Vec3'
 855 |                         }
 856 |                     });
 857 |                 } else if (propertyType === 'vec2' && processedValue && typeof processedValue === 'object') {
 858 |                     // 特殊处理Vec2属性
 859 |                     const vec2Value = {
 860 |                         x: Number(processedValue.x) || 0,
 861 |                         y: Number(processedValue.y) || 0
 862 |                     };
 863 |                     
 864 |                     await Editor.Message.request('scene', 'set-property', {
 865 |                         uuid: nodeUuid,
 866 |                         path: propertyPath,
 867 |                         dump: { 
 868 |                             value: vec2Value,
 869 |                             type: 'cc.Vec2'
 870 |                         }
 871 |                     });
 872 |                 } else if (propertyType === 'size' && processedValue && typeof processedValue === 'object') {
 873 |                     // 特殊处理Size属性
 874 |                     const sizeValue = {
 875 |                         width: Number(processedValue.width) || 0,
 876 |                         height: Number(processedValue.height) || 0
 877 |                     };
 878 |                     
 879 |                     await Editor.Message.request('scene', 'set-property', {
 880 |                         uuid: nodeUuid,
 881 |                         path: propertyPath,
 882 |                         dump: { 
 883 |                             value: sizeValue,
 884 |                             type: 'cc.Size'
 885 |                         }
 886 |                     });
 887 |                 } else if (propertyType === 'node' && processedValue && typeof processedValue === 'object' && 'uuid' in processedValue) {
 888 |                     // 特殊处理节点引用
 889 |                     console.log(`[ComponentTools] Setting node reference with UUID: ${processedValue.uuid}`);
 890 |                     await Editor.Message.request('scene', 'set-property', {
 891 |                         uuid: nodeUuid,
 892 |                         path: propertyPath,
 893 |                         dump: { 
 894 |                             value: processedValue,
 895 |                             type: 'cc.Node'
 896 |                         }
 897 |                     });
 898 |                 } else if (propertyType === 'component' && typeof processedValue === 'string') {
 899 |                     // 特殊处理组件引用:通过节点UUID找到组件的__id__
 900 |                     const targetNodeUuid = processedValue;
 901 |                     console.log(`[ComponentTools] Setting component reference - finding component on node: ${targetNodeUuid}`);
 902 |                     
 903 |                     // 从当前组件的属性元数据中获取期望的组件类型
 904 |                     let expectedComponentType = '';
 905 |                     
 906 |                     // 获取当前组件的详细信息,包括属性元数据
 907 |                     const currentComponentInfo = await this.getComponentInfo(nodeUuid, componentType);
 908 |                     if (currentComponentInfo.success && currentComponentInfo.data?.properties?.[property]) {
 909 |                         const propertyMeta = currentComponentInfo.data.properties[property];
 910 |                         
 911 |                         // 从属性元数据中提取组件类型信息
 912 |                         if (propertyMeta && typeof propertyMeta === 'object') {
 913 |                             // 检查是否有type字段指示组件类型
 914 |                             if (propertyMeta.type) {
 915 |                                 expectedComponentType = propertyMeta.type;
 916 |                             } else if (propertyMeta.ctor) {
 917 |                                 // 有些属性可能使用ctor字段
 918 |                                 expectedComponentType = propertyMeta.ctor;
 919 |                             } else if (propertyMeta.extends && Array.isArray(propertyMeta.extends)) {
 920 |                                 // 检查extends数组,通常第一个是最具体的类型
 921 |                                 for (const extendType of propertyMeta.extends) {
 922 |                                     if (extendType.startsWith('cc.') && extendType !== 'cc.Component' && extendType !== 'cc.Object') {
 923 |                                         expectedComponentType = extendType;
 924 |                                         break;
 925 |                                     }
 926 |                                 }
 927 |                             }
 928 |                         }
 929 |                     }
 930 |                     
 931 |                     if (!expectedComponentType) {
 932 |                         throw new Error(`Unable to determine required component type for property '${property}' on component '${componentType}'. Property metadata may not contain type information.`);
 933 |                     }
 934 |                     
 935 |                     console.log(`[ComponentTools] Detected required component type: ${expectedComponentType} for property: ${property}`);
 936 |                     
 937 |                     try {
 938 |                         // 获取目标节点的组件信息
 939 |                         const targetNodeData = await Editor.Message.request('scene', 'query-node', targetNodeUuid);
 940 |                         if (!targetNodeData || !targetNodeData.__comps__) {
 941 |                             throw new Error(`Target node ${targetNodeUuid} not found or has no components`);
 942 |                         }
 943 |                         
 944 |                         // 打印目标节点的组件概览
 945 |                         console.log(`[ComponentTools] Target node ${targetNodeUuid} has ${targetNodeData.__comps__.length} components:`);
 946 |                         targetNodeData.__comps__.forEach((comp: any, index: number) => {
 947 |                             const sceneId = comp.value && comp.value.uuid && comp.value.uuid.value ? comp.value.uuid.value : 'unknown';
 948 |                             console.log(`[ComponentTools] Component ${index}: ${comp.type} (scene_id: ${sceneId})`);
 949 |                         });
 950 |                         
 951 |                         // 查找对应的组件
 952 |                         let targetComponent = null;
 953 |                         let componentId: string | null = null;
 954 |                         
 955 |                         // 在目标节点的_components数组中查找指定类型的组件
 956 |                         // 注意:__comps__和_components的索引是对应的
 957 |                         console.log(`[ComponentTools] Searching for component type: ${expectedComponentType}`);
 958 |                         
 959 |                         for (let i = 0; i < targetNodeData.__comps__.length; i++) {
 960 |                             const comp = targetNodeData.__comps__[i] as any;
 961 |                             console.log(`[ComponentTools] Checking component ${i}: type=${comp.type}, target=${expectedComponentType}`);
 962 |                             
 963 |                             if (comp.type === expectedComponentType) {
 964 |                                 targetComponent = comp;
 965 |                                 console.log(`[ComponentTools] Found matching component at index ${i}: ${comp.type}`);
 966 |                                 
 967 |                                 // 从组件的value.uuid.value中获取组件在场景中的ID
 968 |                                 if (comp.value && comp.value.uuid && comp.value.uuid.value) {
 969 |                                     componentId = comp.value.uuid.value;
 970 |                                     console.log(`[ComponentTools] Got componentId from comp.value.uuid.value: ${componentId}`);
 971 |                                 } else {
 972 |                                     console.log(`[ComponentTools] Component structure:`, {
 973 |                                         hasValue: !!comp.value,
 974 |                                         hasUuid: !!(comp.value && comp.value.uuid),
 975 |                                         hasUuidValue: !!(comp.value && comp.value.uuid && comp.value.uuid.value),
 976 |                                         uuidStructure: comp.value ? comp.value.uuid : 'No value'
 977 |                                     });
 978 |                                     throw new Error(`Unable to extract component ID from component structure`);
 979 |                                 }
 980 |                                 
 981 |                                 break;
 982 |                             }
 983 |                         }
 984 |                         
 985 |                         if (!targetComponent) {
 986 |                             // 如果没找到,列出可用组件让用户了解,显示场景中的真实ID
 987 |                             const availableComponents = targetNodeData.__comps__.map((comp: any, index: number) => {
 988 |                                 let sceneId = 'unknown';
 989 |                                 // 从组件的value.uuid.value获取场景ID
 990 |                                 if (comp.value && comp.value.uuid && comp.value.uuid.value) {
 991 |                                     sceneId = comp.value.uuid.value;
 992 |                                 }
 993 |                                 return `${comp.type}(scene_id:${sceneId})`;
 994 |                             });
 995 |                             throw new Error(`Component type '${expectedComponentType}' not found on node ${targetNodeUuid}. Available components: ${availableComponents.join(', ')}`);
 996 |                         }
 997 |                         
 998 |                         console.log(`[ComponentTools] Found component ${expectedComponentType} with scene ID: ${componentId} on node ${targetNodeUuid}`);
 999 |                         
1000 |                         // 更新期望值为实际的组件ID对象格式,用于后续验证
1001 |                         if (componentId) {
1002 |                             actualExpectedValue = { uuid: componentId };
1003 |                         }
1004 |                         
1005 |                         // 尝试使用与节点/资源引用相同的格式:{uuid: componentId}
1006 |                         // 测试看是否能正确设置组件引用
1007 |                         await Editor.Message.request('scene', 'set-property', {
1008 |                             uuid: nodeUuid,
1009 |                             path: propertyPath,
1010 |                             dump: { 
1011 |                                 value: { uuid: componentId },  // 使用对象格式,像节点/资源引用一样
1012 |                                 type: expectedComponentType
1013 |                             }
1014 |                         });
1015 |                         
1016 |                     } catch (error) {
1017 |                         console.error(`[ComponentTools] Error setting component reference:`, error);
1018 |                         throw error;
1019 |                     }
1020 |                 } else if (propertyType === 'nodeArray' && Array.isArray(processedValue)) {
1021 |                     // 特殊处理节点数组 - 保持预处理的格式
1022 |                     console.log(`[ComponentTools] Setting node array:`, processedValue);
1023 |                     
1024 |                     await Editor.Message.request('scene', 'set-property', {
1025 |                         uuid: nodeUuid,
1026 |                         path: propertyPath,
1027 |                         dump: { 
1028 |                             value: processedValue  // 保持 [{uuid: "..."}, {uuid: "..."}] 格式
1029 |                         }
1030 |                     });
1031 |                 } else if (propertyType === 'colorArray' && Array.isArray(processedValue)) {
1032 |                     // 特殊处理颜色数组
1033 |                     const colorArrayValue = processedValue.map((item: any) => {
1034 |                         if (item && typeof item === 'object' && 'r' in item) {
1035 |                             return {
1036 |                                 r: Math.min(255, Math.max(0, Number(item.r) || 0)),
1037 |                                 g: Math.min(255, Math.max(0, Number(item.g) || 0)),
1038 |                                 b: Math.min(255, Math.max(0, Number(item.b) || 0)),
1039 |                                 a: item.a !== undefined ? Math.min(255, Math.max(0, Number(item.a))) : 255
1040 |                             };
1041 |                         } else {
1042 |                             return { r: 255, g: 255, b: 255, a: 255 };
1043 |                         }
1044 |                     });
1045 |                     
1046 |                     await Editor.Message.request('scene', 'set-property', {
1047 |                         uuid: nodeUuid,
1048 |                         path: propertyPath,
1049 |                         dump: { 
1050 |                             value: colorArrayValue,
1051 |                             type: 'cc.Color'
1052 |                         }
1053 |                     });
1054 |                 } else {
1055 |                     // Normal property setting for non-asset properties
1056 |                     await Editor.Message.request('scene', 'set-property', {
1057 |                         uuid: nodeUuid,
1058 |                         path: propertyPath,
1059 |                         dump: { value: processedValue }
1060 |                     });
1061 |                 }
1062 |                 
1063 |                 // Step 5: 等待Editor完成更新,然后验证设置结果
1064 |                 await new Promise(resolve => setTimeout(resolve, 200)); // 等待200ms让Editor完成更新
1065 |                 
1066 |                 const verification = await this.verifyPropertyChange(nodeUuid, componentType, property, originalValue, actualExpectedValue);
1067 |                 
1068 |                 resolve({
1069 |                     success: true,
1070 |                     message: `Successfully set ${componentType}.${property}`,
1071 |                     data: {
1072 |                         nodeUuid,
1073 |                         componentType,
1074 |                         property,
1075 |                         actualValue: verification.actualValue,
1076 |                         changeVerified: verification.verified
1077 |                     }
1078 |                 });
1079 |                 
1080 |             } catch (error: any) {
1081 |                 console.error(`[ComponentTools] Error setting property:`, error);
1082 |                 resolve({
1083 |                     success: false,
1084 |                     error: `Failed to set property: ${error.message}`
1085 |                 });
1086 |             }
1087 |         });
1088 |     }
1089 | 
1090 | 
1091 |     private async attachScript(nodeUuid: string, scriptPath: string): Promise<ToolResponse> {
1092 |         return new Promise(async (resolve) => {
1093 |             // 从脚本路径提取组件类名
1094 |             const scriptName = scriptPath.split('/').pop()?.replace('.ts', '').replace('.js', '');
1095 |             if (!scriptName) {
1096 |                 resolve({ success: false, error: 'Invalid script path' });
1097 |                 return;
1098 |             }
1099 |             // 先查找节点上是否已存在该脚本组件
1100 |             const allComponentsInfo = await this.getComponents(nodeUuid);
1101 |             if (allComponentsInfo.success && allComponentsInfo.data?.components) {
1102 |                 const existingScript = allComponentsInfo.data.components.find((comp: any) => comp.type === scriptName);
1103 |                 if (existingScript) {
1104 |                     resolve({
1105 |                         success: true,
1106 |                         message: `Script '${scriptName}' already exists on node`,
1107 |                         data: {
1108 |                             nodeUuid: nodeUuid,
1109 |                             componentName: scriptName,
1110 |                             existing: true
1111 |                         }
1112 |                     });
1113 |                     return;
1114 |                 }
1115 |             }
1116 |             // 首先尝试直接使用脚本名称作为组件类型
1117 |             Editor.Message.request('scene', 'create-component', {
1118 |                 uuid: nodeUuid,
1119 |                 component: scriptName  // 使用脚本名称而非UUID
1120 |             }).then(async (result: any) => {
1121 |                 // 等待一段时间让Editor完成组件添加
1122 |                 await new Promise(resolve => setTimeout(resolve, 100));
1123 |                 // 重新查询节点信息验证脚本是否真的添加成功
1124 |                 const allComponentsInfo2 = await this.getComponents(nodeUuid);
1125 |                 if (allComponentsInfo2.success && allComponentsInfo2.data?.components) {
1126 |                     const addedScript = allComponentsInfo2.data.components.find((comp: any) => comp.type === scriptName);
1127 |                     if (addedScript) {
1128 |                         resolve({
1129 |                             success: true,
1130 |                             message: `Script '${scriptName}' attached successfully`,
1131 |                             data: {
1132 |                                 nodeUuid: nodeUuid,
1133 |                                 componentName: scriptName,
1134 |                                 existing: false
1135 |                             }
1136 |                         });
1137 |                     } else {
1138 |                         resolve({
1139 |                             success: false,
1140 |                             error: `Script '${scriptName}' was not found on node after addition. Available components: ${allComponentsInfo2.data.components.map((c: any) => c.type).join(', ')}`
1141 |                         });
1142 |                     }
1143 |                 } else {
1144 |                     resolve({
1145 |                         success: false,
1146 |                         error: `Failed to verify script addition: ${allComponentsInfo2.error || 'Unable to get node components'}`
1147 |                     });
1148 |                 }
1149 |             }).catch((err: Error) => {
1150 |                 // 备用方案:使用场景脚本
1151 |                 const options = {
1152 |                     name: 'cocos-mcp-server',
1153 |                     method: 'attachScript',
1154 |                     args: [nodeUuid, scriptPath]
1155 |                 };
1156 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
1157 |                     resolve(result);
1158 |                 }).catch(() => {
1159 |                     resolve({ 
1160 |                         success: false, 
1161 |                         error: `Failed to attach script '${scriptName}': ${err.message}`,
1162 |                         instruction: 'Please ensure the script is properly compiled and exported as a Component class. You can also manually attach the script through the Properties panel in the editor.'
1163 |                     });
1164 |                 });
1165 |             });
1166 |         });
1167 |     }
1168 | 
1169 |     private async getAvailableComponents(category: string = 'all'): Promise<ToolResponse> {
1170 |         const componentCategories: Record<string, string[]> = {
1171 |             renderer: ['cc.Sprite', 'cc.Label', 'cc.RichText', 'cc.Mask', 'cc.Graphics'],
1172 |             ui: ['cc.Button', 'cc.Toggle', 'cc.Slider', 'cc.ScrollView', 'cc.EditBox', 'cc.ProgressBar'],
1173 |             physics: ['cc.RigidBody2D', 'cc.BoxCollider2D', 'cc.CircleCollider2D', 'cc.PolygonCollider2D'],
1174 |             animation: ['cc.Animation', 'cc.AnimationClip', 'cc.SkeletalAnimation'],
1175 |             audio: ['cc.AudioSource'],
1176 |             layout: ['cc.Layout', 'cc.Widget', 'cc.PageView', 'cc.PageViewIndicator'],
1177 |             effects: ['cc.MotionStreak', 'cc.ParticleSystem2D'],
1178 |             camera: ['cc.Camera'],
1179 |             light: ['cc.Light', 'cc.DirectionalLight', 'cc.PointLight', 'cc.SpotLight']
1180 |         };
1181 | 
1182 |         let components: string[] = [];
1183 |         
1184 |         if (category === 'all') {
1185 |             for (const cat in componentCategories) {
1186 |                 components = components.concat(componentCategories[cat]);
1187 |             }
1188 |         } else if (componentCategories[category]) {
1189 |             components = componentCategories[category];
1190 |         }
1191 | 
1192 |         return {
1193 |             success: true,
1194 |             data: {
1195 |                 category: category,
1196 |                 components: components
1197 |             }
1198 |         };
1199 |     }
1200 | 
1201 |     private isValidPropertyDescriptor(propData: any): boolean {
1202 |         // 检查是否是有效的属性描述对象
1203 |         if (typeof propData !== 'object' || propData === null) {
1204 |             return false;
1205 |         }
1206 |         
1207 |         try {
1208 |             const keys = Object.keys(propData);
1209 |             
1210 |             // 避免遍历简单的数值对象(如 {width: 200, height: 150})
1211 |             const isSimpleValueObject = keys.every(key => {
1212 |                 const value = propData[key];
1213 |                 return typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean';
1214 |             });
1215 |             
1216 |             if (isSimpleValueObject) {
1217 |                 return false;
1218 |             }
1219 |             
1220 |             // 检查是否包含属性描述符的特征字段,不使用'in'操作符
1221 |             const hasName = keys.includes('name');
1222 |             const hasValue = keys.includes('value');
1223 |             const hasType = keys.includes('type');
1224 |             const hasDisplayName = keys.includes('displayName');
1225 |             const hasReadonly = keys.includes('readonly');
1226 |             
1227 |             // 必须包含name或value字段,且通常还有type字段
1228 |             const hasValidStructure = (hasName || hasValue) && (hasType || hasDisplayName || hasReadonly);
1229 |             
1230 |             // 额外检查:如果有default字段且结构复杂,避免深度遍历
1231 |             if (keys.includes('default') && propData.default && typeof propData.default === 'object') {
1232 |                 const defaultKeys = Object.keys(propData.default);
1233 |                 if (defaultKeys.includes('value') && typeof propData.default.value === 'object') {
1234 |                     // 这种情况下,我们只返回顶层属性,不深入遍历default.value
1235 |                     return hasValidStructure;
1236 |                 }
1237 |             }
1238 |             
1239 |             return hasValidStructure;
1240 |         } catch (error) {
1241 |             console.warn(`[isValidPropertyDescriptor] Error checking property descriptor:`, error);
1242 |             return false;
1243 |         }
1244 |     }
1245 | 
1246 |     private analyzeProperty(component: any, propertyName: string): { exists: boolean; type: string; availableProperties: string[]; originalValue: any } {
1247 |         // 从复杂的组件结构中提取可用属性
1248 |         const availableProperties: string[] = [];
1249 |         let propertyValue: any = undefined;
1250 |         let propertyExists = false;
1251 |         
1252 |         // 尝试多种方式查找属性:
1253 |         // 1. 直接属性访问
1254 |         if (Object.prototype.hasOwnProperty.call(component, propertyName)) {
1255 |             propertyValue = component[propertyName];
1256 |             propertyExists = true;
1257 |         }
1258 |         
1259 |         // 2. 从嵌套结构中查找 (如从测试数据看到的复杂结构)
1260 |         if (!propertyExists && component.properties && typeof component.properties === 'object') {
1261 |             // 首先检查properties.value是否存在(这是我们在getComponents中看到的结构)
1262 |             if (component.properties.value && typeof component.properties.value === 'object') {
1263 |                 const valueObj = component.properties.value;
1264 |                 for (const [key, propData] of Object.entries(valueObj)) {
1265 |                     // 检查propData是否是一个有效的属性描述对象
1266 |                     // 确保propData是对象且包含预期的属性结构
1267 |                     if (this.isValidPropertyDescriptor(propData)) {
1268 |                         const propInfo = propData as any;
1269 |                         availableProperties.push(key);
1270 |                         if (key === propertyName) {
1271 |                             // 优先使用value属性,如果没有则使用propData本身
1272 |                             try {
1273 |                                 const propKeys = Object.keys(propInfo);
1274 |                                 propertyValue = propKeys.includes('value') ? propInfo.value : propInfo;
1275 |                             } catch (error) {
1276 |                                 // 如果检查失败,直接使用propInfo
1277 |                                 propertyValue = propInfo;
1278 |                             }
1279 |                             propertyExists = true;
1280 |                         }
1281 |                     }
1282 |                 }
1283 |             } else {
1284 |                 // 备用方案:直接从properties查找
1285 |                 for (const [key, propData] of Object.entries(component.properties)) {
1286 |                     if (this.isValidPropertyDescriptor(propData)) {
1287 |                         const propInfo = propData as any;
1288 |                         availableProperties.push(key);
1289 |                         if (key === propertyName) {
1290 |                             // 优先使用value属性,如果没有则使用propData本身
1291 |                             try {
1292 |                                 const propKeys = Object.keys(propInfo);
1293 |                                 propertyValue = propKeys.includes('value') ? propInfo.value : propInfo;
1294 |                             } catch (error) {
1295 |                                 // 如果检查失败,直接使用propInfo
1296 |                                 propertyValue = propInfo;
1297 |                             }
1298 |                             propertyExists = true;
1299 |                         }
1300 |                     }
1301 |                 }
1302 |             }
1303 |         }
1304 |         
1305 |         // 3. 从直接属性中提取简单属性名
1306 |         if (availableProperties.length === 0) {
1307 |             for (const key of Object.keys(component)) {
1308 |                 if (!key.startsWith('_') && !['__type__', 'cid', 'node', 'uuid', 'name', 'enabled', 'type', 'readonly', 'visible'].includes(key)) {
1309 |                     availableProperties.push(key);
1310 |                 }
1311 |             }
1312 |         }
1313 |         
1314 |         if (!propertyExists) {
1315 |             return {
1316 |                 exists: false,
1317 |                 type: 'unknown',
1318 |                 availableProperties,
1319 |                 originalValue: undefined
1320 |             };
1321 |         }
1322 |         
1323 |         let type = 'unknown';
1324 |         
1325 |         // 智能类型检测
1326 |         if (Array.isArray(propertyValue)) {
1327 |             // 数组类型检测
1328 |             if (propertyName.toLowerCase().includes('node')) {
1329 |                 type = 'nodeArray';
1330 |             } else if (propertyName.toLowerCase().includes('color')) {
1331 |                 type = 'colorArray';
1332 |             } else {
1333 |                 type = 'array';
1334 |             }
1335 |         } else if (typeof propertyValue === 'string') {
1336 |             // Check if property name suggests it's an asset
1337 |             if (['spriteFrame', 'texture', 'material', 'font', 'clip', 'prefab'].includes(propertyName.toLowerCase())) {
1338 |                 type = 'asset';
1339 |             } else {
1340 |                 type = 'string';
1341 |             }
1342 |         } else if (typeof propertyValue === 'number') {
1343 |             type = 'number';
1344 |         } else if (typeof propertyValue === 'boolean') {
1345 |             type = 'boolean';
1346 |         } else if (propertyValue && typeof propertyValue === 'object') {
1347 |             try {
1348 |                 const keys = Object.keys(propertyValue);
1349 |                 if (keys.includes('r') && keys.includes('g') && keys.includes('b')) {
1350 |                     type = 'color';
1351 |                 } else if (keys.includes('x') && keys.includes('y')) {
1352 |                     type = propertyValue.z !== undefined ? 'vec3' : 'vec2';
1353 |                 } else if (keys.includes('width') && keys.includes('height')) {
1354 |                     type = 'size';
1355 |                 } else if (keys.includes('uuid') || keys.includes('__uuid__')) {
1356 |                     // 检查是否是节点引用(通过属性名或__id__属性判断)
1357 |                     if (propertyName.toLowerCase().includes('node') || 
1358 |                         propertyName.toLowerCase().includes('target') ||
1359 |                         keys.includes('__id__')) {
1360 |                         type = 'node';
1361 |                     } else {
1362 |                         type = 'asset';
1363 |                     }
1364 |                 } else if (keys.includes('__id__')) {
1365 |                     // 节点引用特征
1366 |                     type = 'node';
1367 |                 } else {
1368 |                     type = 'object';
1369 |                 }
1370 |             } catch (error) {
1371 |                 console.warn(`[analyzeProperty] Error checking property type for: ${JSON.stringify(propertyValue)}`);
1372 |                 type = 'object';
1373 |             }
1374 |         } else if (propertyValue === null || propertyValue === undefined) {
1375 |             // For null/undefined values, check property name to determine type
1376 |             if (['spriteFrame', 'texture', 'material', 'font', 'clip', 'prefab'].includes(propertyName.toLowerCase())) {
1377 |                 type = 'asset';
1378 |             } else if (propertyName.toLowerCase().includes('node') || 
1379 |                       propertyName.toLowerCase().includes('target')) {
1380 |                 type = 'node';
1381 |             } else if (propertyName.toLowerCase().includes('component')) {
1382 |                 type = 'component';
1383 |             } else {
1384 |                 type = 'unknown';
1385 |             }
1386 |         }
1387 |         
1388 |         return {
1389 |             exists: true,
1390 |             type,
1391 |             availableProperties,
1392 |             originalValue: propertyValue
1393 |         };
1394 |     }
1395 | 
1396 |     private smartConvertValue(inputValue: any, propertyInfo: any): any {
1397 |         const { type, originalValue } = propertyInfo;
1398 |         
1399 |         console.log(`[smartConvertValue] Converting ${JSON.stringify(inputValue)} to type: ${type}`);
1400 |         
1401 |         switch (type) {
1402 |             case 'string':
1403 |                 return String(inputValue);
1404 |                 
1405 |             case 'number':
1406 |                 return Number(inputValue);
1407 |                 
1408 |             case 'boolean':
1409 |                 if (typeof inputValue === 'boolean') return inputValue;
1410 |                 if (typeof inputValue === 'string') {
1411 |                     return inputValue.toLowerCase() === 'true' || inputValue === '1';
1412 |                 }
1413 |                 return Boolean(inputValue);
1414 |                 
1415 |             case 'color':
1416 |                 // 优化的颜色处理,支持多种输入格式
1417 |                 if (typeof inputValue === 'string') {
1418 |                     // 字符串格式:十六进制、颜色名称、rgb()/rgba()
1419 |                     return this.parseColorString(inputValue);
1420 |                 } else if (typeof inputValue === 'object' && inputValue !== null) {
1421 |                     try {
1422 |                         const inputKeys = Object.keys(inputValue);
1423 |                         // 如果输入是颜色对象,验证并转换
1424 |                         if (inputKeys.includes('r') || inputKeys.includes('g') || inputKeys.includes('b')) {
1425 |                             return {
1426 |                                 r: Math.min(255, Math.max(0, Number(inputValue.r) || 0)),
1427 |                                 g: Math.min(255, Math.max(0, Number(inputValue.g) || 0)),
1428 |                                 b: Math.min(255, Math.max(0, Number(inputValue.b) || 0)),
1429 |                                 a: inputValue.a !== undefined ? Math.min(255, Math.max(0, Number(inputValue.a))) : 255
1430 |                             };
1431 |                         }
1432 |                     } catch (error) {
1433 |                         console.warn(`[smartConvertValue] Invalid color object: ${JSON.stringify(inputValue)}`);
1434 |                     }
1435 |                 }
1436 |                 // 如果有原值,保持原值结构并更新提供的值
1437 |                 if (originalValue && typeof originalValue === 'object') {
1438 |                     try {
1439 |                         const inputKeys = typeof inputValue === 'object' && inputValue ? Object.keys(inputValue) : [];
1440 |                         return {
1441 |                             r: inputKeys.includes('r') ? Math.min(255, Math.max(0, Number(inputValue.r))) : (originalValue.r || 255),
1442 |                             g: inputKeys.includes('g') ? Math.min(255, Math.max(0, Number(inputValue.g))) : (originalValue.g || 255),
1443 |                             b: inputKeys.includes('b') ? Math.min(255, Math.max(0, Number(inputValue.b))) : (originalValue.b || 255),
1444 |                             a: inputKeys.includes('a') ? Math.min(255, Math.max(0, Number(inputValue.a))) : (originalValue.a || 255)
1445 |                         };
1446 |                     } catch (error) {
1447 |                         console.warn(`[smartConvertValue] Error processing color with original value: ${error}`);
1448 |                     }
1449 |                 }
1450 |                 // 默认返回白色
1451 |                 console.warn(`[smartConvertValue] Using default white color for invalid input: ${JSON.stringify(inputValue)}`);
1452 |                 return { r: 255, g: 255, b: 255, a: 255 };
1453 |                 
1454 |             case 'vec2':
1455 |                 if (typeof inputValue === 'object' && inputValue !== null) {
1456 |                     return {
1457 |                         x: Number(inputValue.x) || originalValue.x || 0,
1458 |                         y: Number(inputValue.y) || originalValue.y || 0
1459 |                     };
1460 |                 }
1461 |                 return originalValue;
1462 |                 
1463 |             case 'vec3':
1464 |                 if (typeof inputValue === 'object' && inputValue !== null) {
1465 |                     return {
1466 |                         x: Number(inputValue.x) || originalValue.x || 0,
1467 |                         y: Number(inputValue.y) || originalValue.y || 0,
1468 |                         z: Number(inputValue.z) || originalValue.z || 0
1469 |                     };
1470 |                 }
1471 |                 return originalValue;
1472 |                 
1473 |             case 'size':
1474 |                 if (typeof inputValue === 'object' && inputValue !== null) {
1475 |                     return {
1476 |                         width: Number(inputValue.width) || originalValue.width || 100,
1477 |                         height: Number(inputValue.height) || originalValue.height || 100
1478 |                     };
1479 |                 }
1480 |                 return originalValue;
1481 |                 
1482 |             case 'node':
1483 |                 if (typeof inputValue === 'string') {
1484 |                     // 节点引用需要特殊处理
1485 |                     return inputValue;
1486 |                 } else if (typeof inputValue === 'object' && inputValue !== null) {
1487 |                     // 如果已经是对象形式,返回UUID或完整对象
1488 |                     return inputValue.uuid || inputValue;
1489 |                 }
1490 |                 return originalValue;
1491 |                 
1492 |             case 'asset':
1493 |                 if (typeof inputValue === 'string') {
1494 |                     // 如果输入是字符串路径,转换为asset对象
1495 |                     return { uuid: inputValue };
1496 |                 } else if (typeof inputValue === 'object' && inputValue !== null) {
1497 |                     return inputValue;
1498 |                 }
1499 |                 return originalValue;
1500 |                 
1501 |             default:
1502 |                 // 对于未知类型,尽量保持原有结构
1503 |                 if (typeof inputValue === typeof originalValue) {
1504 |                     return inputValue;
1505 |                 }
1506 |                 return originalValue;
1507 |         }
1508 |     }
1509 | 
1510 |         private parseColorString(colorStr: string): { r: number; g: number; b: number; a: number } {
1511 |         const str = colorStr.trim();
1512 |         
1513 |         // 只支持十六进制格式 #RRGGBB 或 #RRGGBBAA
1514 |         if (str.startsWith('#')) {
1515 |             if (str.length === 7) { // #RRGGBB
1516 |                 const r = parseInt(str.substring(1, 3), 16);
1517 |                 const g = parseInt(str.substring(3, 5), 16);
1518 |                 const b = parseInt(str.substring(5, 7), 16);
1519 |                 return { r, g, b, a: 255 };
1520 |             } else if (str.length === 9) { // #RRGGBBAA
1521 |                 const r = parseInt(str.substring(1, 3), 16);
1522 |                 const g = parseInt(str.substring(3, 5), 16);
1523 |                 const b = parseInt(str.substring(5, 7), 16);
1524 |                 const a = parseInt(str.substring(7, 9), 16);
1525 |                 return { r, g, b, a };
1526 |             }
1527 |         }
1528 |         
1529 |         // 如果不是有效的十六进制格式,返回错误提示
1530 |         throw new Error(`Invalid color format: "${colorStr}". Only hexadecimal format is supported (e.g., "#FF0000" or "#FF0000FF")`);
1531 |     }
1532 | 
1533 |     private async verifyPropertyChange(nodeUuid: string, componentType: string, property: string, originalValue: any, expectedValue: any): Promise<{ verified: boolean; actualValue: any; fullData: any }> {
1534 |         console.log(`[verifyPropertyChange] Starting verification for ${componentType}.${property}`);
1535 |         console.log(`[verifyPropertyChange] Expected value:`, JSON.stringify(expectedValue));
1536 |         console.log(`[verifyPropertyChange] Original value:`, JSON.stringify(originalValue));
1537 |         
1538 |         try {
1539 |             // 重新获取组件信息进行验证
1540 |             console.log(`[verifyPropertyChange] Calling getComponentInfo...`);
1541 |             const componentInfo = await this.getComponentInfo(nodeUuid, componentType);
1542 |             console.log(`[verifyPropertyChange] getComponentInfo success:`, componentInfo.success);
1543 |             
1544 |             const allComponents = await this.getComponents(nodeUuid);
1545 |             console.log(`[verifyPropertyChange] getComponents success:`, allComponents.success);
1546 |             
1547 |             if (componentInfo.success && componentInfo.data) {
1548 |                 console.log(`[verifyPropertyChange] Component data available, extracting property '${property}'`);
1549 |                 const allPropertyNames = Object.keys(componentInfo.data.properties || {});
1550 |                 console.log(`[verifyPropertyChange] Available properties:`, allPropertyNames);
1551 |                 const propertyData = componentInfo.data.properties?.[property];
1552 |                 console.log(`[verifyPropertyChange] Raw property data for '${property}':`, JSON.stringify(propertyData));
1553 |                 
1554 |                 // 从属性数据中提取实际值
1555 |                 let actualValue = propertyData;
1556 |                 console.log(`[verifyPropertyChange] Initial actualValue:`, JSON.stringify(actualValue));
1557 |                 
1558 |                 if (propertyData && typeof propertyData === 'object' && 'value' in propertyData) {
1559 |                     actualValue = propertyData.value;
1560 |                     console.log(`[verifyPropertyChange] Extracted actualValue from .value:`, JSON.stringify(actualValue));
1561 |                 } else {
1562 |                     console.log(`[verifyPropertyChange] No .value property found, using raw data`);
1563 |                 }
1564 |                 
1565 |                 // 修复验证逻辑:检查实际值是否匹配期望值
1566 |                 let verified = false;
1567 |                 
1568 |                 if (typeof expectedValue === 'object' && expectedValue !== null && 'uuid' in expectedValue) {
1569 |                     // 对于引用类型(节点/组件/资源),比较UUID
1570 |                     const actualUuid = actualValue && typeof actualValue === 'object' && 'uuid' in actualValue ? actualValue.uuid : '';
1571 |                     const expectedUuid = expectedValue.uuid || '';
1572 |                     verified = actualUuid === expectedUuid && expectedUuid !== '';
1573 |                     
1574 |                     console.log(`[verifyPropertyChange] Reference comparison:`);
1575 |                     console.log(`  - Expected UUID: "${expectedUuid}"`);
1576 |                     console.log(`  - Actual UUID: "${actualUuid}"`);
1577 |                     console.log(`  - UUID match: ${actualUuid === expectedUuid}`);
1578 |                     console.log(`  - UUID not empty: ${expectedUuid !== ''}`);
1579 |                     console.log(`  - Final verified: ${verified}`);
1580 |                 } else {
1581 |                     // 对于其他类型,直接比较值
1582 |                     console.log(`[verifyPropertyChange] Value comparison:`);
1583 |                     console.log(`  - Expected type: ${typeof expectedValue}`);
1584 |                     console.log(`  - Actual type: ${typeof actualValue}`);
1585 |                     
1586 |                     if (typeof actualValue === typeof expectedValue) {
1587 |                         if (typeof actualValue === 'object' && actualValue !== null && expectedValue !== null) {
1588 |                             // 对象类型的深度比较
1589 |                             verified = JSON.stringify(actualValue) === JSON.stringify(expectedValue);
1590 |                             console.log(`  - Object comparison (JSON): ${verified}`);
1591 |                         } else {
1592 |                             // 基本类型的直接比较
1593 |                             verified = actualValue === expectedValue;
1594 |                             console.log(`  - Direct comparison: ${verified}`);
1595 |                         }
1596 |                     } else {
1597 |                         // 类型不匹配时的特殊处理(如数字和字符串)
1598 |                         const stringMatch = String(actualValue) === String(expectedValue);
1599 |                         const numberMatch = Number(actualValue) === Number(expectedValue);
1600 |                         verified = stringMatch || numberMatch;
1601 |                         console.log(`  - String match: ${stringMatch}`);
1602 |                         console.log(`  - Number match: ${numberMatch}`);
1603 |                         console.log(`  - Type mismatch verified: ${verified}`);
1604 |                     }
1605 |                 }
1606 |                 
1607 |                 console.log(`[verifyPropertyChange] Final verification result: ${verified}`);
1608 |                 console.log(`[verifyPropertyChange] Final actualValue:`, JSON.stringify(actualValue));
1609 |                 
1610 |                 const result = {
1611 |                     verified,
1612 |                     actualValue,
1613 |                     fullData: {
1614 |                         // 只返回修改的属性信息,不返回完整组件数据
1615 |                         modifiedProperty: {
1616 |                             name: property,
1617 |                             before: originalValue,
1618 |                             expected: expectedValue,
1619 |                             actual: actualValue,
1620 |                             verified,
1621 |                             propertyMetadata: propertyData // 只包含这个属性的元数据
1622 |                         },
1623 |                         // 简化的组件信息
1624 |                         componentSummary: {
1625 |                             nodeUuid,
1626 |                             componentType,
1627 |                             totalProperties: Object.keys(componentInfo.data?.properties || {}).length
1628 |                         }
1629 |                     }
1630 |                 };
1631 |                 
1632 |                 console.log(`[verifyPropertyChange] Returning result:`, JSON.stringify(result, null, 2));
1633 |                 return result;
1634 |             } else {
1635 |                 console.log(`[verifyPropertyChange] ComponentInfo failed or no data:`, componentInfo);
1636 |             }
1637 |         } catch (error) {
1638 |             console.error('[verifyPropertyChange] Verification failed with error:', error);
1639 |             console.error('[verifyPropertyChange] Error stack:', error instanceof Error ? error.stack : 'No stack trace');
1640 |         }
1641 |         
1642 |         console.log(`[verifyPropertyChange] Returning fallback result`);
1643 |         return {
1644 |             verified: false,
1645 |             actualValue: undefined,
1646 |             fullData: null
1647 |         };
1648 |     }
1649 | 
1650 |     /**
1651 |      * 检测是否为节点属性,如果是则重定向到对应的节点方法
1652 |      */
1653 |     private async checkAndRedirectNodeProperties(args: any): Promise<ToolResponse | null> {
1654 |         const { nodeUuid, componentType, property, propertyType, value } = args;
1655 |         
1656 |         // 检测是否为节点基础属性(应该使用 set_node_property)
1657 |         const nodeBasicProperties = [
1658 |             'name', 'active', 'layer', 'mobility', 'parent', 'children', 'hideFlags'
1659 |         ];
1660 |         
1661 |         // 检测是否为节点变换属性(应该使用 set_node_transform)
1662 |         const nodeTransformProperties = [
1663 |             'position', 'rotation', 'scale', 'eulerAngles', 'angle'
1664 |         ];
1665 |         
1666 |         // Detect attempts to set cc.Node properties (common mistake)
1667 |         if (componentType === 'cc.Node' || componentType === 'Node') {
1668 |             if (nodeBasicProperties.includes(property)) {
1669 |                 return {
1670 |                     success: false,
1671 |                                           error: `Property '${property}' is a node basic property, not a component property`,
1672 |                       instruction: `Please use set_node_property method to set node properties: set_node_property(uuid="${nodeUuid}", property="${property}", value=${JSON.stringify(value)})`
1673 |                   };
1674 |               } else if (nodeTransformProperties.includes(property)) {
1675 |                   return {
1676 |                       success: false,
1677 |                       error: `Property '${property}' is a node transform property, not a component property`,
1678 |                       instruction: `Please use set_node_transform method to set transform properties: set_node_transform(uuid="${nodeUuid}", ${property}=${JSON.stringify(value)})`
1679 |                   };
1680 |               }
1681 |           }
1682 |           
1683 |           // Detect common incorrect usage
1684 |           if (nodeBasicProperties.includes(property) || nodeTransformProperties.includes(property)) {
1685 |               const methodName = nodeTransformProperties.includes(property) ? 'set_node_transform' : 'set_node_property';
1686 |               return {
1687 |                   success: false,
1688 |                   error: `Property '${property}' is a node property, not a component property`,
1689 |                   instruction: `Property '${property}' should be set using ${methodName} method, not set_component_property. Please use: ${methodName}(uuid="${nodeUuid}", ${nodeTransformProperties.includes(property) ? property : `property="${property}"`}=${JSON.stringify(value)})`
1690 |               };
1691 |           }
1692 |           
1693 |           return null; // 不是节点属性,继续正常处理
1694 |       }
1695 | 
1696 |       /**
1697 |        * 生成组件建议信息
1698 |        */
1699 |       private generateComponentSuggestion(requestedType: string, availableTypes: string[], property: string): string {
1700 |           // 检查是否存在相似的组件类型
1701 |           const similarTypes = availableTypes.filter(type => 
1702 |               type.toLowerCase().includes(requestedType.toLowerCase()) || 
1703 |               requestedType.toLowerCase().includes(type.toLowerCase())
1704 |           );
1705 |           
1706 |           let instruction = '';
1707 |           
1708 |           if (similarTypes.length > 0) {
1709 |               instruction += `\n\n🔍 Found similar components: ${similarTypes.join(', ')}`;
1710 |               instruction += `\n💡 Suggestion: Perhaps you meant to set the '${similarTypes[0]}' component?`;
1711 |           }
1712 |           
1713 |           // Recommend possible components based on property name
1714 |           const propertyToComponentMap: Record<string, string[]> = {
1715 |               'string': ['cc.Label', 'cc.RichText', 'cc.EditBox'],
1716 |               'text': ['cc.Label', 'cc.RichText'],
1717 |               'fontSize': ['cc.Label', 'cc.RichText'],
1718 |               'spriteFrame': ['cc.Sprite'],
1719 |               'color': ['cc.Label', 'cc.Sprite', 'cc.Graphics'],
1720 |               'normalColor': ['cc.Button'],
1721 |               'pressedColor': ['cc.Button'],
1722 |               'target': ['cc.Button'],
1723 |               'contentSize': ['cc.UITransform'],
1724 |               'anchorPoint': ['cc.UITransform']
1725 |           };
1726 |           
1727 |           const recommendedComponents = propertyToComponentMap[property] || [];
1728 |           const availableRecommended = recommendedComponents.filter(comp => availableTypes.includes(comp));
1729 |           
1730 |           if (availableRecommended.length > 0) {
1731 |               instruction += `\n\n🎯 Based on property '${property}', recommended components: ${availableRecommended.join(', ')}`;
1732 |           }
1733 |           
1734 |           // Provide operation suggestions
1735 |           instruction += `\n\n📋 Suggested Actions:`;
1736 |           instruction += `\n1. Use get_components(nodeUuid="${requestedType.includes('uuid') ? 'YOUR_NODE_UUID' : 'nodeUuid'}") to view all components on the node`;
1737 |           instruction += `\n2. If you need to add a component, use add_component(nodeUuid="...", componentType="${requestedType}")`;
1738 |           instruction += `\n3. Verify that the component type name is correct (case-sensitive)`;
1739 |           
1740 |                   return instruction;
1741 |     }
1742 | 
1743 |     /**
1744 |      * 快速验证资源设置结果
1745 |      */
1746 |     private async quickVerifyAsset(nodeUuid: string, componentType: string, property: string): Promise<any> {
1747 |         try {
1748 |             const rawNodeData = await Editor.Message.request('scene', 'query-node', nodeUuid);
1749 |             if (!rawNodeData || !rawNodeData.__comps__) {
1750 |                 return null;
1751 |             }
1752 |             
1753 |             // 找到组件
1754 |             const component = rawNodeData.__comps__.find((comp: any) => {
1755 |                 const compType = comp.__type__ || comp.cid || comp.type;
1756 |                 return compType === componentType;
1757 |             });
1758 |             
1759 |             if (!component) {
1760 |                 return null;
1761 |             }
1762 |             
1763 |             // 提取属性值
1764 |             const properties = this.extractComponentProperties(component);
1765 |             const propertyData = properties[property];
1766 |             
1767 |             if (propertyData && typeof propertyData === 'object' && 'value' in propertyData) {
1768 |                 return propertyData.value;
1769 |             } else {
1770 |                 return propertyData;
1771 |             }
1772 |         } catch (error) {
1773 |             console.error(`[quickVerifyAsset] Error:`, error);
1774 |             return null;
1775 |         }
1776 |     }
1777 | }
```
Page 4/5FirstPrevNextLast