#
tokens: 36056/50000 3/37 files (page 3/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 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

--------------------------------------------------------------------------------
/FEATURE_GUIDE_EN.md:
--------------------------------------------------------------------------------

```markdown
   1 | # Cocos Creator MCP Server Feature Guide
   2 | 
   3 | ## Overview
   4 | 
   5 | The Cocos Creator MCP Server is a comprehensive Model Context Protocol (MCP) server plugin designed for Cocos Creator 3.8+, enabling AI assistants to interact with the Cocos Creator editor through standardized protocols.
   6 | 
   7 | This document provides detailed information about all available MCP tools and their usage.
   8 | 
   9 | ## Tool Categories
  10 | 
  11 | The MCP server provides **158 tools** organized into 13 main categories by functionality:
  12 | 
  13 | 1. [Scene Tools](#1-scene-tools)
  14 | 2. [Node Tools](#2-node-tools)
  15 | 3. [Component Management Tools](#3-component-management-tools)
  16 | 4. [Prefab Tools](#4-prefab-tools)
  17 | 5. [Project Control Tools](#5-project-control-tools)
  18 | 6. [Debug Tools](#6-debug-tools)
  19 | 7. [Preferences Tools](#7-preferences-tools)
  20 | 8. [Server Tools](#8-server-tools)
  21 | 9. [Broadcast Tools](#9-broadcast-tools)
  22 | 10. [Asset Advanced Tools](#10-asset-advanced-tools)
  23 | 11. [Reference Image Tools](#11-reference-image-tools)
  24 | 12. [Scene Advanced Tools](#12-scene-advanced-tools)
  25 | 13. [Scene View Tools](#13-scene-view-tools)
  26 | 
  27 | ---
  28 | 
  29 | ## 1. Scene Tools
  30 | 
  31 | ### 1.1 scene_get_current_scene
  32 | Get current scene information
  33 | 
  34 | **Parameters**: None
  35 | 
  36 | **Returns**: Current scene name, UUID, type, active status, and node count
  37 | 
  38 | **Example**:
  39 | ```json
  40 | {
  41 |   "tool": "scene_get_current_scene",
  42 |   "arguments": {}
  43 | }
  44 | ```
  45 | 
  46 | ### 1.2 scene_get_scene_list
  47 | Get all scenes in the project
  48 | 
  49 | **Parameters**: None
  50 | 
  51 | **Returns**: List of all scenes in the project, including names, paths, and UUIDs
  52 | 
  53 | **Example**:
  54 | ```json
  55 | {
  56 |   "tool": "scene_get_scene_list",
  57 |   "arguments": {}
  58 | }
  59 | ```
  60 | 
  61 | ### 1.3 scene_open_scene
  62 | Open a scene by path
  63 | 
  64 | **Parameters**:
  65 | - `scenePath` (string, required): Scene file path
  66 | 
  67 | **Example**:
  68 | ```json
  69 | {
  70 |   "tool": "scene_open_scene",
  71 |   "arguments": {
  72 |     "scenePath": "db://assets/scenes/GameScene.scene"
  73 |   }
  74 | }
  75 | ```
  76 | 
  77 | ### 1.4 scene_save_scene
  78 | Save current scene
  79 | 
  80 | **Parameters**: None
  81 | 
  82 | **Example**:
  83 | ```json
  84 | {
  85 |   "tool": "scene_save_scene",
  86 |   "arguments": {}
  87 | }
  88 | ```
  89 | 
  90 | ### 1.5 scene_create_scene
  91 | Create a new scene asset
  92 | 
  93 | **Parameters**:
  94 | - `sceneName` (string, required): Name of the new scene
  95 | - `savePath` (string, required): Path to save the scene
  96 | 
  97 | **Example**:
  98 | ```json
  99 | {
 100 |   "tool": "scene_create_scene",
 101 |   "arguments": {
 102 |     "sceneName": "NewLevel",
 103 |     "savePath": "db://assets/scenes/NewLevel.scene"
 104 |   }
 105 | }
 106 | ```
 107 | 
 108 | ### 1.6 scene_save_scene_as
 109 | Save scene as a new file
 110 | 
 111 | **Parameters**:
 112 | - `path` (string, required): Path to save the scene
 113 | 
 114 | **Example**:
 115 | ```json
 116 | {
 117 |   "tool": "scene_save_scene_as",
 118 |   "arguments": {
 119 |     "path": "db://assets/scenes/GameScene_Copy.scene"
 120 |   }
 121 | }
 122 | ```
 123 | 
 124 | ### 1.7 scene_close_scene
 125 | Close current scene
 126 | 
 127 | **Parameters**: None
 128 | 
 129 | **Example**:
 130 | ```json
 131 | {
 132 |   "tool": "scene_close_scene",
 133 |   "arguments": {}
 134 | }
 135 | ```
 136 | 
 137 | ### 1.8 scene_get_scene_hierarchy
 138 | Get the complete hierarchy of current scene
 139 | 
 140 | **Parameters**:
 141 | - `includeComponents` (boolean, optional): Whether to include component information, defaults to false
 142 | 
 143 | **Example**:
 144 | ```json
 145 | {
 146 |   "tool": "scene_get_scene_hierarchy",
 147 |   "arguments": {
 148 |     "includeComponents": true
 149 |   }
 150 | }
 151 | ```
 152 | 
 153 | ---
 154 | 
 155 | ## 2. Node Tools
 156 | 
 157 | ### 2.1 node_create_node
 158 | Create a new node in the scene
 159 | 
 160 | **Parameters**:
 161 | - `name` (string, required): Node name
 162 | - `parentUuid` (string, **strongly recommended**): Parent node UUID. **Important**: It is strongly recommended to always provide this parameter. Use `get_current_scene` or `get_all_nodes` to find parent node UUIDs. If not provided, the node will be created at the scene root.
 163 | - `nodeType` (string, optional): Node type, options: `Node`, `2DNode`, `3DNode`, defaults to `Node`
 164 | - `siblingIndex` (number, optional): Sibling index, -1 means append at end, defaults to -1
 165 | 
 166 | **Important Note**: To ensure the node is created at the expected location, always provide the `parentUuid` parameter. You can obtain parent node UUIDs by:
 167 | - Using `scene_get_current_scene` to get the scene root node UUID
 168 | - Using `node_get_all_nodes` to view all nodes and their UUIDs
 169 | - Using `node_find_node_by_name` to find specific node UUIDs
 170 | 
 171 | **Example**:
 172 | ```json
 173 | {
 174 |   "tool": "node_create_node",
 175 |   "arguments": {
 176 |     "name": "PlayerNode",
 177 |     "nodeType": "2DNode",
 178 |     "parentUuid": "parent-uuid-here"
 179 |   }
 180 | }
 181 | ```
 182 | 
 183 | ### 2.2 node_get_node_info
 184 | Get node information by UUID
 185 | 
 186 | **Parameters**:
 187 | - `uuid` (string, required): Node UUID
 188 | 
 189 | **Example**:
 190 | ```json
 191 | {
 192 |   "tool": "node_get_node_info",
 193 |   "arguments": {
 194 |     "uuid": "node-uuid-here"
 195 |   }
 196 | }
 197 | ```
 198 | 
 199 | ### 2.3 node_find_nodes
 200 | Find nodes by name pattern
 201 | 
 202 | **Parameters**:
 203 | - `pattern` (string, required): Name pattern to search
 204 | - `exactMatch` (boolean, optional): Whether to match exactly, defaults to false
 205 | 
 206 | **Example**:
 207 | ```json
 208 | {
 209 |   "tool": "node_find_nodes",
 210 |   "arguments": {
 211 |     "pattern": "Enemy",
 212 |     "exactMatch": false
 213 |   }
 214 | }
 215 | ```
 216 | 
 217 | ### 2.4 node_find_node_by_name
 218 | Find the first node by exact name
 219 | 
 220 | **Parameters**:
 221 | - `name` (string, required): Node name to find
 222 | 
 223 | **Example**:
 224 | ```json
 225 | {
 226 |   "tool": "node_find_node_by_name",
 227 |   "arguments": {
 228 |     "name": "Player"
 229 |   }
 230 | }
 231 | ```
 232 | 
 233 | ### 2.5 node_get_all_nodes
 234 | Get all nodes in the scene with their UUIDs
 235 | 
 236 | **Parameters**: None
 237 | 
 238 | **Example**:
 239 | ```json
 240 | {
 241 |   "tool": "node_get_all_nodes",
 242 |   "arguments": {}
 243 | }
 244 | ```
 245 | 
 246 | ### 2.6 node_set_node_property
 247 | Set node property value
 248 | 
 249 | **Parameters**:
 250 | - `uuid` (string, required): Node UUID
 251 | - `property` (string, required): Property name (e.g., position, rotation, scale, active)
 252 | - `value` (any, required): Property value
 253 | 
 254 | **Example**:
 255 | ```json
 256 | {
 257 |   "tool": "node_set_node_property",
 258 |   "arguments": {
 259 |     "uuid": "node-uuid-here",
 260 |     "property": "position",
 261 |     "value": {"x": 100, "y": 200, "z": 0}
 262 |   }
 263 | }
 264 | ```
 265 | 
 266 | ### 2.7 node_delete_node
 267 | Delete a node from the scene
 268 | 
 269 | **Parameters**:
 270 | - `uuid` (string, required): UUID of the node to delete
 271 | 
 272 | **Example**:
 273 | ```json
 274 | {
 275 |   "tool": "node_delete_node",
 276 |   "arguments": {
 277 |     "uuid": "node-uuid-here"
 278 |   }
 279 | }
 280 | ```
 281 | 
 282 | ### 2.8 node_move_node
 283 | Move a node to a new parent
 284 | 
 285 | **Parameters**:
 286 | - `nodeUuid` (string, required): UUID of the node to move
 287 | - `newParentUuid` (string, required): New parent node UUID
 288 | - `siblingIndex` (number, optional): Sibling index in the new parent, defaults to -1
 289 | 
 290 | **Example**:
 291 | ```json
 292 | {
 293 |   "tool": "node_move_node",
 294 |   "arguments": {
 295 |     "nodeUuid": "node-uuid-here",
 296 |     "newParentUuid": "parent-uuid-here",
 297 |     "siblingIndex": 0
 298 |   }
 299 | }
 300 | ```
 301 | 
 302 | ### 2.9 node_duplicate_node
 303 | Duplicate a node
 304 | 
 305 | **Parameters**:
 306 | - `uuid` (string, required): UUID of the node to duplicate
 307 | - `includeChildren` (boolean, optional): Whether to include child nodes, defaults to true
 308 | 
 309 | **Example**:
 310 | ```json
 311 | {
 312 |   "tool": "node_duplicate_node",
 313 |   "arguments": {
 314 |     "uuid": "node-uuid-here",
 315 |     "includeChildren": true
 316 |   }
 317 | }
 318 | ```
 319 | 
 320 | ---
 321 | 
 322 | ## 3. Component Management Tools
 323 | 
 324 | ### 3.1 component_add_component
 325 | Add a component to a specific node
 326 | 
 327 | **Parameters**:
 328 | - `nodeUuid` (string, **required**): Target node UUID. **Important**: 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.
 329 | - `componentType` (string, required): Component type (e.g., cc.Sprite, cc.Label, cc.Button)
 330 | 
 331 | **Important Note**: Before adding a component, ensure:
 332 | 1. First use `node_get_all_nodes` or `node_find_node_by_name` to find the target node's UUID
 333 | 2. Verify the node exists and the UUID is correct
 334 | 3. Choose the appropriate component type
 335 | 
 336 | **Example**:
 337 | ```json
 338 | {
 339 |   "tool": "component_add_component",
 340 |   "arguments": {
 341 |     "nodeUuid": "node-uuid-here",
 342 |     "componentType": "cc.Sprite"
 343 |   }
 344 | }
 345 | ```
 346 | 
 347 | ### 3.2 component_remove_component
 348 | Remove a component from a node
 349 | 
 350 | **Parameters**:
 351 | - `nodeUuid` (string, required): Node UUID
 352 | - `componentType` (string, required): Component type to remove
 353 | 
 354 | **Example**:
 355 | ```json
 356 | {
 357 |   "tool": "component_remove_component",
 358 |   "arguments": {
 359 |     "nodeUuid": "node-uuid-here",
 360 |     "componentType": "cc.Sprite"
 361 |   }
 362 | }
 363 | ```
 364 | 
 365 | ### 3.3 component_get_components
 366 | Get all components of a node
 367 | 
 368 | **Parameters**:
 369 | - `nodeUuid` (string, required): Node UUID
 370 | 
 371 | **Example**:
 372 | ```json
 373 | {
 374 |   "tool": "component_get_components",
 375 |   "arguments": {
 376 |     "nodeUuid": "node-uuid-here"
 377 |   }
 378 | }
 379 | ```
 380 | 
 381 | ### 3.4 component_get_component_info
 382 | Get specific component information
 383 | 
 384 | **Parameters**:
 385 | - `nodeUuid` (string, required): Node UUID
 386 | - `componentType` (string, required): Component type to get info for
 387 | 
 388 | **Example**:
 389 | ```json
 390 | {
 391 |   "tool": "component_get_component_info",
 392 |   "arguments": {
 393 |     "nodeUuid": "node-uuid-here",
 394 |     "componentType": "cc.Sprite"
 395 |   }
 396 | }
 397 | ```
 398 | 
 399 | ### 3.5 component_set_component_property
 400 | Set component property value
 401 | 
 402 | **Parameters**:
 403 | - `nodeUuid` (string, required): Node UUID
 404 | - `componentType` (string, required): Component type
 405 | - `property` (string, required): Property name
 406 | - `value` (any, required): Property value
 407 | 
 408 | **Example**:
 409 | ```json
 410 | {
 411 |   "tool": "component_set_component_property",
 412 |   "arguments": {
 413 |     "nodeUuid": "node-uuid-here",
 414 |     "componentType": "cc.Sprite",
 415 |     "property": "spriteFrame",
 416 |     "value": "sprite-frame-uuid"
 417 |   }
 418 | }
 419 | ```
 420 | 
 421 | ### 3.6 component_attach_script
 422 | Attach a script component to a node
 423 | 
 424 | **Parameters**:
 425 | - `nodeUuid` (string, required): Node UUID
 426 | - `scriptPath` (string, required): Script asset path
 427 | 
 428 | **Example**:
 429 | ```json
 430 | {
 431 |   "tool": "component_attach_script",
 432 |   "arguments": {
 433 |     "nodeUuid": "node-uuid-here",
 434 |     "scriptPath": "db://assets/scripts/PlayerController.ts"
 435 |   }
 436 | }
 437 | ```
 438 | 
 439 | ### 3.7 component_get_available_components
 440 | Get list of available component types
 441 | 
 442 | **Parameters**:
 443 | - `category` (string, optional): Component category filter, options: `all`, `renderer`, `ui`, `physics`, `animation`, `audio`, defaults to `all`
 444 | 
 445 | **Example**:
 446 | ```json
 447 | {
 448 |   "tool": "component_get_available_components",
 449 |   "arguments": {
 450 |     "category": "ui"
 451 |   }
 452 | }
 453 | ```
 454 | 
 455 | ---
 456 | 
 457 | ## 4. Prefab Tools
 458 | 
 459 | **⚠️ Known Issue**: When using standard Cocos Creator API for prefab instantiation, complex prefabs with child nodes may not be properly restored. While prefab creation functionality can correctly save all child node information, the instantiation process through `create-node` with `assetUuid` has limitations that may result in missing child nodes in the instantiated prefab.
 460 | 
 461 | ### 4.1 prefab_get_prefab_list
 462 | Get all prefabs in the project
 463 | 
 464 | **Parameters**:
 465 | - `folder` (string, optional): Search folder path, defaults to `db://assets`
 466 | 
 467 | **Example**:
 468 | ```json
 469 | {
 470 |   "tool": "prefab_get_prefab_list",
 471 |   "arguments": {
 472 |     "folder": "db://assets/prefabs"
 473 |   }
 474 | }
 475 | ```
 476 | 
 477 | ### 4.2 prefab_load_prefab
 478 | Load a prefab by path
 479 | 
 480 | **Parameters**:
 481 | - `prefabPath` (string, required): Prefab asset path
 482 | 
 483 | **Example**:
 484 | ```json
 485 | {
 486 |   "tool": "prefab_load_prefab",
 487 |   "arguments": {
 488 |     "prefabPath": "db://assets/prefabs/Enemy.prefab"
 489 |   }
 490 | }
 491 | ```
 492 | 
 493 | ### 4.3 prefab_instantiate_prefab
 494 | Instantiate a prefab in the scene
 495 | 
 496 | **Parameters**:
 497 | - `prefabPath` (string, required): Prefab asset path
 498 | - `parentUuid` (string, optional): Parent node UUID
 499 | - `position` (object, optional): Initial position with x, y, z properties
 500 | 
 501 | **Example**:
 502 | ```json
 503 | {
 504 |   "tool": "prefab_instantiate_prefab",
 505 |   "arguments": {
 506 |     "prefabPath": "db://assets/prefabs/Enemy.prefab",
 507 |     "parentUuid": "parent-uuid-here",
 508 |     "position": {"x": 100, "y": 200, "z": 0}
 509 |   }
 510 | }
 511 | ```
 512 | 
 513 | **⚠️ Functionality Limitation**: Complex prefabs with child nodes may not instantiate correctly. Due to Cocos Creator API limitations in the standard `create-node` method using `assetUuid`, only the root node may be created, and child nodes may be lost. This is a known issue with the current implementation.
 514 | 
 515 | ### 4.4 prefab_create_prefab
 516 | Create a prefab from a node
 517 | 
 518 | **Parameters**:
 519 | - `nodeUuid` (string, required): Source node UUID
 520 | - `savePath` (string, required): Path to save the prefab
 521 | - `prefabName` (string, required): Prefab name
 522 | 
 523 | **Example**:
 524 | ```json
 525 | {
 526 |   "tool": "prefab_create_prefab",
 527 |   "arguments": {
 528 |     "nodeUuid": "node-uuid-here",
 529 |     "savePath": "db://assets/prefabs/",
 530 |     "prefabName": "MyPrefab"
 531 |   }
 532 | }
 533 | ```
 534 | 
 535 | ### 4.5 prefab_create_prefab_from_node
 536 | Create a prefab from a node (alias for create_prefab)
 537 | 
 538 | **Parameters**:
 539 | - `nodeUuid` (string, required): Source node UUID
 540 | - `prefabPath` (string, required): Path to save the prefab
 541 | 
 542 | **Example**:
 543 | ```json
 544 | {
 545 |   "tool": "prefab_create_prefab_from_node",
 546 |   "arguments": {
 547 |     "nodeUuid": "node-uuid-here",
 548 |     "prefabPath": "db://assets/prefabs/MyPrefab.prefab"
 549 |   }
 550 | }
 551 | ```
 552 | 
 553 | ### 4.6 prefab_update_prefab
 554 | Update an existing prefab
 555 | 
 556 | **Parameters**:
 557 | - `prefabPath` (string, required): Prefab asset path
 558 | - `nodeUuid` (string, required): Node UUID containing changes
 559 | 
 560 | **Example**:
 561 | ```json
 562 | {
 563 |   "tool": "prefab_update_prefab",
 564 |   "arguments": {
 565 |     "prefabPath": "db://assets/prefabs/Enemy.prefab",
 566 |     "nodeUuid": "node-uuid-here"
 567 |   }
 568 | }
 569 | ```
 570 | 
 571 | ### 4.7 prefab_revert_prefab
 572 | Revert a prefab instance to its original state
 573 | 
 574 | **Parameters**:
 575 | - `nodeUuid` (string, required): Prefab instance node UUID
 576 | 
 577 | **Example**:
 578 | ```json
 579 | {
 580 |   "tool": "prefab_revert_prefab",
 581 |   "arguments": {
 582 |     "nodeUuid": "prefab-instance-uuid-here"
 583 |   }
 584 | }
 585 | ```
 586 | 
 587 | ### 4.8 prefab_get_prefab_info
 588 | Get detailed prefab information
 589 | 
 590 | **Parameters**:
 591 | - `prefabPath` (string, required): Prefab asset path
 592 | 
 593 | **Example**:
 594 | ```json
 595 | {
 596 |   "tool": "prefab_get_prefab_info",
 597 |   "arguments": {
 598 |     "prefabPath": "db://assets/prefabs/Enemy.prefab"
 599 |   }
 600 | }
 601 | ```
 602 | 
 603 | ---
 604 | 
 605 | ## 5. Project Control Tools
 606 | 
 607 | ### 5.1 project_run_project
 608 | Run the project in preview mode
 609 | 
 610 | **Parameters**:
 611 | - `platform` (string, optional): Target platform, options: `browser`, `simulator`, `preview`, defaults to `browser`
 612 | 
 613 | **Example**:
 614 | ```json
 615 | {
 616 |   "tool": "project_run_project",
 617 |   "arguments": {
 618 |     "platform": "browser"
 619 |   }
 620 | }
 621 | ```
 622 | 
 623 | ### 5.2 project_build_project
 624 | Build the project
 625 | 
 626 | **Parameters**:
 627 | - `platform` (string, required): Build platform, options: `web-mobile`, `web-desktop`, `ios`, `android`, `windows`, `mac`
 628 | - `debug` (boolean, optional): Whether to build in debug mode, defaults to true
 629 | 
 630 | **Example**:
 631 | ```json
 632 | {
 633 |   "tool": "project_build_project",
 634 |   "arguments": {
 635 |     "platform": "web-mobile",
 636 |     "debug": false
 637 |   }
 638 | }
 639 | ```
 640 | 
 641 | ### 5.3 project_get_project_info
 642 | Get project information
 643 | 
 644 | **Parameters**: None
 645 | 
 646 | **Example**:
 647 | ```json
 648 | {
 649 |   "tool": "project_get_project_info",
 650 |   "arguments": {}
 651 | }
 652 | ```
 653 | 
 654 | ### 5.4 project_get_project_settings
 655 | Get project settings
 656 | 
 657 | **Parameters**:
 658 | - `category` (string, optional): Settings category, options: `general`, `physics`, `render`, `assets`, defaults to `general`
 659 | 
 660 | **Example**:
 661 | ```json
 662 | {
 663 |   "tool": "project_get_project_settings",
 664 |   "arguments": {
 665 |     "category": "physics"
 666 |   }
 667 | }
 668 | ```
 669 | 
 670 | ### 5.5 project_refresh_assets
 671 | Refresh the asset database
 672 | 
 673 | **Parameters**:
 674 | - `folder` (string, optional): Specific folder to refresh
 675 | 
 676 | **Example**:
 677 | ```json
 678 | {
 679 |   "tool": "project_refresh_assets",
 680 |   "arguments": {
 681 |     "folder": "db://assets/textures"
 682 |   }
 683 | }
 684 | ```
 685 | 
 686 | ### 5.6 project_import_asset
 687 | Import an asset file
 688 | 
 689 | **Parameters**:
 690 | - `sourcePath` (string, required): Source file path
 691 | - `targetFolder` (string, required): Target folder in assets
 692 | 
 693 | **Example**:
 694 | ```json
 695 | {
 696 |   "tool": "project_import_asset",
 697 |   "arguments": {
 698 |     "sourcePath": "/path/to/image.png",
 699 |     "targetFolder": "db://assets/textures"
 700 |   }
 701 | }
 702 | ```
 703 | 
 704 | ### 5.7 project_get_asset_info
 705 | Get asset information
 706 | 
 707 | **Parameters**:
 708 | - `assetPath` (string, required): Asset path
 709 | 
 710 | **Example**:
 711 | ```json
 712 | {
 713 |   "tool": "project_get_asset_info",
 714 |   "arguments": {
 715 |     "assetPath": "db://assets/textures/player.png"
 716 |   }
 717 | }
 718 | ```
 719 | 
 720 | ### 5.8 project_get_assets
 721 | Get assets by type
 722 | 
 723 | **Parameters**:
 724 | - `type` (string, optional): Asset type filter, options: `all`, `scene`, `prefab`, `script`, `texture`, `material`, `mesh`, `audio`, `animation`, defaults to `all`
 725 | - `folder` (string, optional): Search folder, defaults to `db://assets`
 726 | 
 727 | **Example**:
 728 | ```json
 729 | {
 730 |   "tool": "project_get_assets",
 731 |   "arguments": {
 732 |     "type": "texture",
 733 |     "folder": "db://assets/textures"
 734 |   }
 735 | }
 736 | ```
 737 | 
 738 | ### 5.9 project_get_build_settings
 739 | Get build settings
 740 | 
 741 | **Parameters**: None
 742 | 
 743 | **Example**:
 744 | ```json
 745 | {
 746 |   "tool": "project_get_build_settings",
 747 |   "arguments": {}
 748 | }
 749 | ```
 750 | 
 751 | ### 5.10 project_open_build_panel
 752 | Open the build panel in the editor
 753 | 
 754 | **Parameters**: None
 755 | 
 756 | **Example**:
 757 | ```json
 758 | {
 759 |   "tool": "project_open_build_panel",
 760 |   "arguments": {}
 761 | }
 762 | ```
 763 | 
 764 | ### 5.11 project_check_builder_status
 765 | Check if the builder worker process is ready
 766 | 
 767 | **Parameters**: None
 768 | 
 769 | **Example**:
 770 | ```json
 771 | {
 772 |   "tool": "project_check_builder_status",
 773 |   "arguments": {}
 774 | }
 775 | ```
 776 | 
 777 | ### 5.12 project_start_preview_server
 778 | Start the preview server
 779 | 
 780 | **Parameters**:
 781 | - `port` (number, optional): Preview server port, defaults to 7456
 782 | 
 783 | **Example**:
 784 | ```json
 785 | {
 786 |   "tool": "project_start_preview_server",
 787 |   "arguments": {
 788 |     "port": 8080
 789 |   }
 790 | }
 791 | ```
 792 | 
 793 | ### 5.13 project_stop_preview_server
 794 | Stop the preview server
 795 | 
 796 | **Parameters**: None
 797 | 
 798 | **Example**:
 799 | ```json
 800 | {
 801 |   "tool": "project_stop_preview_server",
 802 |   "arguments": {}
 803 | }
 804 | ```
 805 | 
 806 | ### 5.14 project_create_asset
 807 | Create a new asset file or folder
 808 | 
 809 | **Parameters**:
 810 | - `url` (string, required): Asset URL
 811 | - `content` (string, optional): File content, null means create folder
 812 | - `overwrite` (boolean, optional): Whether to overwrite existing file, defaults to false
 813 | 
 814 | **Example**:
 815 | ```json
 816 | {
 817 |   "tool": "project_create_asset",
 818 |   "arguments": {
 819 |     "url": "db://assets/scripts/NewScript.ts",
 820 |     "content": "// New TypeScript script\n",
 821 |     "overwrite": false
 822 |   }
 823 | }
 824 | ```
 825 | 
 826 | ### 5.15 project_copy_asset
 827 | Copy an asset to another location
 828 | 
 829 | **Parameters**:
 830 | - `source` (string, required): Source asset URL
 831 | - `target` (string, required): Target location URL
 832 | - `overwrite` (boolean, optional): Whether to overwrite existing file, defaults to false
 833 | 
 834 | **Example**:
 835 | ```json
 836 | {
 837 |   "tool": "project_copy_asset",
 838 |   "arguments": {
 839 |     "source": "db://assets/textures/player.png",
 840 |     "target": "db://assets/textures/backup/player.png",
 841 |     "overwrite": false
 842 |   }
 843 | }
 844 | ```
 845 | 
 846 | ### 5.16 project_move_asset
 847 | Move an asset to another location
 848 | 
 849 | **Parameters**:
 850 | - `source` (string, required): Source asset URL
 851 | - `target` (string, required): Target location URL
 852 | - `overwrite` (boolean, optional): Whether to overwrite existing file, defaults to false
 853 | 
 854 | **Example**:
 855 | ```json
 856 | {
 857 |   "tool": "project_move_asset",
 858 |   "arguments": {
 859 |     "source": "db://assets/textures/old_player.png",
 860 |     "target": "db://assets/textures/player.png",
 861 |     "overwrite": true
 862 |   }
 863 | }
 864 | ```
 865 | 
 866 | ### 5.17 project_delete_asset
 867 | Delete an asset
 868 | 
 869 | **Parameters**:
 870 | - `url` (string, required): Asset URL to delete
 871 | 
 872 | **Example**:
 873 | ```json
 874 | {
 875 |   "tool": "project_delete_asset",
 876 |   "arguments": {
 877 |     "url": "db://assets/textures/unused.png"
 878 |   }
 879 | }
 880 | ```
 881 | 
 882 | ### 5.18 project_save_asset
 883 | Save asset content
 884 | 
 885 | **Parameters**:
 886 | - `url` (string, required): Asset URL
 887 | - `content` (string, required): Asset content
 888 | 
 889 | **Example**:
 890 | ```json
 891 | {
 892 |   "tool": "project_save_asset",
 893 |   "arguments": {
 894 |     "url": "db://assets/scripts/GameManager.ts",
 895 |     "content": "// Updated script content\n"
 896 |   }
 897 | }
 898 | ```
 899 | 
 900 | ### 5.19 project_reimport_asset
 901 | Reimport an asset
 902 | 
 903 | **Parameters**:
 904 | - `url` (string, required): Asset URL to reimport
 905 | 
 906 | **Example**:
 907 | ```json
 908 | {
 909 |   "tool": "project_reimport_asset",
 910 |   "arguments": {
 911 |     "url": "db://assets/textures/player.png"
 912 |   }
 913 | }
 914 | ```
 915 | 
 916 | ### 5.20 project_query_asset_path
 917 | Get asset disk path
 918 | 
 919 | **Parameters**:
 920 | - `url` (string, required): Asset URL
 921 | 
 922 | **Example**:
 923 | ```json
 924 | {
 925 |   "tool": "project_query_asset_path",
 926 |   "arguments": {
 927 |     "url": "db://assets/textures/player.png"
 928 |   }
 929 | }
 930 | ```
 931 | 
 932 | ### 5.21 project_query_asset_uuid
 933 | Get asset UUID from URL
 934 | 
 935 | **Parameters**:
 936 | - `url` (string, required): Asset URL
 937 | 
 938 | **Example**:
 939 | ```json
 940 | {
 941 |   "tool": "project_query_asset_uuid",
 942 |   "arguments": {
 943 |     "url": "db://assets/textures/player.png"
 944 |   }
 945 | }
 946 | ```
 947 | 
 948 | ### 5.22 project_query_asset_url
 949 | Get asset URL from UUID
 950 | 
 951 | **Parameters**:
 952 | - `uuid` (string, required): Asset UUID
 953 | 
 954 | **Example**:
 955 | ```json
 956 | {
 957 |   "tool": "project_query_asset_url",
 958 |   "arguments": {
 959 |     "uuid": "asset-uuid-here"
 960 |   }
 961 | }
 962 | ```
 963 | 
 964 | ---
 965 | 
 966 | ## 6. Debug Tools
 967 | 
 968 | ### 6.1 debug_get_console_logs
 969 | Get editor console logs
 970 | 
 971 | **Parameters**:
 972 | - `limit` (number, optional): Number of latest logs to retrieve, defaults to 100
 973 | - `filter` (string, optional): Filter logs by type, options: `all`, `log`, `warn`, `error`, `info`, defaults to `all`
 974 | 
 975 | **Example**:
 976 | ```json
 977 | {
 978 |   "tool": "debug_get_console_logs",
 979 |   "arguments": {
 980 |     "limit": 50,
 981 |     "filter": "error"
 982 |   }
 983 | }
 984 | ```
 985 | 
 986 | ### 6.2 debug_clear_console
 987 | Clear the editor console
 988 | 
 989 | **Parameters**: None
 990 | 
 991 | **Example**:
 992 | ```json
 993 | {
 994 |   "tool": "debug_clear_console",
 995 |   "arguments": {}
 996 | }
 997 | ```
 998 | 
 999 | ### 6.3 debug_execute_script
1000 | Execute JavaScript code in scene context
1001 | 
1002 | **Parameters**:
1003 | - `script` (string, required): JavaScript code to execute
1004 | 
1005 | **Example**:
1006 | ```json
1007 | {
1008 |   "tool": "debug_execute_script",
1009 |   "arguments": {
1010 |     "script": "console.log('Hello from MCP!');"
1011 |   }
1012 | }
1013 | ```
1014 | 
1015 | ### 6.4 debug_get_node_tree
1016 | Get detailed node tree for debugging
1017 | 
1018 | **Parameters**:
1019 | - `rootUuid` (string, optional): Root node UUID, if not provided uses scene root node
1020 | - `maxDepth` (number, optional): Maximum tree depth, defaults to 10
1021 | 
1022 | **Example**:
1023 | ```json
1024 | {
1025 |   "tool": "debug_get_node_tree",
1026 |   "arguments": {
1027 |     "rootUuid": "root-node-uuid",
1028 |     "maxDepth": 5
1029 |   }
1030 | }
1031 | ```
1032 | 
1033 | ### 6.5 debug_get_performance_stats
1034 | Get performance statistics
1035 | 
1036 | **Parameters**: None
1037 | 
1038 | **Example**:
1039 | ```json
1040 | {
1041 |   "tool": "debug_get_performance_stats",
1042 |   "arguments": {}
1043 | }
1044 | ```
1045 | 
1046 | ### 6.6 debug_validate_scene
1047 | Validate if the current scene has issues
1048 | 
1049 | **Parameters**:
1050 | - `checkMissingAssets` (boolean, optional): Check for missing asset references, defaults to true
1051 | - `checkPerformance` (boolean, optional): Check for performance issues, defaults to true
1052 | 
1053 | **Example**:
1054 | ```json
1055 | {
1056 |   "tool": "debug_validate_scene",
1057 |   "arguments": {
1058 |     "checkMissingAssets": true,
1059 |     "checkPerformance": true
1060 |   }
1061 | }
1062 | ```
1063 | 
1064 | ### 6.7 debug_get_editor_info
1065 | Get editor and environment information
1066 | 
1067 | **Parameters**: None
1068 | 
1069 | **Example**:
1070 | ```json
1071 | {
1072 |   "tool": "debug_get_editor_info",
1073 |   "arguments": {}
1074 | }
1075 | ```
1076 | 
1077 | ### 6.8 debug_get_project_logs
1078 | Get project logs from temp/logs/project.log file
1079 | 
1080 | **Parameters**:
1081 | - `lines` (number, optional): Number of lines to read from the end of the log file, default is 100, range: 1-10000
1082 | - `filterKeyword` (string, optional): Filter logs by specific keyword
1083 | - `logLevel` (string, optional): Filter by log level, options: `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`, `ALL`, defaults to `ALL`
1084 | 
1085 | **Example**:
1086 | ```json
1087 | {
1088 |   "tool": "debug_get_project_logs",
1089 |   "arguments": {
1090 |     "lines": 200,
1091 |     "filterKeyword": "prefab",
1092 |     "logLevel": "INFO"
1093 |   }
1094 | }
1095 | ```
1096 | 
1097 | ### 6.9 debug_get_log_file_info
1098 | Get project log file information
1099 | 
1100 | **Parameters**: None
1101 | 
1102 | **Returns**: File size, last modified time, line count, and file path information
1103 | 
1104 | **Example**:
1105 | ```json
1106 | {
1107 |   "tool": "debug_get_log_file_info",
1108 |   "arguments": {}
1109 | }
1110 | ```
1111 | 
1112 | ### 6.10 debug_search_project_logs
1113 | Search for specific patterns or errors in project logs
1114 | 
1115 | **Parameters**:
1116 | - `pattern` (string, required): Search pattern (supports regex)
1117 | - `maxResults` (number, optional): Maximum number of matching results, defaults to 20, range: 1-100
1118 | - `contextLines` (number, optional): Number of context lines to show around each match, defaults to 2, range: 0-10
1119 | 
1120 | **Example**:
1121 | ```json
1122 | {
1123 |   "tool": "debug_search_project_logs",
1124 |   "arguments": {
1125 |     "pattern": "error|failed|exception",
1126 |     "maxResults": 10,
1127 |     "contextLines": 3
1128 |   }
1129 | }
1130 | ```
1131 | 
1132 | ---
1133 | 
1134 | ## 7. Preferences Tools
1135 | 
1136 | ### 7.1 preferences_get_preferences
1137 | Get editor preferences
1138 | 
1139 | **Parameters**:
1140 | - `key` (string, optional): Specific preference key to get
1141 | 
1142 | **Example**:
1143 | ```json
1144 | {
1145 |   "tool": "preferences_get_preferences",
1146 |   "arguments": {
1147 |     "key": "editor.theme"
1148 |   }
1149 | }
1150 | ```
1151 | 
1152 | ### 7.2 preferences_set_preferences
1153 | Set editor preferences
1154 | 
1155 | **Parameters**:
1156 | - `key` (string, required): Preference key to set
1157 | - `value` (any, required): Preference value to set
1158 | 
1159 | **Example**:
1160 | ```json
1161 | {
1162 |   "tool": "preferences_set_preferences",
1163 |   "arguments": {
1164 |     "key": "editor.theme",
1165 |     "value": "dark"
1166 |   }
1167 | }
1168 | ```
1169 | 
1170 | ### 7.3 preferences_get_global_preferences
1171 | Get global editor preferences
1172 | 
1173 | **Parameters**:
1174 | - `key` (string, optional): Global preference key to get
1175 | 
1176 | **Example**:
1177 | ```json
1178 | {
1179 |   "tool": "preferences_get_global_preferences",
1180 |   "arguments": {
1181 |     "key": "global.autoSave"
1182 |   }
1183 | }
1184 | ```
1185 | 
1186 | ### 7.4 preferences_set_global_preferences
1187 | Set global editor preferences
1188 | 
1189 | **Parameters**:
1190 | - `key` (string, required): Global preference key to set
1191 | - `value` (any, required): Global preference value to set
1192 | 
1193 | **Example**:
1194 | ```json
1195 | {
1196 |   "tool": "preferences_set_global_preferences",
1197 |   "arguments": {
1198 |     "key": "global.autoSave",
1199 |     "value": true
1200 |   }
1201 | }
1202 | ```
1203 | 
1204 | ### 7.5 preferences_get_recent_projects
1205 | Get recently opened projects
1206 | 
1207 | **Parameters**: None
1208 | 
1209 | **Example**:
1210 | ```json
1211 | {
1212 |   "tool": "preferences_get_recent_projects",
1213 |   "arguments": {}
1214 | }
1215 | ```
1216 | 
1217 | ### 7.6 preferences_clear_recent_projects
1218 | Clear the list of recently opened projects
1219 | 
1220 | **Parameters**: None
1221 | 
1222 | **Example**:
1223 | ```json
1224 | {
1225 |   "tool": "preferences_clear_recent_projects",
1226 |   "arguments": {}
1227 | }
1228 | ```
1229 | 
1230 | ---
1231 | 
1232 | ## 8. Server Tools
1233 | 
1234 | ### 8.1 server_get_server_info
1235 | Get server information
1236 | 
1237 | **Parameters**: None
1238 | 
1239 | **Example**:
1240 | ```json
1241 | {
1242 |   "tool": "server_get_server_info",
1243 |   "arguments": {}
1244 | }
1245 | ```
1246 | 
1247 | ### 8.2 server_broadcast_custom_message
1248 | Broadcast a custom message
1249 | 
1250 | **Parameters**:
1251 | - `message` (string, required): Message name
1252 | - `data` (any, optional): Message data
1253 | 
1254 | **Example**:
1255 | ```json
1256 | {
1257 |   "tool": "server_broadcast_custom_message",
1258 |   "arguments": {
1259 |     "message": "custom_event",
1260 |     "data": {"type": "test", "value": 123}
1261 |   }
1262 | }
1263 | ```
1264 | 
1265 | ### 8.3 server_get_editor_version
1266 | Get editor version information
1267 | 
1268 | **Parameters**: None
1269 | 
1270 | **Example**:
1271 | ```json
1272 | {
1273 |   "tool": "server_get_editor_version",
1274 |   "arguments": {}
1275 | }
1276 | ```
1277 | 
1278 | ### 8.4 server_get_project_name
1279 | Get current project name
1280 | 
1281 | **Parameters**: None
1282 | 
1283 | **Example**:
1284 | ```json
1285 | {
1286 |   "tool": "server_get_project_name",
1287 |   "arguments": {}
1288 | }
1289 | ```
1290 | 
1291 | ### 8.5 server_get_project_path
1292 | Get current project path
1293 | 
1294 | **Parameters**: None
1295 | 
1296 | **Example**:
1297 | ```json
1298 | {
1299 |   "tool": "server_get_project_path",
1300 |   "arguments": {}
1301 | }
1302 | ```
1303 | 
1304 | ### 8.6 server_get_project_uuid
1305 | Get current project UUID
1306 | 
1307 | **Parameters**: None
1308 | 
1309 | **Example**:
1310 | ```json
1311 | {
1312 |   "tool": "server_get_project_uuid",
1313 |   "arguments": {}
1314 | }
1315 | ```
1316 | 
1317 | ### 8.7 server_restart_editor
1318 | Request to restart the editor
1319 | 
1320 | **Parameters**: None
1321 | 
1322 | **Example**:
1323 | ```json
1324 | {
1325 |   "tool": "server_restart_editor",
1326 |   "arguments": {}
1327 | }
1328 | ```
1329 | 
1330 | ### 8.8 server_quit_editor
1331 | Request to quit the editor
1332 | 
1333 | **Parameters**: None
1334 | 
1335 | **Example**:
1336 | ```json
1337 | {
1338 |   "tool": "server_quit_editor",
1339 |   "arguments": {}
1340 | }
1341 | ```
1342 | 
1343 | ---
1344 | 
1345 | ## 9. Broadcast Tools
1346 | 
1347 | ### 9.1 broadcast_get_broadcast_log
1348 | Get recent broadcast message log
1349 | 
1350 | **Parameters**:
1351 | - `limit` (number, optional): Number of latest messages to return, defaults to 50
1352 | - `messageType` (string, optional): Filter by message type
1353 | 
1354 | **Example**:
1355 | ```json
1356 | {
1357 |   "tool": "broadcast_get_broadcast_log",
1358 |   "arguments": {
1359 |     "limit": 100,
1360 |     "messageType": "scene_change"
1361 |   }
1362 | }
1363 | ```
1364 | 
1365 | ### 9.2 broadcast_listen_broadcast
1366 | Start listening for specific broadcast messages
1367 | 
1368 | **Parameters**:
1369 | - `messageType` (string, required): Message type to listen for
1370 | 
1371 | **Example**:
1372 | ```json
1373 | {
1374 |   "tool": "broadcast_listen_broadcast",
1375 |   "arguments": {
1376 |     "messageType": "node_created"
1377 |   }
1378 | }
1379 | ```
1380 | 
1381 | ### 9.3 broadcast_stop_listening
1382 | Stop listening for specific broadcast messages
1383 | 
1384 | **Parameters**:
1385 | - `messageType` (string, required): Message type to stop listening for
1386 | 
1387 | **Example**:
1388 | ```json
1389 | {
1390 |   "tool": "broadcast_stop_listening",
1391 |   "arguments": {
1392 |     "messageType": "node_created"
1393 |   }
1394 | }
1395 | ```
1396 | 
1397 | ### 9.4 broadcast_clear_broadcast_log
1398 | Clear broadcast message log
1399 | 
1400 | **Parameters**: None
1401 | 
1402 | **Example**:
1403 | ```json
1404 | {
1405 |   "tool": "broadcast_clear_broadcast_log",
1406 |   "arguments": {}
1407 | }
1408 | ```
1409 | 
1410 | ### 9.5 broadcast_get_active_listeners
1411 | Get list of active broadcast listeners
1412 | 
1413 | **Parameters**: None
1414 | 
1415 | **Example**:
1416 | ```json
1417 | {
1418 |   "tool": "broadcast_get_active_listeners",
1419 |   "arguments": {}
1420 | }
1421 | ```
1422 | 
1423 | ---
1424 | 
1425 | ## Usage Guidelines
1426 | 
1427 | ### 1. Tool Call Format
1428 | 
1429 | All tool calls use JSON-RPC 2.0 format:
1430 | 
1431 | ```json
1432 | {
1433 |   "jsonrpc": "2.0",
1434 |   "method": "tools/call",
1435 |   "params": {
1436 |     "name": "tool_name",
1437 |     "arguments": {
1438 |       // Tool parameters
1439 |     }
1440 |   },
1441 |   "id": 1
1442 | }
1443 | ```
1444 | 
1445 | ### 2. Common UUID Retrieval Methods
1446 | 
1447 | - Use `node_get_all_nodes` to get all node UUIDs
1448 | - Use `node_find_node_by_name` to find node UUIDs by name
1449 | - Use `scene_get_current_scene` to get scene UUID
1450 | - Use `prefab_get_prefab_list` to get prefab information
1451 | 
1452 | ### 3. Asset Path Format
1453 | 
1454 | Cocos Creator uses `db://` prefixed asset URL format:
1455 | - Scenes: `db://assets/scenes/GameScene.scene`
1456 | - Prefabs: `db://assets/prefabs/Player.prefab`
1457 | - Scripts: `db://assets/scripts/GameManager.ts`
1458 | - Textures: `db://assets/textures/player.png`
1459 | 
1460 | ### 4. Error Handling
1461 | 
1462 | If a tool call fails, an error message will be returned:
1463 | 
1464 | ```json
1465 | {
1466 |   "jsonrpc": "2.0",
1467 |   "id": 1,
1468 |   "error": {
1469 |     "code": -32000,
1470 |     "message": "Tool execution failed",
1471 |     "data": {
1472 |       "error": "Detailed error message"
1473 |     }
1474 |   }
1475 | }
1476 | ```
1477 | 
1478 | ### 5. Best Practices
1479 | 
1480 | 1. **Query First, Then Operate**: Before modifying nodes or components, first use query tools to get current state
1481 | 2. **Use UUIDs**: Prefer using UUIDs over names when referencing nodes and assets
1482 | 3. **Error Checking**: Always check the return value of tool calls to ensure operations succeed
1483 | 4. **Asset Management**: Before deleting or moving assets, ensure they are not referenced elsewhere
1484 | 5. **Performance Considerations**: Avoid frequent tool calls in loops, consider batch operations
1485 | 
1486 | ---
1487 | 
1488 | ## Technical Support
1489 | 
1490 | If you encounter issues during use, you can:
1491 | 
1492 | 1. Use `debug_get_console_logs` to view detailed error logs
1493 | 2. Use `debug_validate_scene` to check if the scene has issues
1494 | 3. Use `debug_get_editor_info` to get environment information
1495 | 4. Check the MCP server's running status and logs
1496 | 
1497 | ---
1498 | 
1499 | *This document is based on Cocos Creator MCP Server v1.3.0. Please refer to the latest version documentation for updates.*
```

--------------------------------------------------------------------------------
/source/tools/project-tools.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { ToolDefinition, ToolResponse, ToolExecutor, ProjectInfo, AssetInfo } from '../types';
   2 | import * as fs from 'fs';
   3 | import * as path from 'path';
   4 | 
   5 | export class ProjectTools implements ToolExecutor {
   6 |     getTools(): ToolDefinition[] {
   7 |         return [
   8 |             {
   9 |                 name: 'run_project',
  10 |                 description: 'Run the project in preview mode',
  11 |                 inputSchema: {
  12 |                     type: 'object',
  13 |                     properties: {
  14 |                         platform: {
  15 |                             type: 'string',
  16 |                             description: 'Target platform',
  17 |                             enum: ['browser', 'simulator', 'preview'],
  18 |                             default: 'browser'
  19 |                         }
  20 |                     }
  21 |                 }
  22 |             },
  23 |             {
  24 |                 name: 'build_project',
  25 |                 description: 'Build the project',
  26 |                 inputSchema: {
  27 |                     type: 'object',
  28 |                     properties: {
  29 |                         platform: {
  30 |                             type: 'string',
  31 |                             description: 'Build platform',
  32 |                             enum: ['web-mobile', 'web-desktop', 'ios', 'android', 'windows', 'mac']
  33 |                         },
  34 |                         debug: {
  35 |                             type: 'boolean',
  36 |                             description: 'Debug build',
  37 |                             default: true
  38 |                         }
  39 |                     },
  40 |                     required: ['platform']
  41 |                 }
  42 |             },
  43 |             {
  44 |                 name: 'get_project_info',
  45 |                 description: 'Get project information',
  46 |                 inputSchema: {
  47 |                     type: 'object',
  48 |                     properties: {}
  49 |                 }
  50 |             },
  51 |             {
  52 |                 name: 'get_project_settings',
  53 |                 description: 'Get project settings',
  54 |                 inputSchema: {
  55 |                     type: 'object',
  56 |                     properties: {
  57 |                         category: {
  58 |                             type: 'string',
  59 |                             description: 'Settings category',
  60 |                             enum: ['general', 'physics', 'render', 'assets'],
  61 |                             default: 'general'
  62 |                         }
  63 |                     }
  64 |                 }
  65 |             },
  66 |             {
  67 |                 name: 'refresh_assets',
  68 |                 description: 'Refresh asset database',
  69 |                 inputSchema: {
  70 |                     type: 'object',
  71 |                     properties: {
  72 |                         folder: {
  73 |                             type: 'string',
  74 |                             description: 'Specific folder to refresh (optional)'
  75 |                         }
  76 |                     }
  77 |                 }
  78 |             },
  79 |             {
  80 |                 name: 'import_asset',
  81 |                 description: 'Import an asset file',
  82 |                 inputSchema: {
  83 |                     type: 'object',
  84 |                     properties: {
  85 |                         sourcePath: {
  86 |                             type: 'string',
  87 |                             description: 'Source file path'
  88 |                         },
  89 |                         targetFolder: {
  90 |                             type: 'string',
  91 |                             description: 'Target folder in assets'
  92 |                         }
  93 |                     },
  94 |                     required: ['sourcePath', 'targetFolder']
  95 |                 }
  96 |             },
  97 |             {
  98 |                 name: 'get_asset_info',
  99 |                 description: 'Get asset information',
 100 |                 inputSchema: {
 101 |                     type: 'object',
 102 |                     properties: {
 103 |                         assetPath: {
 104 |                             type: 'string',
 105 |                             description: 'Asset path (db://assets/...)'
 106 |                         }
 107 |                     },
 108 |                     required: ['assetPath']
 109 |                 }
 110 |             },
 111 |             {
 112 |                 name: 'get_assets',
 113 |                 description: 'Get assets by type',
 114 |                 inputSchema: {
 115 |                     type: 'object',
 116 |                     properties: {
 117 |                         type: {
 118 |                             type: 'string',
 119 |                             description: 'Asset type filter',
 120 |                             enum: ['all', 'scene', 'prefab', 'script', 'texture', 'material', 'mesh', 'audio', 'animation'],
 121 |                             default: 'all'
 122 |                         },
 123 |                         folder: {
 124 |                             type: 'string',
 125 |                             description: 'Folder to search in',
 126 |                             default: 'db://assets'
 127 |                         }
 128 |                     }
 129 |                 }
 130 |             },
 131 |             {
 132 |                 name: 'get_build_settings',
 133 |                 description: 'Get build settings - shows current limitations',
 134 |                 inputSchema: {
 135 |                     type: 'object',
 136 |                     properties: {}
 137 |                 }
 138 |             },
 139 |             {
 140 |                 name: 'open_build_panel',
 141 |                 description: 'Open the build panel in the editor',
 142 |                 inputSchema: {
 143 |                     type: 'object',
 144 |                     properties: {}
 145 |                 }
 146 |             },
 147 |             {
 148 |                 name: 'check_builder_status',
 149 |                 description: 'Check if builder worker is ready',
 150 |                 inputSchema: {
 151 |                     type: 'object',
 152 |                     properties: {}
 153 |                 }
 154 |             },
 155 |             {
 156 |                 name: 'start_preview_server',
 157 |                 description: 'Start preview server',
 158 |                 inputSchema: {
 159 |                     type: 'object',
 160 |                     properties: {
 161 |                         port: {
 162 |                             type: 'number',
 163 |                             description: 'Preview server port',
 164 |                             default: 7456
 165 |                         }
 166 |                     }
 167 |                 }
 168 |             },
 169 |             {
 170 |                 name: 'stop_preview_server',
 171 |                 description: 'Stop preview server',
 172 |                 inputSchema: {
 173 |                     type: 'object',
 174 |                     properties: {}
 175 |                 }
 176 |             },
 177 |             {
 178 |                 name: 'create_asset',
 179 |                 description: 'Create a new asset file or folder',
 180 |                 inputSchema: {
 181 |                     type: 'object',
 182 |                     properties: {
 183 |                         url: {
 184 |                             type: 'string',
 185 |                             description: 'Asset URL (e.g., db://assets/newfile.json)'
 186 |                         },
 187 |                         content: {
 188 |                             type: 'string',
 189 |                             description: 'File content (null for folder)',
 190 |                             default: null
 191 |                         },
 192 |                         overwrite: {
 193 |                             type: 'boolean',
 194 |                             description: 'Overwrite existing file',
 195 |                             default: false
 196 |                         }
 197 |                     },
 198 |                     required: ['url']
 199 |                 }
 200 |             },
 201 |             {
 202 |                 name: 'copy_asset',
 203 |                 description: 'Copy an asset to another location',
 204 |                 inputSchema: {
 205 |                     type: 'object',
 206 |                     properties: {
 207 |                         source: {
 208 |                             type: 'string',
 209 |                             description: 'Source asset URL'
 210 |                         },
 211 |                         target: {
 212 |                             type: 'string',
 213 |                             description: 'Target location URL'
 214 |                         },
 215 |                         overwrite: {
 216 |                             type: 'boolean',
 217 |                             description: 'Overwrite existing file',
 218 |                             default: false
 219 |                         }
 220 |                     },
 221 |                     required: ['source', 'target']
 222 |                 }
 223 |             },
 224 |             {
 225 |                 name: 'move_asset',
 226 |                 description: 'Move an asset to another location',
 227 |                 inputSchema: {
 228 |                     type: 'object',
 229 |                     properties: {
 230 |                         source: {
 231 |                             type: 'string',
 232 |                             description: 'Source asset URL'
 233 |                         },
 234 |                         target: {
 235 |                             type: 'string',
 236 |                             description: 'Target location URL'
 237 |                         },
 238 |                         overwrite: {
 239 |                             type: 'boolean',
 240 |                             description: 'Overwrite existing file',
 241 |                             default: false
 242 |                         }
 243 |                     },
 244 |                     required: ['source', 'target']
 245 |                 }
 246 |             },
 247 |             {
 248 |                 name: 'delete_asset',
 249 |                 description: 'Delete an asset',
 250 |                 inputSchema: {
 251 |                     type: 'object',
 252 |                     properties: {
 253 |                         url: {
 254 |                             type: 'string',
 255 |                             description: 'Asset URL to delete'
 256 |                         }
 257 |                     },
 258 |                     required: ['url']
 259 |                 }
 260 |             },
 261 |             {
 262 |                 name: 'save_asset',
 263 |                 description: 'Save asset content',
 264 |                 inputSchema: {
 265 |                     type: 'object',
 266 |                     properties: {
 267 |                         url: {
 268 |                             type: 'string',
 269 |                             description: 'Asset URL'
 270 |                         },
 271 |                         content: {
 272 |                             type: 'string',
 273 |                             description: 'Asset content'
 274 |                         }
 275 |                     },
 276 |                     required: ['url', 'content']
 277 |                 }
 278 |             },
 279 |             {
 280 |                 name: 'reimport_asset',
 281 |                 description: 'Reimport an asset',
 282 |                 inputSchema: {
 283 |                     type: 'object',
 284 |                     properties: {
 285 |                         url: {
 286 |                             type: 'string',
 287 |                             description: 'Asset URL to reimport'
 288 |                         }
 289 |                     },
 290 |                     required: ['url']
 291 |                 }
 292 |             },
 293 |             {
 294 |                 name: 'query_asset_path',
 295 |                 description: 'Get asset disk path',
 296 |                 inputSchema: {
 297 |                     type: 'object',
 298 |                     properties: {
 299 |                         url: {
 300 |                             type: 'string',
 301 |                             description: 'Asset URL'
 302 |                         }
 303 |                     },
 304 |                     required: ['url']
 305 |                 }
 306 |             },
 307 |             {
 308 |                 name: 'query_asset_uuid',
 309 |                 description: 'Get asset UUID from URL',
 310 |                 inputSchema: {
 311 |                     type: 'object',
 312 |                     properties: {
 313 |                         url: {
 314 |                             type: 'string',
 315 |                             description: 'Asset URL'
 316 |                         }
 317 |                     },
 318 |                     required: ['url']
 319 |                 }
 320 |             },
 321 |             {
 322 |                 name: 'query_asset_url',
 323 |                 description: 'Get asset URL from UUID',
 324 |                 inputSchema: {
 325 |                     type: 'object',
 326 |                     properties: {
 327 |                         uuid: {
 328 |                             type: 'string',
 329 |                             description: 'Asset UUID'
 330 |                         }
 331 |                     },
 332 |                     required: ['uuid']
 333 |                 }
 334 |             },
 335 |             {
 336 |                 name: 'find_asset_by_name',
 337 |                 description: 'Find assets by name (supports partial matching and multiple results)',
 338 |                 inputSchema: {
 339 |                     type: 'object',
 340 |                     properties: {
 341 |                         name: {
 342 |                             type: 'string',
 343 |                             description: 'Asset name to search for (supports partial matching)'
 344 |                         },
 345 |                         exactMatch: {
 346 |                             type: 'boolean',
 347 |                             description: 'Whether to use exact name matching',
 348 |                             default: false
 349 |                         },
 350 |                         assetType: {
 351 |                             type: 'string',
 352 |                             description: 'Filter by asset type',
 353 |                             enum: ['all', 'scene', 'prefab', 'script', 'texture', 'material', 'mesh', 'audio', 'animation', 'spriteFrame'],
 354 |                             default: 'all'
 355 |                         },
 356 |                         folder: {
 357 |                             type: 'string',
 358 |                             description: 'Folder to search in',
 359 |                             default: 'db://assets'
 360 |                         },
 361 |                         maxResults: {
 362 |                             type: 'number',
 363 |                             description: 'Maximum number of results to return',
 364 |                             default: 20,
 365 |                             minimum: 1,
 366 |                             maximum: 100
 367 |                         }
 368 |                     },
 369 |                     required: ['name']
 370 |                 }
 371 |             },
 372 |             {
 373 |                 name: 'get_asset_details',
 374 |                 description: 'Get detailed asset information including spriteFrame sub-assets',
 375 |                 inputSchema: {
 376 |                     type: 'object',
 377 |                     properties: {
 378 |                         assetPath: {
 379 |                             type: 'string',
 380 |                             description: 'Asset path (db://assets/...)'
 381 |                         },
 382 |                         includeSubAssets: {
 383 |                             type: 'boolean',
 384 |                             description: 'Include sub-assets like spriteFrame, texture',
 385 |                             default: true
 386 |                         }
 387 |                     },
 388 |                     required: ['assetPath']
 389 |                 }
 390 |             }
 391 |         ];
 392 |     }
 393 | 
 394 |     async execute(toolName: string, args: any): Promise<ToolResponse> {
 395 |         switch (toolName) {
 396 |             case 'run_project':
 397 |                 return await this.runProject(args.platform);
 398 |             case 'build_project':
 399 |                 return await this.buildProject(args);
 400 |             case 'get_project_info':
 401 |                 return await this.getProjectInfo();
 402 |             case 'get_project_settings':
 403 |                 return await this.getProjectSettings(args.category);
 404 |             case 'refresh_assets':
 405 |                 return await this.refreshAssets(args.folder);
 406 |             case 'import_asset':
 407 |                 return await this.importAsset(args.sourcePath, args.targetFolder);
 408 |             case 'get_asset_info':
 409 |                 return await this.getAssetInfo(args.assetPath);
 410 |             case 'get_assets':
 411 |                 return await this.getAssets(args.type, args.folder);
 412 |             case 'get_build_settings':
 413 |                 return await this.getBuildSettings();
 414 |             case 'open_build_panel':
 415 |                 return await this.openBuildPanel();
 416 |             case 'check_builder_status':
 417 |                 return await this.checkBuilderStatus();
 418 |             case 'start_preview_server':
 419 |                 return await this.startPreviewServer(args.port);
 420 |             case 'stop_preview_server':
 421 |                 return await this.stopPreviewServer();
 422 |             case 'create_asset':
 423 |                 return await this.createAsset(args.url, args.content, args.overwrite);
 424 |             case 'copy_asset':
 425 |                 return await this.copyAsset(args.source, args.target, args.overwrite);
 426 |             case 'move_asset':
 427 |                 return await this.moveAsset(args.source, args.target, args.overwrite);
 428 |             case 'delete_asset':
 429 |                 return await this.deleteAsset(args.url);
 430 |             case 'save_asset':
 431 |                 return await this.saveAsset(args.url, args.content);
 432 |             case 'reimport_asset':
 433 |                 return await this.reimportAsset(args.url);
 434 |             case 'query_asset_path':
 435 |                 return await this.queryAssetPath(args.url);
 436 |             case 'query_asset_uuid':
 437 |                 return await this.queryAssetUuid(args.url);
 438 |             case 'query_asset_url':
 439 |                 return await this.queryAssetUrl(args.uuid);
 440 |             case 'find_asset_by_name':
 441 |                 return await this.findAssetByName(args);
 442 |             case 'get_asset_details':
 443 |                 return await this.getAssetDetails(args.assetPath, args.includeSubAssets);
 444 |             default:
 445 |                 throw new Error(`Unknown tool: ${toolName}`);
 446 |         }
 447 |     }
 448 | 
 449 |     private async runProject(platform: string = 'browser'): Promise<ToolResponse> {
 450 |         return new Promise((resolve) => {
 451 |             const previewConfig = {
 452 |                 platform: platform,
 453 |                 scenes: [] // Will use current scene
 454 |             };
 455 | 
 456 |             // Note: Preview module is not documented in official API
 457 |             // Using fallback approach - open build panel as alternative
 458 |             Editor.Message.request('builder', 'open').then(() => {
 459 |                 resolve({
 460 |                     success: true,
 461 |                     message: `Build panel opened. Preview functionality requires manual setup.`
 462 |                 });
 463 |             }).catch((err: Error) => {
 464 |                 resolve({ success: false, error: err.message });
 465 |             });
 466 |         });
 467 |     }
 468 | 
 469 |     private async buildProject(args: any): Promise<ToolResponse> {
 470 |         return new Promise((resolve) => {
 471 |             const buildOptions = {
 472 |                 platform: args.platform,
 473 |                 debug: args.debug !== false,
 474 |                 sourceMaps: args.debug !== false,
 475 |                 buildPath: `build/${args.platform}`
 476 |             };
 477 | 
 478 |             // Note: Builder module only supports 'open' and 'query-worker-ready'
 479 |             // Building requires manual interaction through the build panel
 480 |             Editor.Message.request('builder', 'open').then(() => {
 481 |                 resolve({
 482 |                     success: true,
 483 |                     message: `Build panel opened for ${args.platform}. Please configure and start build manually.`,
 484 |                     data: { 
 485 |                         platform: args.platform,
 486 |                         instruction: "Use the build panel to configure and start the build process"
 487 |                     }
 488 |                 });
 489 |             }).catch((err: Error) => {
 490 |                 resolve({ success: false, error: err.message });
 491 |             });
 492 |         });
 493 |     }
 494 | 
 495 |     private async getProjectInfo(): Promise<ToolResponse> {
 496 |         return new Promise((resolve) => {
 497 |             const info: ProjectInfo = {
 498 |                 name: Editor.Project.name,
 499 |                 path: Editor.Project.path,
 500 |                 uuid: Editor.Project.uuid,
 501 |                 version: (Editor.Project as any).version || '1.0.0',
 502 |                 cocosVersion: (Editor as any).versions?.cocos || 'Unknown'
 503 |             };
 504 | 
 505 |             // Note: 'query-info' API doesn't exist, using 'query-config' instead
 506 |             Editor.Message.request('project', 'query-config', 'project').then((additionalInfo: any) => {
 507 |                 if (additionalInfo) {
 508 |                     Object.assign(info, { config: additionalInfo });
 509 |                 }
 510 |                 resolve({ success: true, data: info });
 511 |             }).catch(() => {
 512 |                 // Return basic info even if detailed query fails
 513 |                 resolve({ success: true, data: info });
 514 |             });
 515 |         });
 516 |     }
 517 | 
 518 |     private async getProjectSettings(category: string = 'general'): Promise<ToolResponse> {
 519 |         return new Promise((resolve) => {
 520 |             // 使用正确的 project API 查询项目配置
 521 |             const configMap: Record<string, string> = {
 522 |                 general: 'project',
 523 |                 physics: 'physics',
 524 |                 render: 'render',
 525 |                 assets: 'asset-db'
 526 |             };
 527 | 
 528 |             const configName = configMap[category] || 'project';
 529 | 
 530 |             Editor.Message.request('project', 'query-config', configName).then((settings: any) => {
 531 |                 resolve({
 532 |                     success: true,
 533 |                     data: {
 534 |                         category: category,
 535 |                         config: settings,
 536 |                         message: `${category} settings retrieved successfully`
 537 |                     }
 538 |                 });
 539 |             }).catch((err: Error) => {
 540 |                 resolve({ success: false, error: err.message });
 541 |             });
 542 |         });
 543 |     }
 544 | 
 545 |     private async refreshAssets(folder?: string): Promise<ToolResponse> {
 546 |         return new Promise((resolve) => {
 547 |             // 使用正确的 asset-db API 刷新资源
 548 |             const targetPath = folder || 'db://assets';
 549 |             
 550 |             Editor.Message.request('asset-db', 'refresh-asset', targetPath).then(() => {
 551 |                 resolve({
 552 |                     success: true,
 553 |                     message: `Assets refreshed in: ${targetPath}`
 554 |                 });
 555 |             }).catch((err: Error) => {
 556 |                 resolve({ success: false, error: err.message });
 557 |             });
 558 |         });
 559 |     }
 560 | 
 561 |     private async importAsset(sourcePath: string, targetFolder: string): Promise<ToolResponse> {
 562 |         return new Promise((resolve) => {
 563 |             if (!fs.existsSync(sourcePath)) {
 564 |                 resolve({ success: false, error: 'Source file not found' });
 565 |                 return;
 566 |             }
 567 | 
 568 |             const fileName = path.basename(sourcePath);
 569 |             const targetPath = targetFolder.startsWith('db://') ?
 570 |                 targetFolder : `db://assets/${targetFolder}`;
 571 | 
 572 |             Editor.Message.request('asset-db', 'import-asset', sourcePath, `${targetPath}/${fileName}`).then((result: any) => {
 573 |                 resolve({
 574 |                     success: true,
 575 |                     data: {
 576 |                         uuid: result.uuid,
 577 |                         path: result.url,
 578 |                         message: `Asset imported: ${fileName}`
 579 |                     }
 580 |                 });
 581 |             }).catch((err: Error) => {
 582 |                 resolve({ success: false, error: err.message });
 583 |             });
 584 |         });
 585 |     }
 586 | 
 587 |     private async getAssetInfo(assetPath: string): Promise<ToolResponse> {
 588 |         return new Promise((resolve) => {
 589 |             Editor.Message.request('asset-db', 'query-asset-info', assetPath).then((assetInfo: any) => {
 590 |                 if (!assetInfo) {
 591 |                     throw new Error('Asset not found');
 592 |                 }
 593 | 
 594 |                 const info: AssetInfo = {
 595 |                     name: assetInfo.name,
 596 |                     uuid: assetInfo.uuid,
 597 |                     path: assetInfo.url,
 598 |                     type: assetInfo.type,
 599 |                     size: assetInfo.size,
 600 |                     isDirectory: assetInfo.isDirectory
 601 |                 };
 602 | 
 603 |                 if (assetInfo.meta) {
 604 |                     info.meta = {
 605 |                         ver: assetInfo.meta.ver,
 606 |                         importer: assetInfo.meta.importer
 607 |                     };
 608 |                 }
 609 | 
 610 |                 resolve({ success: true, data: info });
 611 |             }).catch((err: Error) => {
 612 |                 resolve({ success: false, error: err.message });
 613 |             });
 614 |         });
 615 |     }
 616 | 
 617 |     private async getAssets(type: string = 'all', folder: string = 'db://assets'): Promise<ToolResponse> {
 618 |         return new Promise((resolve) => {
 619 |             let pattern = `${folder}/**/*`;
 620 |             
 621 |             // 添加类型过滤
 622 |             if (type !== 'all') {
 623 |                 const typeExtensions: Record<string, string> = {
 624 |                     'scene': '.scene',
 625 |                     'prefab': '.prefab',
 626 |                     'script': '.{ts,js}',
 627 |                     'texture': '.{png,jpg,jpeg,gif,tga,bmp,psd}',
 628 |                     'material': '.mtl',
 629 |                     'mesh': '.{fbx,obj,dae}',
 630 |                     'audio': '.{mp3,ogg,wav,m4a}',
 631 |                     'animation': '.{anim,clip}'
 632 |                 };
 633 |                 
 634 |                 const extension = typeExtensions[type];
 635 |                 if (extension) {
 636 |                     pattern = `${folder}/**/*${extension}`;
 637 |                 }
 638 |             }
 639 | 
 640 |             // Note: query-assets API parameters corrected based on documentation
 641 |             Editor.Message.request('asset-db', 'query-assets', { pattern: pattern }).then((results: any[]) => {
 642 |                 const assets = results.map(asset => ({
 643 |                     name: asset.name,
 644 |                     uuid: asset.uuid,
 645 |                     path: asset.url,
 646 |                     type: asset.type,
 647 |                     size: asset.size || 0,
 648 |                     isDirectory: asset.isDirectory || false
 649 |                 }));
 650 |                 
 651 |                 resolve({ 
 652 |                     success: true, 
 653 |                     data: {
 654 |                         type: type,
 655 |                         folder: folder,
 656 |                         count: assets.length,
 657 |                         assets: assets
 658 |                     }
 659 |                 });
 660 |             }).catch((err: Error) => {
 661 |                 resolve({ success: false, error: err.message });
 662 |             });
 663 |         });
 664 |     }
 665 | 
 666 |     private async getBuildSettings(): Promise<ToolResponse> {
 667 |         return new Promise((resolve) => {
 668 |             // 检查构建器是否准备就绪
 669 |             Editor.Message.request('builder', 'query-worker-ready').then((ready: boolean) => {
 670 |                 resolve({
 671 |                     success: true,
 672 |                     data: {
 673 |                         builderReady: ready,
 674 |                         message: 'Build settings are limited in MCP plugin environment',
 675 |                         availableActions: [
 676 |                             'Open build panel with open_build_panel',
 677 |                             'Check builder status with check_builder_status',
 678 |                             'Start preview server with start_preview_server',
 679 |                             'Stop preview server with stop_preview_server'
 680 |                         ],
 681 |                         limitation: 'Full build configuration requires direct Editor UI access'
 682 |                     }
 683 |                 });
 684 |             }).catch((err: Error) => {
 685 |                 resolve({ success: false, error: err.message });
 686 |             });
 687 |         });
 688 |     }
 689 | 
 690 |     private async openBuildPanel(): Promise<ToolResponse> {
 691 |         return new Promise((resolve) => {
 692 |             Editor.Message.request('builder', 'open').then(() => {
 693 |                 resolve({
 694 |                     success: true,
 695 |                     message: 'Build panel opened successfully'
 696 |                 });
 697 |             }).catch((err: Error) => {
 698 |                 resolve({ success: false, error: err.message });
 699 |             });
 700 |         });
 701 |     }
 702 | 
 703 |     private async checkBuilderStatus(): Promise<ToolResponse> {
 704 |         return new Promise((resolve) => {
 705 |             Editor.Message.request('builder', 'query-worker-ready').then((ready: boolean) => {
 706 |                 resolve({
 707 |                     success: true,
 708 |                     data: {
 709 |                         ready: ready,
 710 |                         status: ready ? 'Builder worker is ready' : 'Builder worker is not ready',
 711 |                         message: 'Builder status checked successfully'
 712 |                     }
 713 |                 });
 714 |             }).catch((err: Error) => {
 715 |                 resolve({ success: false, error: err.message });
 716 |             });
 717 |         });
 718 |     }
 719 | 
 720 |     private async startPreviewServer(port: number = 7456): Promise<ToolResponse> {
 721 |         return new Promise((resolve) => {
 722 |             resolve({
 723 |                 success: false,
 724 |                 error: 'Preview server control is not supported through MCP API',
 725 |                 instruction: 'Please start the preview server manually using the editor menu: Project > Preview, or use the preview panel in the editor'
 726 |             });
 727 |         });
 728 |     }
 729 | 
 730 |     private async stopPreviewServer(): Promise<ToolResponse> {
 731 |         return new Promise((resolve) => {
 732 |             resolve({
 733 |                 success: false,
 734 |                 error: 'Preview server control is not supported through MCP API',
 735 |                 instruction: 'Please stop the preview server manually using the preview panel in the editor'
 736 |             });
 737 |         });
 738 |     }
 739 | 
 740 |     private async createAsset(url: string, content: string | null = null, overwrite: boolean = false): Promise<ToolResponse> {
 741 |         return new Promise((resolve) => {
 742 |             const options = {
 743 |                 overwrite: overwrite,
 744 |                 rename: !overwrite
 745 |             };
 746 | 
 747 |             Editor.Message.request('asset-db', 'create-asset', url, content, options).then((result: any) => {
 748 |                 if (result && result.uuid) {
 749 |                     resolve({
 750 |                         success: true,
 751 |                         data: {
 752 |                             uuid: result.uuid,
 753 |                             url: result.url,
 754 |                             message: content === null ? 'Folder created successfully' : 'File created successfully'
 755 |                         }
 756 |                     });
 757 |                 } else {
 758 |                     resolve({
 759 |                         success: true,
 760 |                         data: {
 761 |                             url: url,
 762 |                             message: content === null ? 'Folder created successfully' : 'File created successfully'
 763 |                         }
 764 |                     });
 765 |                 }
 766 |             }).catch((err: Error) => {
 767 |                 resolve({ success: false, error: err.message });
 768 |             });
 769 |         });
 770 |     }
 771 | 
 772 |     private async copyAsset(source: string, target: string, overwrite: boolean = false): Promise<ToolResponse> {
 773 |         return new Promise((resolve) => {
 774 |             const options = {
 775 |                 overwrite: overwrite,
 776 |                 rename: !overwrite
 777 |             };
 778 | 
 779 |             Editor.Message.request('asset-db', 'copy-asset', source, target, options).then((result: any) => {
 780 |                 if (result && result.uuid) {
 781 |                     resolve({
 782 |                         success: true,
 783 |                         data: {
 784 |                             uuid: result.uuid,
 785 |                             url: result.url,
 786 |                             message: 'Asset copied successfully'
 787 |                         }
 788 |                     });
 789 |                 } else {
 790 |                     resolve({
 791 |                         success: true,
 792 |                         data: {
 793 |                             source: source,
 794 |                             target: target,
 795 |                             message: 'Asset copied successfully'
 796 |                         }
 797 |                     });
 798 |                 }
 799 |             }).catch((err: Error) => {
 800 |                 resolve({ success: false, error: err.message });
 801 |             });
 802 |         });
 803 |     }
 804 | 
 805 |     private async moveAsset(source: string, target: string, overwrite: boolean = false): Promise<ToolResponse> {
 806 |         return new Promise((resolve) => {
 807 |             const options = {
 808 |                 overwrite: overwrite,
 809 |                 rename: !overwrite
 810 |             };
 811 | 
 812 |             Editor.Message.request('asset-db', 'move-asset', source, target, options).then((result: any) => {
 813 |                 if (result && result.uuid) {
 814 |                     resolve({
 815 |                         success: true,
 816 |                         data: {
 817 |                             uuid: result.uuid,
 818 |                             url: result.url,
 819 |                             message: 'Asset moved successfully'
 820 |                         }
 821 |                     });
 822 |                 } else {
 823 |                     resolve({
 824 |                         success: true,
 825 |                         data: {
 826 |                             source: source,
 827 |                             target: target,
 828 |                             message: 'Asset moved successfully'
 829 |                         }
 830 |                     });
 831 |                 }
 832 |             }).catch((err: Error) => {
 833 |                 resolve({ success: false, error: err.message });
 834 |             });
 835 |         });
 836 |     }
 837 | 
 838 |     private async deleteAsset(url: string): Promise<ToolResponse> {
 839 |         return new Promise((resolve) => {
 840 |             Editor.Message.request('asset-db', 'delete-asset', url).then((result: any) => {
 841 |                 resolve({
 842 |                     success: true,
 843 |                     data: {
 844 |                         url: url,
 845 |                         message: 'Asset deleted successfully'
 846 |                     }
 847 |                 });
 848 |             }).catch((err: Error) => {
 849 |                 resolve({ success: false, error: err.message });
 850 |             });
 851 |         });
 852 |     }
 853 | 
 854 |     private async saveAsset(url: string, content: string): Promise<ToolResponse> {
 855 |         return new Promise((resolve) => {
 856 |             Editor.Message.request('asset-db', 'save-asset', url, content).then((result: any) => {
 857 |                 if (result && result.uuid) {
 858 |                     resolve({
 859 |                         success: true,
 860 |                         data: {
 861 |                             uuid: result.uuid,
 862 |                             url: result.url,
 863 |                             message: 'Asset saved successfully'
 864 |                         }
 865 |                     });
 866 |                 } else {
 867 |                     resolve({
 868 |                         success: true,
 869 |                         data: {
 870 |                             url: url,
 871 |                             message: 'Asset saved successfully'
 872 |                         }
 873 |                     });
 874 |                 }
 875 |             }).catch((err: Error) => {
 876 |                 resolve({ success: false, error: err.message });
 877 |             });
 878 |         });
 879 |     }
 880 | 
 881 |     private async reimportAsset(url: string): Promise<ToolResponse> {
 882 |         return new Promise((resolve) => {
 883 |             Editor.Message.request('asset-db', 'reimport-asset', url).then(() => {
 884 |                 resolve({
 885 |                     success: true,
 886 |                     data: {
 887 |                         url: url,
 888 |                         message: 'Asset reimported successfully'
 889 |                     }
 890 |                 });
 891 |             }).catch((err: Error) => {
 892 |                 resolve({ success: false, error: err.message });
 893 |             });
 894 |         });
 895 |     }
 896 | 
 897 |     private async queryAssetPath(url: string): Promise<ToolResponse> {
 898 |         return new Promise((resolve) => {
 899 |             Editor.Message.request('asset-db', 'query-path', url).then((path: string | null) => {
 900 |                 if (path) {
 901 |                     resolve({
 902 |                         success: true,
 903 |                         data: {
 904 |                             url: url,
 905 |                             path: path,
 906 |                             message: 'Asset path retrieved successfully'
 907 |                         }
 908 |                     });
 909 |                 } else {
 910 |                     resolve({ success: false, error: 'Asset path not found' });
 911 |                 }
 912 |             }).catch((err: Error) => {
 913 |                 resolve({ success: false, error: err.message });
 914 |             });
 915 |         });
 916 |     }
 917 | 
 918 |     private async queryAssetUuid(url: string): Promise<ToolResponse> {
 919 |         return new Promise((resolve) => {
 920 |             Editor.Message.request('asset-db', 'query-uuid', url).then((uuid: string | null) => {
 921 |                 if (uuid) {
 922 |                     resolve({
 923 |                         success: true,
 924 |                         data: {
 925 |                             url: url,
 926 |                             uuid: uuid,
 927 |                             message: 'Asset UUID retrieved successfully'
 928 |                         }
 929 |                     });
 930 |                 } else {
 931 |                     resolve({ success: false, error: 'Asset UUID not found' });
 932 |                 }
 933 |             }).catch((err: Error) => {
 934 |                 resolve({ success: false, error: err.message });
 935 |             });
 936 |         });
 937 |     }
 938 | 
 939 |     private async queryAssetUrl(uuid: string): Promise<ToolResponse> {
 940 |         return new Promise((resolve) => {
 941 |             Editor.Message.request('asset-db', 'query-url', uuid).then((url: string | null) => {
 942 |                 if (url) {
 943 |                     resolve({
 944 |                         success: true,
 945 |                         data: {
 946 |                             uuid: uuid,
 947 |                             url: url,
 948 |                             message: 'Asset URL retrieved successfully'
 949 |                         }
 950 |                     });
 951 |                 } else {
 952 |                     resolve({ success: false, error: 'Asset URL not found' });
 953 |                 }
 954 |             }).catch((err: Error) => {
 955 |                 resolve({ success: false, error: err.message });
 956 |             });
 957 |         });
 958 |     }
 959 | 
 960 |     private async findAssetByName(args: any): Promise<ToolResponse> {
 961 |         const { name, exactMatch = false, assetType = 'all', folder = 'db://assets', maxResults = 20 } = args;
 962 |         
 963 |         return new Promise(async (resolve) => {
 964 |             try {
 965 |                 // Get all assets in the specified folder
 966 |                 const allAssetsResponse = await this.getAssets(assetType, folder);
 967 |                 if (!allAssetsResponse.success || !allAssetsResponse.data) {
 968 |                     resolve({
 969 |                         success: false,
 970 |                         error: `Failed to get assets: ${allAssetsResponse.error}`
 971 |                     });
 972 |                     return;
 973 |                 }
 974 |                 
 975 |                 const allAssets = allAssetsResponse.data.assets as any[];
 976 |                 let matchedAssets: any[] = [];
 977 |                 
 978 |                 // Search for matching assets
 979 |                 for (const asset of allAssets) {
 980 |                     const assetName = asset.name;
 981 |                     let matches = false;
 982 |                     
 983 |                     if (exactMatch) {
 984 |                         matches = assetName === name;
 985 |                     } else {
 986 |                         matches = assetName.toLowerCase().includes(name.toLowerCase());
 987 |                     }
 988 |                     
 989 |                     if (matches) {
 990 |                         // Get detailed asset info if needed
 991 |                         try {
 992 |                             const detailResponse = await this.getAssetInfo(asset.path);
 993 |                             if (detailResponse.success) {
 994 |                                 matchedAssets.push({
 995 |                                     ...asset,
 996 |                                     details: detailResponse.data
 997 |                                 });
 998 |                             } else {
 999 |                                 matchedAssets.push(asset);
1000 |                             }
1001 |                         } catch {
1002 |                             matchedAssets.push(asset);
1003 |                         }
1004 |                         
1005 |                         if (matchedAssets.length >= maxResults) {
1006 |                             break;
1007 |                         }
1008 |                     }
1009 |                 }
1010 |                 
1011 |                 resolve({
1012 |                     success: true,
1013 |                     data: {
1014 |                         searchTerm: name,
1015 |                         exactMatch,
1016 |                         assetType,
1017 |                         folder,
1018 |                         totalFound: matchedAssets.length,
1019 |                         maxResults,
1020 |                         assets: matchedAssets,
1021 |                         message: `Found ${matchedAssets.length} assets matching '${name}'`
1022 |                     }
1023 |                 });
1024 |                 
1025 |             } catch (error: any) {
1026 |                 resolve({
1027 |                     success: false,
1028 |                     error: `Asset search failed: ${error.message}`
1029 |                 });
1030 |             }
1031 |         });
1032 |     }
1033 |     
1034 |     private async getAssetDetails(assetPath: string, includeSubAssets: boolean = true): Promise<ToolResponse> {
1035 |         return new Promise(async (resolve) => {
1036 |             try {
1037 |                 // Get basic asset info
1038 |                 const assetInfoResponse = await this.getAssetInfo(assetPath);
1039 |                 if (!assetInfoResponse.success) {
1040 |                     resolve(assetInfoResponse);
1041 |                     return;
1042 |                 }
1043 |                 
1044 |                 const assetInfo = assetInfoResponse.data;
1045 |                 const detailedInfo: any = {
1046 |                     ...assetInfo,
1047 |                     subAssets: []
1048 |                 };
1049 |                 
1050 |                 if (includeSubAssets && assetInfo) {
1051 |                     // For image assets, try to get spriteFrame and texture sub-assets
1052 |                     if (assetInfo.type === 'cc.ImageAsset' || assetPath.match(/\.(png|jpg|jpeg|gif|tga|bmp|psd)$/i)) {
1053 |                         // Generate common sub-asset UUIDs
1054 |                         const baseUuid = assetInfo.uuid;
1055 |                         const possibleSubAssets = [
1056 |                             { type: 'spriteFrame', uuid: `${baseUuid}@f9941`, suffix: '@f9941' },
1057 |                             { type: 'texture', uuid: `${baseUuid}@6c48a`, suffix: '@6c48a' },
1058 |                             { type: 'texture2D', uuid: `${baseUuid}@6c48a`, suffix: '@6c48a' }
1059 |                         ];
1060 |                         
1061 |                         for (const subAsset of possibleSubAssets) {
1062 |                             try {
1063 |                                 // Try to get URL for the sub-asset to verify it exists
1064 |                                 const subAssetUrl = await Editor.Message.request('asset-db', 'query-url', subAsset.uuid);
1065 |                                 if (subAssetUrl) {
1066 |                                     detailedInfo.subAssets.push({
1067 |                                         type: subAsset.type,
1068 |                                         uuid: subAsset.uuid,
1069 |                                         url: subAssetUrl,
1070 |                                         suffix: subAsset.suffix
1071 |                                     });
1072 |                                 }
1073 |                             } catch {
1074 |                                 // Sub-asset doesn't exist, skip it
1075 |                             }
1076 |                         }
1077 |                     }
1078 |                 }
1079 |                 
1080 |                 resolve({
1081 |                     success: true,
1082 |                     data: {
1083 |                         assetPath,
1084 |                         includeSubAssets,
1085 |                         ...detailedInfo,
1086 |                         message: `Asset details retrieved. Found ${detailedInfo.subAssets.length} sub-assets.`
1087 |                     }
1088 |                 });
1089 |                 
1090 |             } catch (error: any) {
1091 |                 resolve({
1092 |                     success: false,
1093 |                     error: `Failed to get asset details: ${error.message}`
1094 |                 });
1095 |             }
1096 |         });
1097 |     }
1098 | }
```

--------------------------------------------------------------------------------
/source/tools/node-tools.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { ToolDefinition, ToolResponse, ToolExecutor, NodeInfo } from '../types';
   2 | import { ComponentTools } from './component-tools';
   3 | 
   4 | export class NodeTools implements ToolExecutor {
   5 |     private componentTools = new ComponentTools();
   6 |     getTools(): ToolDefinition[] {
   7 |         return [
   8 |             {
   9 |                 name: 'create_node',
  10 |                 description: 'Create a new node in the scene. Supports creating empty nodes, nodes with components, or instantiating from assets (prefabs, etc.). IMPORTANT: You should always provide parentUuid to specify where to create the node.',
  11 |                 inputSchema: {
  12 |                     type: 'object',
  13 |                     properties: {
  14 |                         name: {
  15 |                             type: 'string',
  16 |                             description: 'Node name'
  17 |                         },
  18 |                         parentUuid: {
  19 |                             type: 'string',
  20 |                             description: 'Parent node UUID. STRONGLY RECOMMENDED: Always provide this parameter. Use get_current_scene or get_all_nodes to find parent UUIDs. If not provided, node will be created at scene root.'
  21 |                         },
  22 |                         nodeType: {
  23 |                             type: 'string',
  24 |                             description: 'Node type: Node, 2DNode, 3DNode',
  25 |                             enum: ['Node', '2DNode', '3DNode'],
  26 |                             default: 'Node'
  27 |                         },
  28 |                         siblingIndex: {
  29 |                             type: 'number',
  30 |                             description: 'Sibling index for ordering (-1 means append at end)',
  31 |                             default: -1
  32 |                         },
  33 |                         assetUuid: {
  34 |                             type: 'string',
  35 |                             description: 'Asset UUID to instantiate from (e.g., prefab UUID). When provided, creates a node instance from the asset instead of an empty node.'
  36 |                         },
  37 |                         assetPath: {
  38 |                             type: 'string',
  39 |                             description: 'Asset path to instantiate from (e.g., "db://assets/prefabs/MyPrefab.prefab"). Alternative to assetUuid.'
  40 |                         },
  41 |                         components: {
  42 |                             type: 'array',
  43 |                             items: { type: 'string' },
  44 |                             description: 'Array of component type names to add to the new node (e.g., ["cc.Sprite", "cc.Button"])'
  45 |                         },
  46 |                         unlinkPrefab: {
  47 |                             type: 'boolean',
  48 |                             description: 'If true and creating from prefab, unlink from prefab to create a regular node',
  49 |                             default: false
  50 |                         },
  51 |                         keepWorldTransform: {
  52 |                             type: 'boolean',
  53 |                             description: 'Whether to keep world transform when creating the node',
  54 |                             default: false
  55 |                         },
  56 |                         initialTransform: {
  57 |                             type: 'object',
  58 |                             properties: {
  59 |                                 position: {
  60 |                                     type: 'object',
  61 |                                     properties: {
  62 |                                         x: { type: 'number' },
  63 |                                         y: { type: 'number' },
  64 |                                         z: { type: 'number' }
  65 |                                     }
  66 |                                 },
  67 |                                 rotation: {
  68 |                                     type: 'object',
  69 |                                     properties: {
  70 |                                         x: { type: 'number' },
  71 |                                         y: { type: 'number' },
  72 |                                         z: { type: 'number' }
  73 |                                     }
  74 |                                 },
  75 |                                 scale: {
  76 |                                     type: 'object',
  77 |                                     properties: {
  78 |                                         x: { type: 'number' },
  79 |                                         y: { type: 'number' },
  80 |                                         z: { type: 'number' }
  81 |                                     }
  82 |                                 }
  83 |                             },
  84 |                             description: 'Initial transform to apply to the created node'
  85 |                         }
  86 |                     },
  87 |                     required: ['name']
  88 |                 }
  89 |             },
  90 |             {
  91 |                 name: 'get_node_info',
  92 |                 description: 'Get node information by UUID',
  93 |                 inputSchema: {
  94 |                     type: 'object',
  95 |                     properties: {
  96 |                         uuid: {
  97 |                             type: 'string',
  98 |                             description: 'Node UUID'
  99 |                         }
 100 |                     },
 101 |                     required: ['uuid']
 102 |                 }
 103 |             },
 104 |             {
 105 |                 name: 'find_nodes',
 106 |                 description: 'Find nodes by name pattern',
 107 |                 inputSchema: {
 108 |                     type: 'object',
 109 |                     properties: {
 110 |                         pattern: {
 111 |                             type: 'string',
 112 |                             description: 'Name pattern to search'
 113 |                         },
 114 |                         exactMatch: {
 115 |                             type: 'boolean',
 116 |                             description: 'Exact match or partial match',
 117 |                             default: false
 118 |                         }
 119 |                     },
 120 |                     required: ['pattern']
 121 |                 }
 122 |             },
 123 |             {
 124 |                 name: 'find_node_by_name',
 125 |                 description: 'Find first node by exact name',
 126 |                 inputSchema: {
 127 |                     type: 'object',
 128 |                     properties: {
 129 |                         name: {
 130 |                             type: 'string',
 131 |                             description: 'Node name to find'
 132 |                         }
 133 |                     },
 134 |                     required: ['name']
 135 |                 }
 136 |             },
 137 |             {
 138 |                 name: 'get_all_nodes',
 139 |                 description: 'Get all nodes in the scene with their UUIDs',
 140 |                 inputSchema: {
 141 |                     type: 'object',
 142 |                     properties: {}
 143 |                 }
 144 |             },
 145 |             {
 146 |                 name: 'set_node_property',
 147 |                 description: 'Set node property value (prefer using set_node_transform for active/layer/mobility/position/rotation/scale)',
 148 |                 inputSchema: {
 149 |                     type: 'object',
 150 |                     properties: {
 151 |                         uuid: {
 152 |                             type: 'string',
 153 |                             description: 'Node UUID'
 154 |                         },
 155 |                         property: {
 156 |                             type: 'string',
 157 |                             description: 'Property name (e.g., active, name, layer)'
 158 |                         },
 159 |                         value: {
 160 |                             description: 'Property value'
 161 |                         }
 162 |                     },
 163 |                     required: ['uuid', 'property', 'value']
 164 |                 }
 165 |             },
 166 |             {
 167 |                 name: 'set_node_transform',
 168 |                 description: 'Set node transform properties (position, rotation, scale) with unified interface. Automatically handles 2D/3D node differences.',
 169 |                 inputSchema: {
 170 |                     type: 'object',
 171 |                     properties: {
 172 |                         uuid: {
 173 |                             type: 'string',
 174 |                             description: 'Node UUID'
 175 |                         },
 176 |                         position: {
 177 |                             type: 'object',
 178 |                             properties: {
 179 |                                 x: { type: 'number' },
 180 |                                 y: { type: 'number' },
 181 |                                 z: { type: 'number', description: 'Z coordinate (ignored for 2D nodes)' }
 182 |                             },
 183 |                             description: 'Node position. For 2D nodes, only x,y are used; z is ignored. For 3D nodes, all coordinates are used.'
 184 |                         },
 185 |                         rotation: {
 186 |                             type: 'object',
 187 |                             properties: {
 188 |                                 x: { type: 'number', description: 'X rotation (ignored for 2D nodes)' },
 189 |                                 y: { type: 'number', description: 'Y rotation (ignored for 2D nodes)' },
 190 |                                 z: { type: 'number', description: 'Z rotation (main rotation axis for 2D nodes)' }
 191 |                             },
 192 |                             description: 'Node rotation in euler angles. For 2D nodes, only z rotation is used. For 3D nodes, all axes are used.'
 193 |                         },
 194 |                         scale: {
 195 |                             type: 'object',
 196 |                             properties: {
 197 |                                 x: { type: 'number' },
 198 |                                 y: { type: 'number' },
 199 |                                 z: { type: 'number', description: 'Z scale (usually 1 for 2D nodes)' }
 200 |                             },
 201 |                             description: 'Node scale. For 2D nodes, z is typically 1. For 3D nodes, all axes are used.'
 202 |                         }
 203 |                     },
 204 |                     required: ['uuid']
 205 |                 }
 206 |             },
 207 |             {
 208 |                 name: 'delete_node',
 209 |                 description: 'Delete a node from scene',
 210 |                 inputSchema: {
 211 |                     type: 'object',
 212 |                     properties: {
 213 |                         uuid: {
 214 |                             type: 'string',
 215 |                             description: 'Node UUID to delete'
 216 |                         }
 217 |                     },
 218 |                     required: ['uuid']
 219 |                 }
 220 |             },
 221 |             {
 222 |                 name: 'move_node',
 223 |                 description: 'Move node to new parent',
 224 |                 inputSchema: {
 225 |                     type: 'object',
 226 |                     properties: {
 227 |                         nodeUuid: {
 228 |                             type: 'string',
 229 |                             description: 'Node UUID to move'
 230 |                         },
 231 |                         newParentUuid: {
 232 |                             type: 'string',
 233 |                             description: 'New parent node UUID'
 234 |                         },
 235 |                         siblingIndex: {
 236 |                             type: 'number',
 237 |                             description: 'Sibling index in new parent',
 238 |                             default: -1
 239 |                         }
 240 |                     },
 241 |                     required: ['nodeUuid', 'newParentUuid']
 242 |                 }
 243 |             },
 244 |             {
 245 |                 name: 'duplicate_node',
 246 |                 description: 'Duplicate a node',
 247 |                 inputSchema: {
 248 |                     type: 'object',
 249 |                     properties: {
 250 |                         uuid: {
 251 |                             type: 'string',
 252 |                             description: 'Node UUID to duplicate'
 253 |                         },
 254 |                         includeChildren: {
 255 |                             type: 'boolean',
 256 |                             description: 'Include children nodes',
 257 |                             default: true
 258 |                         }
 259 |                     },
 260 |                     required: ['uuid']
 261 |                 }
 262 |             },
 263 |             {
 264 |                 name: 'detect_node_type',
 265 |                 description: 'Detect if a node is 2D or 3D based on its components and properties',
 266 |                 inputSchema: {
 267 |                     type: 'object',
 268 |                     properties: {
 269 |                         uuid: {
 270 |                             type: 'string',
 271 |                             description: 'Node UUID to analyze'
 272 |                         }
 273 |                     },
 274 |                     required: ['uuid']
 275 |                 }
 276 |             }
 277 |         ];
 278 |     }
 279 | 
 280 |     async execute(toolName: string, args: any): Promise<ToolResponse> {
 281 |         switch (toolName) {
 282 |             case 'create_node':
 283 |                 return await this.createNode(args);
 284 |             case 'get_node_info':
 285 |                 return await this.getNodeInfo(args.uuid);
 286 |             case 'find_nodes':
 287 |                 return await this.findNodes(args.pattern, args.exactMatch);
 288 |             case 'find_node_by_name':
 289 |                 return await this.findNodeByName(args.name);
 290 |             case 'get_all_nodes':
 291 |                 return await this.getAllNodes();
 292 |             case 'set_node_property':
 293 |                 return await this.setNodeProperty(args.uuid, args.property, args.value);
 294 |             case 'set_node_transform':
 295 |                 return await this.setNodeTransform(args);
 296 |             case 'delete_node':
 297 |                 return await this.deleteNode(args.uuid);
 298 |             case 'move_node':
 299 |                 return await this.moveNode(args.nodeUuid, args.newParentUuid, args.siblingIndex);
 300 |             case 'duplicate_node':
 301 |                 return await this.duplicateNode(args.uuid, args.includeChildren);
 302 |             case 'detect_node_type':
 303 |                 return await this.detectNodeType(args.uuid);
 304 |             default:
 305 |                 throw new Error(`Unknown tool: ${toolName}`);
 306 |         }
 307 |     }
 308 | 
 309 |     private async createNode(args: any): Promise<ToolResponse> {
 310 |         return new Promise(async (resolve) => {
 311 |             try {
 312 |                 let targetParentUuid = args.parentUuid;
 313 |                 
 314 |                 // 如果没有提供父节点UUID,获取场景根节点
 315 |                 if (!targetParentUuid) {
 316 |                     try {
 317 |                         const sceneInfo = await Editor.Message.request('scene', 'query-node-tree');
 318 |                         if (sceneInfo && typeof sceneInfo === 'object' && !Array.isArray(sceneInfo) && Object.prototype.hasOwnProperty.call(sceneInfo, 'uuid')) {
 319 |                             targetParentUuid = (sceneInfo as any).uuid;
 320 |                             console.log(`No parent specified, using scene root: ${targetParentUuid}`);
 321 |                         } else if (Array.isArray(sceneInfo) && sceneInfo.length > 0 && sceneInfo[0].uuid) {
 322 |                             targetParentUuid = sceneInfo[0].uuid;
 323 |                             console.log(`No parent specified, using scene root: ${targetParentUuid}`);
 324 |                         } else {
 325 |                             const currentScene = await Editor.Message.request('scene', 'query-current-scene');
 326 |                             if (currentScene && currentScene.uuid) {
 327 |                                 targetParentUuid = currentScene.uuid;
 328 |                             }
 329 |                         }
 330 |                     } catch (err) {
 331 |                         console.warn('Failed to get scene root, will use default behavior');
 332 |                     }
 333 |                 }
 334 | 
 335 |                 // 如果提供了assetPath,先解析为assetUuid
 336 |                 let finalAssetUuid = args.assetUuid;
 337 |                 if (args.assetPath && !finalAssetUuid) {
 338 |                     try {
 339 |                         const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', args.assetPath);
 340 |                         if (assetInfo && assetInfo.uuid) {
 341 |                             finalAssetUuid = assetInfo.uuid;
 342 |                             console.log(`Asset path '${args.assetPath}' resolved to UUID: ${finalAssetUuid}`);
 343 |                         } else {
 344 |                             resolve({
 345 |                                 success: false,
 346 |                                 error: `Asset not found at path: ${args.assetPath}`
 347 |                             });
 348 |                             return;
 349 |                         }
 350 |                     } catch (err) {
 351 |                         resolve({
 352 |                             success: false,
 353 |                             error: `Failed to resolve asset path '${args.assetPath}': ${err}`
 354 |                         });
 355 |                         return;
 356 |                     }
 357 |                 }
 358 | 
 359 |                 // 构建create-node选项
 360 |                 const createNodeOptions: any = {
 361 |                     name: args.name
 362 |                 };
 363 | 
 364 |                 // 设置父节点
 365 |                 if (targetParentUuid) {
 366 |                     createNodeOptions.parent = targetParentUuid;
 367 |                 }
 368 | 
 369 |                 // 从资源实例化
 370 |                 if (finalAssetUuid) {
 371 |                     createNodeOptions.assetUuid = finalAssetUuid;
 372 |                     if (args.unlinkPrefab) {
 373 |                         createNodeOptions.unlinkPrefab = true;
 374 |                     }
 375 |                 }
 376 | 
 377 |                 // 添加组件
 378 |                 if (args.components && args.components.length > 0) {
 379 |                     createNodeOptions.components = args.components;
 380 |                 } else if (args.nodeType && args.nodeType !== 'Node' && !finalAssetUuid) {
 381 |                     // 只有在不从资源实例化时才添加nodeType组件
 382 |                     createNodeOptions.components = [args.nodeType];
 383 |                 }
 384 | 
 385 |                 // 保持世界变换
 386 |                 if (args.keepWorldTransform) {
 387 |                     createNodeOptions.keepWorldTransform = true;
 388 |                 }
 389 | 
 390 |                 // 不使用dump参数处理初始变换,创建后使用set_node_transform设置
 391 | 
 392 |                 console.log('Creating node with options:', createNodeOptions);
 393 | 
 394 |                 // 创建节点
 395 |                 const nodeUuid = await Editor.Message.request('scene', 'create-node', createNodeOptions);
 396 |                 const uuid = Array.isArray(nodeUuid) ? nodeUuid[0] : nodeUuid;
 397 | 
 398 |                 // 处理兄弟索引
 399 |                 if (args.siblingIndex !== undefined && args.siblingIndex >= 0 && uuid && targetParentUuid) {
 400 |                     try {
 401 |                         await new Promise(resolve => setTimeout(resolve, 100)); // 等待内部状态更新
 402 |                         await Editor.Message.request('scene', 'set-parent', {
 403 |                             parent: targetParentUuid,
 404 |                             uuids: [uuid],
 405 |                             keepWorldTransform: args.keepWorldTransform || false
 406 |                         });
 407 |                     } catch (err) {
 408 |                         console.warn('Failed to set sibling index:', err);
 409 |                     }
 410 |                 }
 411 | 
 412 |                 // 添加组件(如果提供的话)
 413 |                 if (args.components && args.components.length > 0 && uuid) {
 414 |                     try {
 415 |                         await new Promise(resolve => setTimeout(resolve, 100)); // 等待节点创建完成
 416 |                         for (const componentType of args.components) {
 417 |                             try {
 418 |                                 const result = await this.componentTools.execute('add_component', {
 419 |                                     nodeUuid: uuid,
 420 |                                     componentType: componentType
 421 |                                 });
 422 |                                 if (result.success) {
 423 |                                     console.log(`Component ${componentType} added successfully`);
 424 |                                 } else {
 425 |                                     console.warn(`Failed to add component ${componentType}:`, result.error);
 426 |                                 }
 427 |                             } catch (err) {
 428 |                                 console.warn(`Failed to add component ${componentType}:`, err);
 429 |                             }
 430 |                         }
 431 |                     } catch (err) {
 432 |                         console.warn('Failed to add components:', err);
 433 |                     }
 434 |                 }
 435 | 
 436 |                 // 设置初始变换(如果提供的话)
 437 |                 if (args.initialTransform && uuid) {
 438 |                     try {
 439 |                         await new Promise(resolve => setTimeout(resolve, 150)); // 等待节点和组件创建完成
 440 |                         await this.setNodeTransform({
 441 |                             uuid: uuid,
 442 |                             position: args.initialTransform.position,
 443 |                             rotation: args.initialTransform.rotation,
 444 |                             scale: args.initialTransform.scale
 445 |                         });
 446 |                         console.log('Initial transform applied successfully');
 447 |                     } catch (err) {
 448 |                         console.warn('Failed to set initial transform:', err);
 449 |                     }
 450 |                 }
 451 | 
 452 |                 // 获取创建后的节点信息进行验证
 453 |                 let verificationData: any = null;
 454 |                 try {
 455 |                     const nodeInfo = await this.getNodeInfo(uuid);
 456 |                     if (nodeInfo.success) {
 457 |                         verificationData = {
 458 |                             nodeInfo: nodeInfo.data,
 459 |                             creationDetails: {
 460 |                                 parentUuid: targetParentUuid,
 461 |                                 nodeType: args.nodeType || 'Node',
 462 |                                 fromAsset: !!finalAssetUuid,
 463 |                                 assetUuid: finalAssetUuid,
 464 |                                 assetPath: args.assetPath,
 465 |                                 timestamp: new Date().toISOString()
 466 |                             }
 467 |                         };
 468 |                     }
 469 |                 } catch (err) {
 470 |                     console.warn('Failed to get verification data:', err);
 471 |                 }
 472 | 
 473 |                 const successMessage = finalAssetUuid 
 474 |                     ? `Node '${args.name}' instantiated from asset successfully`
 475 |                     : `Node '${args.name}' created successfully`;
 476 | 
 477 |                 resolve({
 478 |                     success: true,
 479 |                     data: {
 480 |                         uuid: uuid,
 481 |                         name: args.name,
 482 |                         parentUuid: targetParentUuid,
 483 |                         nodeType: args.nodeType || 'Node',
 484 |                         fromAsset: !!finalAssetUuid,
 485 |                         assetUuid: finalAssetUuid,
 486 |                         message: successMessage
 487 |                     },
 488 |                     verificationData: verificationData
 489 |                 });
 490 | 
 491 |             } catch (err: any) {
 492 |                 resolve({ 
 493 |                     success: false, 
 494 |                     error: `Failed to create node: ${err.message}. Args: ${JSON.stringify(args)}`
 495 |                 });
 496 |             }
 497 |         });
 498 |     }
 499 | 
 500 |     private async getNodeInfo(uuid: string): Promise<ToolResponse> {
 501 |         return new Promise((resolve) => {
 502 |             Editor.Message.request('scene', 'query-node', uuid).then((nodeData: any) => {
 503 |                 if (!nodeData) {
 504 |                     resolve({
 505 |                         success: false,
 506 |                         error: 'Node not found or invalid response'
 507 |                     });
 508 |                     return;
 509 |                 }
 510 |                 
 511 |                 // 根据实际返回的数据结构解析节点信息
 512 |                 const info: NodeInfo = {
 513 |                     uuid: nodeData.uuid?.value || uuid,
 514 |                     name: nodeData.name?.value || 'Unknown',
 515 |                     active: nodeData.active?.value !== undefined ? nodeData.active.value : true,
 516 |                     position: nodeData.position?.value || { x: 0, y: 0, z: 0 },
 517 |                     rotation: nodeData.rotation?.value || { x: 0, y: 0, z: 0 },
 518 |                     scale: nodeData.scale?.value || { x: 1, y: 1, z: 1 },
 519 |                     parent: nodeData.parent?.value?.uuid || null,
 520 |                     children: nodeData.children || [],
 521 |                     components: (nodeData.__comps__ || []).map((comp: any) => ({
 522 |                         type: comp.__type__ || 'Unknown',
 523 |                         enabled: comp.enabled !== undefined ? comp.enabled : true
 524 |                     })),
 525 |                     layer: nodeData.layer?.value || 1073741824,
 526 |                     mobility: nodeData.mobility?.value || 0
 527 |                 };
 528 |                 resolve({ success: true, data: info });
 529 |             }).catch((err: Error) => {
 530 |                 resolve({ success: false, error: err.message });
 531 |             });
 532 |         });
 533 |     }
 534 | 
 535 |     private async findNodes(pattern: string, exactMatch: boolean = false): Promise<ToolResponse> {
 536 |         return new Promise((resolve) => {
 537 |             // Note: 'query-nodes-by-name' API doesn't exist in official documentation
 538 |             // Using tree traversal as primary approach
 539 |             Editor.Message.request('scene', 'query-node-tree').then((tree: any) => {
 540 |                 const nodes: any[] = [];
 541 |                 
 542 |                 const searchTree = (node: any, currentPath: string = '') => {
 543 |                     const nodePath = currentPath ? `${currentPath}/${node.name}` : node.name;
 544 |                     
 545 |                     const matches = exactMatch ? 
 546 |                         node.name === pattern : 
 547 |                         node.name.toLowerCase().includes(pattern.toLowerCase());
 548 |                     
 549 |                     if (matches) {
 550 |                         nodes.push({
 551 |                             uuid: node.uuid,
 552 |                             name: node.name,
 553 |                             path: nodePath
 554 |                         });
 555 |                     }
 556 |                     
 557 |                     if (node.children) {
 558 |                         for (const child of node.children) {
 559 |                             searchTree(child, nodePath);
 560 |                         }
 561 |                     }
 562 |                 };
 563 |                 
 564 |                 if (tree) {
 565 |                     searchTree(tree);
 566 |                 }
 567 |                 
 568 |                 resolve({ success: true, data: nodes });
 569 |             }).catch((err: Error) => {
 570 |                 // 备用方案:使用场景脚本
 571 |                 const options = {
 572 |                     name: 'cocos-mcp-server',
 573 |                     method: 'findNodes',
 574 |                     args: [pattern, exactMatch]
 575 |                 };
 576 |                 
 577 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
 578 |                     resolve(result);
 579 |                 }).catch((err2: Error) => {
 580 |                     resolve({ success: false, error: `Tree search failed: ${err.message}, Scene script failed: ${err2.message}` });
 581 |                 });
 582 |             });
 583 |         });
 584 |     }
 585 | 
 586 |     private async findNodeByName(name: string): Promise<ToolResponse> {
 587 |         return new Promise((resolve) => {
 588 |             // 优先尝试使用 Editor API 查询节点树并搜索
 589 |             Editor.Message.request('scene', 'query-node-tree').then((tree: any) => {
 590 |                 const foundNode = this.searchNodeInTree(tree, name);
 591 |                 if (foundNode) {
 592 |                     resolve({
 593 |                         success: true,
 594 |                         data: {
 595 |                             uuid: foundNode.uuid,
 596 |                             name: foundNode.name,
 597 |                             path: this.getNodePath(foundNode)
 598 |                         }
 599 |                     });
 600 |                 } else {
 601 |                     resolve({ success: false, error: `Node '${name}' not found` });
 602 |                 }
 603 |             }).catch((err: Error) => {
 604 |                 // 备用方案:使用场景脚本
 605 |                 const options = {
 606 |                     name: 'cocos-mcp-server',
 607 |                     method: 'findNodeByName',
 608 |                     args: [name]
 609 |                 };
 610 |                 
 611 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
 612 |                     resolve(result);
 613 |                 }).catch((err2: Error) => {
 614 |                     resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` });
 615 |                 });
 616 |             });
 617 |         });
 618 |     }
 619 | 
 620 |     private searchNodeInTree(node: any, targetName: string): any {
 621 |         if (node.name === targetName) {
 622 |             return node;
 623 |         }
 624 |         
 625 |         if (node.children) {
 626 |             for (const child of node.children) {
 627 |                 const found = this.searchNodeInTree(child, targetName);
 628 |                 if (found) {
 629 |                     return found;
 630 |                 }
 631 |             }
 632 |         }
 633 |         
 634 |         return null;
 635 |     }
 636 | 
 637 |     private async getAllNodes(): Promise<ToolResponse> {
 638 |         return new Promise((resolve) => {
 639 |             // 尝试查询场景节点树
 640 |             Editor.Message.request('scene', 'query-node-tree').then((tree: any) => {
 641 |                 const nodes: any[] = [];
 642 |                 
 643 |                 const traverseTree = (node: any) => {
 644 |                     nodes.push({
 645 |                         uuid: node.uuid,
 646 |                         name: node.name,
 647 |                         type: node.type,
 648 |                         active: node.active,
 649 |                         path: this.getNodePath(node)
 650 |                     });
 651 |                     
 652 |                     if (node.children) {
 653 |                         for (const child of node.children) {
 654 |                             traverseTree(child);
 655 |                         }
 656 |                     }
 657 |                 };
 658 |                 
 659 |                 if (tree && tree.children) {
 660 |                     traverseTree(tree);
 661 |                 }
 662 |                 
 663 |                 resolve({
 664 |                     success: true,
 665 |                     data: {
 666 |                         totalNodes: nodes.length,
 667 |                         nodes: nodes
 668 |                     }
 669 |                 });
 670 |             }).catch((err: Error) => {
 671 |                 // 备用方案:使用场景脚本
 672 |                 const options = {
 673 |                     name: 'cocos-mcp-server',
 674 |                     method: 'getAllNodes',
 675 |                     args: []
 676 |                 };
 677 |                 
 678 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
 679 |                     resolve(result);
 680 |                 }).catch((err2: Error) => {
 681 |                     resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` });
 682 |                 });
 683 |             });
 684 |         });
 685 |     }
 686 | 
 687 |     private getNodePath(node: any): string {
 688 |         const path = [node.name];
 689 |         let current = node.parent;
 690 |         while (current && current.name !== 'Canvas') {
 691 |             path.unshift(current.name);
 692 |             current = current.parent;
 693 |         }
 694 |         return path.join('/');
 695 |     }
 696 | 
 697 |     private async setNodeProperty(uuid: string, property: string, value: any): Promise<ToolResponse> {
 698 |         return new Promise((resolve) => {
 699 |             // 尝试直接使用 Editor API 设置节点属性
 700 |             Editor.Message.request('scene', 'set-property', {
 701 |                 uuid: uuid,
 702 |                 path: property,
 703 |                 dump: {
 704 |                     value: value
 705 |                 }
 706 |             }).then(() => {
 707 |                 // Get comprehensive verification data including updated node info
 708 |                 this.getNodeInfo(uuid).then((nodeInfo) => {
 709 |                     resolve({
 710 |                         success: true,
 711 |                         message: `Property '${property}' updated successfully`,
 712 |                         data: {
 713 |                             nodeUuid: uuid,
 714 |                             property: property,
 715 |                             newValue: value
 716 |                         },
 717 |                         verificationData: {
 718 |                             nodeInfo: nodeInfo.data,
 719 |                             changeDetails: {
 720 |                                 property: property,
 721 |                                 value: value,
 722 |                                 timestamp: new Date().toISOString()
 723 |                             }
 724 |                         }
 725 |                     });
 726 |                 }).catch(() => {
 727 |                     resolve({
 728 |                         success: true,
 729 |                         message: `Property '${property}' updated successfully (verification failed)`
 730 |                     });
 731 |                 });
 732 |             }).catch((err: Error) => {
 733 |                 // 如果直接设置失败,尝试使用场景脚本
 734 |                 const options = {
 735 |                     name: 'cocos-mcp-server',
 736 |                     method: 'setNodeProperty',
 737 |                     args: [uuid, property, value]
 738 |                 };
 739 |                 
 740 |                 Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => {
 741 |                     resolve(result);
 742 |                 }).catch((err2: Error) => {
 743 |                     resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` });
 744 |                 });
 745 |             });
 746 |         });
 747 |     }
 748 | 
 749 |     private async setNodeTransform(args: any): Promise<ToolResponse> {
 750 |         return new Promise(async (resolve) => {
 751 |             const { uuid, position, rotation, scale } = args;
 752 |             const updatePromises: Promise<any>[] = [];
 753 |             const updates: string[] = [];
 754 |             const warnings: string[] = [];
 755 |             
 756 |             try {
 757 |                 // First get node info to determine if it's 2D or 3D
 758 |                 const nodeInfoResponse = await this.getNodeInfo(uuid);
 759 |                 if (!nodeInfoResponse.success || !nodeInfoResponse.data) {
 760 |                     resolve({ success: false, error: 'Failed to get node information' });
 761 |                     return;
 762 |                 }
 763 |                 
 764 |                 const nodeInfo = nodeInfoResponse.data;
 765 |                 const is2DNode = this.is2DNode(nodeInfo);
 766 |                 
 767 |                 if (position) {
 768 |                     const normalizedPosition = this.normalizeTransformValue(position, 'position', is2DNode);
 769 |                     if (normalizedPosition.warning) {
 770 |                         warnings.push(normalizedPosition.warning);
 771 |                     }
 772 |                     
 773 |                     updatePromises.push(
 774 |                         Editor.Message.request('scene', 'set-property', {
 775 |                             uuid: uuid,
 776 |                             path: 'position',
 777 |                             dump: { value: normalizedPosition.value }
 778 |                         })
 779 |                     );
 780 |                     updates.push('position');
 781 |                 }
 782 |                 
 783 |                 if (rotation) {
 784 |                     const normalizedRotation = this.normalizeTransformValue(rotation, 'rotation', is2DNode);
 785 |                     if (normalizedRotation.warning) {
 786 |                         warnings.push(normalizedRotation.warning);
 787 |                     }
 788 |                     
 789 |                     updatePromises.push(
 790 |                         Editor.Message.request('scene', 'set-property', {
 791 |                             uuid: uuid,
 792 |                             path: 'rotation',
 793 |                             dump: { value: normalizedRotation.value }
 794 |                         })
 795 |                     );
 796 |                     updates.push('rotation');
 797 |                 }
 798 |                 
 799 |                 if (scale) {
 800 |                     const normalizedScale = this.normalizeTransformValue(scale, 'scale', is2DNode);
 801 |                     if (normalizedScale.warning) {
 802 |                         warnings.push(normalizedScale.warning);
 803 |                     }
 804 |                     
 805 |                     updatePromises.push(
 806 |                         Editor.Message.request('scene', 'set-property', {
 807 |                             uuid: uuid,
 808 |                             path: 'scale',
 809 |                             dump: { value: normalizedScale.value }
 810 |                         })
 811 |                     );
 812 |                     updates.push('scale');
 813 |                 }
 814 |                 
 815 |                 if (updatePromises.length === 0) {
 816 |                     resolve({ success: false, error: 'No transform properties specified' });
 817 |                     return;
 818 |                 }
 819 |                 
 820 |                 await Promise.all(updatePromises);
 821 |                 
 822 |                 // Verify the changes by getting updated node info
 823 |                 const updatedNodeInfo = await this.getNodeInfo(uuid);
 824 |                 const response: any = {
 825 |                     success: true,
 826 |                     message: `Transform properties updated: ${updates.join(', ')} ${is2DNode ? '(2D node)' : '(3D node)'}`,
 827 |                     updatedProperties: updates,
 828 |                     data: {
 829 |                         nodeUuid: uuid,
 830 |                         nodeType: is2DNode ? '2D' : '3D',
 831 |                         appliedChanges: updates,
 832 |                         transformConstraints: {
 833 |                             position: is2DNode ? 'x, y only (z ignored)' : 'x, y, z all used',
 834 |                             rotation: is2DNode ? 'z only (x, y ignored)' : 'x, y, z all used',
 835 |                             scale: is2DNode ? 'x, y main, z typically 1' : 'x, y, z all used'
 836 |                         }
 837 |                     },
 838 |                     verificationData: {
 839 |                         nodeInfo: updatedNodeInfo.data,
 840 |                         transformDetails: {
 841 |                             originalNodeType: is2DNode ? '2D' : '3D',
 842 |                             appliedTransforms: updates,
 843 |                             timestamp: new Date().toISOString()
 844 |                         },
 845 |                         beforeAfterComparison: {
 846 |                             before: nodeInfo,
 847 |                             after: updatedNodeInfo.data
 848 |                         }
 849 |                     }
 850 |                 };
 851 |                 
 852 |                 if (warnings.length > 0) {
 853 |                     response.warning = warnings.join('; ');
 854 |                 }
 855 |                 
 856 |                 resolve(response);
 857 |                 
 858 |             } catch (err: any) {
 859 |                 resolve({ 
 860 |                     success: false, 
 861 |                     error: `Failed to update transform: ${err.message}` 
 862 |                 });
 863 |             }
 864 |         });
 865 |     }
 866 | 
 867 |     private is2DNode(nodeInfo: any): boolean {
 868 |         // Check if node has 2D-specific components or is under Canvas
 869 |         const components = nodeInfo.components || [];
 870 |         
 871 |         // Check for common 2D components
 872 |         const has2DComponents = components.some((comp: any) => 
 873 |             comp.type && (
 874 |                 comp.type.includes('cc.Sprite') ||
 875 |                 comp.type.includes('cc.Label') ||
 876 |                 comp.type.includes('cc.Button') ||
 877 |                 comp.type.includes('cc.Layout') ||
 878 |                 comp.type.includes('cc.Widget') ||
 879 |                 comp.type.includes('cc.Mask') ||
 880 |                 comp.type.includes('cc.Graphics')
 881 |             )
 882 |         );
 883 |         
 884 |         if (has2DComponents) {
 885 |             return true;
 886 |         }
 887 |         
 888 |         // Check for 3D-specific components  
 889 |         const has3DComponents = components.some((comp: any) =>
 890 |             comp.type && (
 891 |                 comp.type.includes('cc.MeshRenderer') ||
 892 |                 comp.type.includes('cc.Camera') ||
 893 |                 comp.type.includes('cc.Light') ||
 894 |                 comp.type.includes('cc.DirectionalLight') ||
 895 |                 comp.type.includes('cc.PointLight') ||
 896 |                 comp.type.includes('cc.SpotLight')
 897 |             )
 898 |         );
 899 |         
 900 |         if (has3DComponents) {
 901 |             return false;
 902 |         }
 903 |         
 904 |         // Default heuristic: if z position is 0 and hasn't been changed, likely 2D
 905 |         const position = nodeInfo.position;
 906 |         if (position && Math.abs(position.z) < 0.001) {
 907 |             return true;
 908 |         }
 909 |         
 910 |         // Default to 3D if uncertain
 911 |         return false;
 912 |     }
 913 | 
 914 |     private normalizeTransformValue(value: any, type: 'position' | 'rotation' | 'scale', is2D: boolean): { value: any, warning?: string } {
 915 |         const result = { ...value };
 916 |         let warning: string | undefined;
 917 |         
 918 |         if (is2D) {
 919 |             switch (type) {
 920 |                 case 'position':
 921 |                     if (value.z !== undefined && Math.abs(value.z) > 0.001) {
 922 |                         warning = `2D node: z position (${value.z}) ignored, set to 0`;
 923 |                         result.z = 0;
 924 |                     } else if (value.z === undefined) {
 925 |                         result.z = 0;
 926 |                     }
 927 |                     break;
 928 |                     
 929 |                 case 'rotation':
 930 |                     if ((value.x !== undefined && Math.abs(value.x) > 0.001) || 
 931 |                         (value.y !== undefined && Math.abs(value.y) > 0.001)) {
 932 |                         warning = `2D node: x,y rotations ignored, only z rotation applied`;
 933 |                         result.x = 0;
 934 |                         result.y = 0;
 935 |                     } else {
 936 |                         result.x = result.x || 0;
 937 |                         result.y = result.y || 0;
 938 |                     }
 939 |                     result.z = result.z || 0;
 940 |                     break;
 941 |                     
 942 |                 case 'scale':
 943 |                     if (value.z === undefined) {
 944 |                         result.z = 1; // Default scale for 2D
 945 |                     }
 946 |                     break;
 947 |             }
 948 |         } else {
 949 |             // 3D node - ensure all axes are defined
 950 |             result.x = result.x !== undefined ? result.x : (type === 'scale' ? 1 : 0);
 951 |             result.y = result.y !== undefined ? result.y : (type === 'scale' ? 1 : 0);
 952 |             result.z = result.z !== undefined ? result.z : (type === 'scale' ? 1 : 0);
 953 |         }
 954 |         
 955 |         return { value: result, warning };
 956 |     }
 957 | 
 958 |     private async deleteNode(uuid: string): Promise<ToolResponse> {
 959 |         return new Promise((resolve) => {
 960 |             Editor.Message.request('scene', 'remove-node', { uuid: uuid }).then(() => {
 961 |                 resolve({
 962 |                     success: true,
 963 |                     message: 'Node deleted successfully'
 964 |                 });
 965 |             }).catch((err: Error) => {
 966 |                 resolve({ success: false, error: err.message });
 967 |             });
 968 |         });
 969 |     }
 970 | 
 971 |     private async moveNode(nodeUuid: string, newParentUuid: string, siblingIndex: number = -1): Promise<ToolResponse> {
 972 |         return new Promise((resolve) => {
 973 |             // Use correct set-parent API instead of move-node
 974 |             Editor.Message.request('scene', 'set-parent', {
 975 |                 parent: newParentUuid,
 976 |                 uuids: [nodeUuid],
 977 |                 keepWorldTransform: false
 978 |             }).then(() => {
 979 |                 resolve({
 980 |                     success: true,
 981 |                     message: 'Node moved successfully'
 982 |                 });
 983 |             }).catch((err: Error) => {
 984 |                 resolve({ success: false, error: err.message });
 985 |             });
 986 |         });
 987 |     }
 988 | 
 989 |     private async duplicateNode(uuid: string, includeChildren: boolean = true): Promise<ToolResponse> {
 990 |         return new Promise((resolve) => {
 991 |             // Note: includeChildren parameter is accepted for future use but not currently implemented
 992 |             Editor.Message.request('scene', 'duplicate-node', uuid).then((result: any) => {
 993 |                 resolve({
 994 |                     success: true,
 995 |                     data: {
 996 |                         newUuid: result.uuid,
 997 |                         message: 'Node duplicated successfully'
 998 |                     }
 999 |                 });
1000 |             }).catch((err: Error) => {
1001 |                 resolve({ success: false, error: err.message });
1002 |             });
1003 |         });
1004 |     }
1005 | 
1006 |     private async detectNodeType(uuid: string): Promise<ToolResponse> {
1007 |         return new Promise(async (resolve) => {
1008 |             try {
1009 |                 const nodeInfoResponse = await this.getNodeInfo(uuid);
1010 |                 if (!nodeInfoResponse.success || !nodeInfoResponse.data) {
1011 |                     resolve({ success: false, error: 'Failed to get node information' });
1012 |                     return;
1013 |                 }
1014 | 
1015 |                 const nodeInfo = nodeInfoResponse.data;
1016 |                 const is2D = this.is2DNode(nodeInfo);
1017 |                 const components = nodeInfo.components || [];
1018 |                 
1019 |                 // Collect detection reasons
1020 |                 const detectionReasons: string[] = [];
1021 |                 
1022 |                 // Check for 2D components
1023 |                 const twoDComponents = components.filter((comp: any) => 
1024 |                     comp.type && (
1025 |                         comp.type.includes('cc.Sprite') ||
1026 |                         comp.type.includes('cc.Label') ||
1027 |                         comp.type.includes('cc.Button') ||
1028 |                         comp.type.includes('cc.Layout') ||
1029 |                         comp.type.includes('cc.Widget') ||
1030 |                         comp.type.includes('cc.Mask') ||
1031 |                         comp.type.includes('cc.Graphics')
1032 |                     )
1033 |                 );
1034 |                 
1035 |                 // Check for 3D components
1036 |                 const threeDComponents = components.filter((comp: any) =>
1037 |                     comp.type && (
1038 |                         comp.type.includes('cc.MeshRenderer') ||
1039 |                         comp.type.includes('cc.Camera') ||
1040 |                         comp.type.includes('cc.Light') ||
1041 |                         comp.type.includes('cc.DirectionalLight') ||
1042 |                         comp.type.includes('cc.PointLight') ||
1043 |                         comp.type.includes('cc.SpotLight')
1044 |                     )
1045 |                 );
1046 | 
1047 |                 if (twoDComponents.length > 0) {
1048 |                     detectionReasons.push(`Has 2D components: ${twoDComponents.map((c: any) => c.type).join(', ')}`);
1049 |                 }
1050 |                 
1051 |                 if (threeDComponents.length > 0) {
1052 |                     detectionReasons.push(`Has 3D components: ${threeDComponents.map((c: any) => c.type).join(', ')}`);
1053 |                 }
1054 |                 
1055 |                 // Check position for heuristic
1056 |                 const position = nodeInfo.position;
1057 |                 if (position && Math.abs(position.z) < 0.001) {
1058 |                     detectionReasons.push('Z position is ~0 (likely 2D)');
1059 |                 } else if (position && Math.abs(position.z) > 0.001) {
1060 |                     detectionReasons.push(`Z position is ${position.z} (likely 3D)`);
1061 |                 }
1062 | 
1063 |                 if (detectionReasons.length === 0) {
1064 |                     detectionReasons.push('No specific indicators found, defaulting based on heuristics');
1065 |                 }
1066 | 
1067 |                 resolve({
1068 |                     success: true,
1069 |                     data: {
1070 |                         nodeUuid: uuid,
1071 |                         nodeName: nodeInfo.name,
1072 |                         nodeType: is2D ? '2D' : '3D',
1073 |                         detectionReasons: detectionReasons,
1074 |                         components: components.map((comp: any) => ({
1075 |                             type: comp.type,
1076 |                             category: this.getComponentCategory(comp.type)
1077 |                         })),
1078 |                         position: nodeInfo.position,
1079 |                         transformConstraints: {
1080 |                             position: is2D ? 'x, y only (z ignored)' : 'x, y, z all used',
1081 |                             rotation: is2D ? 'z only (x, y ignored)' : 'x, y, z all used',
1082 |                             scale: is2D ? 'x, y main, z typically 1' : 'x, y, z all used'
1083 |                         }
1084 |                     }
1085 |                 });
1086 |                 
1087 |             } catch (err: any) {
1088 |                 resolve({ 
1089 |                     success: false, 
1090 |                     error: `Failed to detect node type: ${err.message}` 
1091 |                 });
1092 |             }
1093 |         });
1094 |     }
1095 | 
1096 |     private getComponentCategory(componentType: string): string {
1097 |         if (!componentType) return 'unknown';
1098 |         
1099 |         if (componentType.includes('cc.Sprite') || componentType.includes('cc.Label') || 
1100 |             componentType.includes('cc.Button') || componentType.includes('cc.Layout') ||
1101 |             componentType.includes('cc.Widget') || componentType.includes('cc.Mask') ||
1102 |             componentType.includes('cc.Graphics')) {
1103 |             return '2D';
1104 |         }
1105 |         
1106 |         if (componentType.includes('cc.MeshRenderer') || componentType.includes('cc.Camera') ||
1107 |             componentType.includes('cc.Light') || componentType.includes('cc.DirectionalLight') ||
1108 |             componentType.includes('cc.PointLight') || componentType.includes('cc.SpotLight')) {
1109 |             return '3D';
1110 |         }
1111 |         
1112 |         return 'generic';
1113 |     }
1114 | }
```
Page 3/5FirstPrevNextLast