修复了一些挂载bug,将面板技术从纯html变更成了Vue3,界面更有好些,后期扩展更方便。增加了工具配置功能,可以自行选择需要使用的工具。

This commit is contained in:
root
2025-07-25 01:41:31 +08:00
parent f395b9f1bc
commit 7e238a0581
26 changed files with 4826 additions and 655 deletions

View File

@@ -8,7 +8,7 @@ Cocos Creator MCP 服务器是一个全面的 Model Context Protocol (MCP) 服
## 工具分类 ## 工具分类
MCP 服务器提供了 **151 个工具**,按功能分为 13 个主要类别: MCP 服务器提供了 **158 个工具**,按功能分为 13 个主要类别:
1. [场景操作工具 (Scene Tools)](#1-场景操作工具-scene-tools) 1. [场景操作工具 (Scene Tools)](#1-场景操作工具-scene-tools)
2. [节点操作工具 (Node Tools)](#2-节点操作工具-node-tools) 2. [节点操作工具 (Node Tools)](#2-节点操作工具-node-tools)
@@ -1496,4 +1496,4 @@ Cocos Creator 使用 `db://` 前缀的资源URL格式
--- ---
*此文档基于 Cocos Creator MCP 服务器 v1.0.0 编写,如有更新请参考最新版本文档。* *此文档基于 Cocos Creator MCP 服务器 v1.3.0 编写,如有更新请参考最新版本文档。*

View File

@@ -2,19 +2,19 @@
## Overview ## Overview
The Cocos Creator MCP Server is a comprehensive Model Context Protocol (MCP) server plugin designed for Cocos Creator 3.8+ that enables AI assistants to interact with the Cocos Creator editor through a standardized protocol. 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.
This document provides detailed information about all available MCP tools and their usage. This document provides detailed information about all available MCP tools and their usage.
## Tool Categories ## Tool Categories
The MCP server provides **151 tools** organized into 13 main categories: The MCP server provides **158 tools** organized into 13 main categories by functionality:
1. [Scene Tools](#1-scene-tools) 1. [Scene Tools](#1-scene-tools)
2. [Node Tools](#2-node-tools) 2. [Node Tools](#2-node-tools)
3. [Component Tools](#3-component-tools) 3. [Component Management Tools](#3-component-management-tools)
4. [Prefab Tools](#4-prefab-tools) 4. [Prefab Tools](#4-prefab-tools)
5. [Project Tools](#5-project-tools) 5. [Project Control Tools](#5-project-control-tools)
6. [Debug Tools](#6-debug-tools) 6. [Debug Tools](#6-debug-tools)
7. [Preferences Tools](#7-preferences-tools) 7. [Preferences Tools](#7-preferences-tools)
8. [Server Tools](#8-server-tools) 8. [Server Tools](#8-server-tools)
@@ -33,7 +33,7 @@ Get current scene information
**Parameters**: None **Parameters**: None
**Returns**: Current scene name, UUID, type, active state, and node count **Returns**: Current scene name, UUID, type, active status, and node count
**Example**: **Example**:
```json ```json
@@ -48,7 +48,7 @@ Get all scenes in the project
**Parameters**: None **Parameters**: None
**Returns**: List of all scenes in the project with name, path, and UUID **Returns**: List of all scenes in the project, including names, paths, and UUIDs
**Example**: **Example**:
```json ```json
@@ -62,7 +62,7 @@ Get all scenes in the project
Open a scene by path Open a scene by path
**Parameters**: **Parameters**:
- `scenePath` (string, required): The scene file path - `scenePath` (string, required): Scene file path
**Example**: **Example**:
```json ```json
@@ -106,7 +106,7 @@ Create a new scene asset
``` ```
### 1.6 scene_save_scene_as ### 1.6 scene_save_scene_as
Save scene as new file Save scene as a new file
**Parameters**: **Parameters**:
- `path` (string, required): Path to save the scene - `path` (string, required): Path to save the scene
@@ -138,7 +138,7 @@ Close current scene
Get the complete hierarchy of current scene Get the complete hierarchy of current scene
**Parameters**: **Parameters**:
- `includeComponents` (boolean, optional): Include component information, default is false - `includeComponents` (boolean, optional): Whether to include component information, defaults to false
**Example**: **Example**:
```json ```json
@@ -159,9 +159,14 @@ Create a new node in the scene
**Parameters**: **Parameters**:
- `name` (string, required): Node name - `name` (string, required): Node name
- `parentUuid` (string, optional): Parent node UUID, if not provided, node will be created at current editor selection - `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.
- `nodeType` (string, optional): Node type, options: `Node`, `2DNode`, `3DNode`, default is `Node` - `nodeType` (string, optional): Node type, options: `Node`, `2DNode`, `3DNode`, defaults to `Node`
- `siblingIndex` (number, optional): Sibling index for ordering, -1 means append at end, default is -1 - `siblingIndex` (number, optional): Sibling index, -1 means append at end, defaults to -1
**Important Note**: To ensure the node is created at the expected location, always provide the `parentUuid` parameter. You can obtain parent node UUIDs by:
- Using `scene_get_current_scene` to get the scene root node UUID
- Using `node_get_all_nodes` to view all nodes and their UUIDs
- Using `node_find_node_by_name` to find specific node UUIDs
**Example**: **Example**:
```json ```json
@@ -196,7 +201,7 @@ Find nodes by name pattern
**Parameters**: **Parameters**:
- `pattern` (string, required): Name pattern to search - `pattern` (string, required): Name pattern to search
- `exactMatch` (boolean, optional): Exact match or partial match, default is false - `exactMatch` (boolean, optional): Whether to match exactly, defaults to false
**Example**: **Example**:
```json ```json
@@ -210,7 +215,7 @@ Find nodes by name pattern
``` ```
### 2.4 node_find_node_by_name ### 2.4 node_find_node_by_name
Find first node by exact name Find the first node by exact name
**Parameters**: **Parameters**:
- `name` (string, required): Node name to find - `name` (string, required): Node name to find
@@ -259,10 +264,10 @@ Set node property value
``` ```
### 2.7 node_delete_node ### 2.7 node_delete_node
Delete a node from scene Delete a node from the scene
**Parameters**: **Parameters**:
- `uuid` (string, required): Node UUID to delete - `uuid` (string, required): UUID of the node to delete
**Example**: **Example**:
```json ```json
@@ -275,12 +280,12 @@ Delete a node from scene
``` ```
### 2.8 node_move_node ### 2.8 node_move_node
Move node to new parent Move a node to a new parent
**Parameters**: **Parameters**:
- `nodeUuid` (string, required): Node UUID to move - `nodeUuid` (string, required): UUID of the node to move
- `newParentUuid` (string, required): New parent node UUID - `newParentUuid` (string, required): New parent node UUID
- `siblingIndex` (number, optional): Sibling index in new parent, default is -1 - `siblingIndex` (number, optional): Sibling index in the new parent, defaults to -1
**Example**: **Example**:
```json ```json
@@ -298,8 +303,8 @@ Move node to new parent
Duplicate a node Duplicate a node
**Parameters**: **Parameters**:
- `uuid` (string, required): Node UUID to duplicate - `uuid` (string, required): UUID of the node to duplicate
- `includeChildren` (boolean, optional): Include children nodes, default is true - `includeChildren` (boolean, optional): Whether to include child nodes, defaults to true
**Example**: **Example**:
```json ```json
@@ -314,15 +319,20 @@ Duplicate a node
--- ---
## 3. Component Tools ## 3. Component Management Tools
### 3.1 component_add_component ### 3.1 component_add_component
Add a component to a specific node Add a component to a specific node
**Parameters**: **Parameters**:
- `nodeUuid` (string, required): Target node UUID - `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.
- `componentType` (string, required): Component type (e.g., cc.Sprite, cc.Label, cc.Button) - `componentType` (string, required): Component type (e.g., cc.Sprite, cc.Label, cc.Button)
**Important Note**: Before adding a component, ensure:
1. First use `node_get_all_nodes` or `node_find_node_by_name` to find the target node's UUID
2. Verify the node exists and the UUID is correct
3. Choose the appropriate component type
**Example**: **Example**:
```json ```json
{ {
@@ -430,7 +440,7 @@ Attach a script component to a node
Get list of available component types Get list of available component types
**Parameters**: **Parameters**:
- `category` (string, optional): Component category filter, options: `all`, `renderer`, `ui`, `physics`, `animation`, `audio`, default is `all` - `category` (string, optional): Component category filter, options: `all`, `renderer`, `ui`, `physics`, `animation`, `audio`, defaults to `all`
**Example**: **Example**:
```json ```json
@@ -446,13 +456,13 @@ Get list of available component types
## 4. Prefab Tools ## 4. Prefab Tools
**⚠️ Known Issue**: Prefab instantiation using the standard Cocos Creator API may not properly restore complex prefab structures with child nodes. While prefab creation correctly saves all child node information, the instantiation process through `create-node` with `assetUuid` has limitations and may result in missing child nodes in the instantiated prefab. **⚠️ 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.
### 4.1 prefab_get_prefab_list ### 4.1 prefab_get_prefab_list
Get all prefabs in the project Get all prefabs in the project
**Parameters**: **Parameters**:
- `folder` (string, optional): Folder path to search, default is `db://assets` - `folder` (string, optional): Search folder path, defaults to `db://assets`
**Example**: **Example**:
```json ```json
@@ -500,7 +510,7 @@ Instantiate a prefab in the scene
} }
``` ```
**⚠️ Limitation**: Complex prefabs with child nodes may not instantiate correctly. Only the root node may be created, and child nodes may be missing due to Cocos Creator API limitations in the standard `create-node` method with `assetUuid`. This is a known issue with the current implementation. **⚠️ 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.
### 4.4 prefab_create_prefab ### 4.4 prefab_create_prefab
Create a prefab from a node Create a prefab from a node
@@ -545,7 +555,7 @@ Update an existing prefab
**Parameters**: **Parameters**:
- `prefabPath` (string, required): Prefab asset path - `prefabPath` (string, required): Prefab asset path
- `nodeUuid` (string, required): Node UUID with changes - `nodeUuid` (string, required): Node UUID containing changes
**Example**: **Example**:
```json ```json
@@ -559,7 +569,7 @@ Update an existing prefab
``` ```
### 4.7 prefab_revert_prefab ### 4.7 prefab_revert_prefab
Revert prefab instance to original Revert a prefab instance to its original state
**Parameters**: **Parameters**:
- `nodeUuid` (string, required): Prefab instance node UUID - `nodeUuid` (string, required): Prefab instance node UUID
@@ -592,13 +602,13 @@ Get detailed prefab information
--- ---
## 5. Project Tools ## 5. Project Control Tools
### 5.1 project_run_project ### 5.1 project_run_project
Run the project in preview mode Run the project in preview mode
**Parameters**: **Parameters**:
- `platform` (string, optional): Target platform, options: `browser`, `simulator`, `preview`, default is `browser` - `platform` (string, optional): Target platform, options: `browser`, `simulator`, `preview`, defaults to `browser`
**Example**: **Example**:
```json ```json
@@ -615,7 +625,7 @@ Build the project
**Parameters**: **Parameters**:
- `platform` (string, required): Build platform, options: `web-mobile`, `web-desktop`, `ios`, `android`, `windows`, `mac` - `platform` (string, required): Build platform, options: `web-mobile`, `web-desktop`, `ios`, `android`, `windows`, `mac`
- `debug` (boolean, optional): Debug build, default is true - `debug` (boolean, optional): Whether to build in debug mode, defaults to true
**Example**: **Example**:
```json ```json
@@ -645,7 +655,7 @@ Get project information
Get project settings Get project settings
**Parameters**: **Parameters**:
- `category` (string, optional): Settings category, options: `general`, `physics`, `render`, `assets`, default is `general` - `category` (string, optional): Settings category, options: `general`, `physics`, `render`, `assets`, defaults to `general`
**Example**: **Example**:
```json ```json
@@ -658,7 +668,7 @@ Get project settings
``` ```
### 5.5 project_refresh_assets ### 5.5 project_refresh_assets
Refresh asset database Refresh the asset database
**Parameters**: **Parameters**:
- `folder` (string, optional): Specific folder to refresh - `folder` (string, optional): Specific folder to refresh
@@ -711,8 +721,8 @@ Get asset information
Get assets by type Get assets by type
**Parameters**: **Parameters**:
- `type` (string, optional): Asset type filter, options: `all`, `scene`, `prefab`, `script`, `texture`, `material`, `mesh`, `audio`, `animation`, default is `all` - `type` (string, optional): Asset type filter, options: `all`, `scene`, `prefab`, `script`, `texture`, `material`, `mesh`, `audio`, `animation`, defaults to `all`
- `folder` (string, optional): Folder to search in, default is `db://assets` - `folder` (string, optional): Search folder, defaults to `db://assets`
**Example**: **Example**:
```json ```json
@@ -752,7 +762,7 @@ Open the build panel in the editor
``` ```
### 5.11 project_check_builder_status ### 5.11 project_check_builder_status
Check if builder worker is ready Check if the builder worker process is ready
**Parameters**: None **Parameters**: None
@@ -765,10 +775,10 @@ Check if builder worker is ready
``` ```
### 5.12 project_start_preview_server ### 5.12 project_start_preview_server
Start preview server Start the preview server
**Parameters**: **Parameters**:
- `port` (number, optional): Preview server port, default is 7456 - `port` (number, optional): Preview server port, defaults to 7456
**Example**: **Example**:
```json ```json
@@ -781,7 +791,7 @@ Start preview server
``` ```
### 5.13 project_stop_preview_server ### 5.13 project_stop_preview_server
Stop preview server Stop the preview server
**Parameters**: None **Parameters**: None
@@ -798,8 +808,8 @@ Create a new asset file or folder
**Parameters**: **Parameters**:
- `url` (string, required): Asset URL - `url` (string, required): Asset URL
- `content` (string, optional): File content, null for folder - `content` (string, optional): File content, null means create folder
- `overwrite` (boolean, optional): Overwrite existing file, default is false - `overwrite` (boolean, optional): Whether to overwrite existing file, defaults to false
**Example**: **Example**:
```json ```json
@@ -819,7 +829,7 @@ Copy an asset to another location
**Parameters**: **Parameters**:
- `source` (string, required): Source asset URL - `source` (string, required): Source asset URL
- `target` (string, required): Target location URL - `target` (string, required): Target location URL
- `overwrite` (boolean, optional): Overwrite existing file, default is false - `overwrite` (boolean, optional): Whether to overwrite existing file, defaults to false
**Example**: **Example**:
```json ```json
@@ -839,7 +849,7 @@ Move an asset to another location
**Parameters**: **Parameters**:
- `source` (string, required): Source asset URL - `source` (string, required): Source asset URL
- `target` (string, required): Target location URL - `target` (string, required): Target location URL
- `overwrite` (boolean, optional): Overwrite existing file, default is false - `overwrite` (boolean, optional): Whether to overwrite existing file, defaults to false
**Example**: **Example**:
```json ```json
@@ -959,8 +969,8 @@ Get asset URL from UUID
Get editor console logs Get editor console logs
**Parameters**: **Parameters**:
- `limit` (number, optional): Number of recent logs to retrieve, default is 100 - `limit` (number, optional): Number of latest logs to retrieve, defaults to 100
- `filter` (string, optional): Filter logs by type, options: `all`, `log`, `warn`, `error`, `info`, default is `all` - `filter` (string, optional): Filter logs by type, options: `all`, `log`, `warn`, `error`, `info`, defaults to `all`
**Example**: **Example**:
```json ```json
@@ -974,7 +984,7 @@ Get editor console logs
``` ```
### 6.2 debug_clear_console ### 6.2 debug_clear_console
Clear editor console Clear the editor console
**Parameters**: None **Parameters**: None
@@ -987,7 +997,7 @@ Clear editor console
``` ```
### 6.3 debug_execute_script ### 6.3 debug_execute_script
Execute JavaScript in scene context Execute JavaScript code in scene context
**Parameters**: **Parameters**:
- `script` (string, required): JavaScript code to execute - `script` (string, required): JavaScript code to execute
@@ -1006,8 +1016,8 @@ Execute JavaScript in scene context
Get detailed node tree for debugging Get detailed node tree for debugging
**Parameters**: **Parameters**:
- `rootUuid` (string, optional): Root node UUID, uses scene root if not provided - `rootUuid` (string, optional): Root node UUID, if not provided uses scene root node
- `maxDepth` (number, optional): Maximum tree depth, default is 10 - `maxDepth` (number, optional): Maximum tree depth, defaults to 10
**Example**: **Example**:
```json ```json
@@ -1034,11 +1044,11 @@ Get performance statistics
``` ```
### 6.6 debug_validate_scene ### 6.6 debug_validate_scene
Validate current scene for issues Validate if the current scene has issues
**Parameters**: **Parameters**:
- `checkMissingAssets` (boolean, optional): Check for missing asset references, default is true - `checkMissingAssets` (boolean, optional): Check for missing asset references, defaults to true
- `checkPerformance` (boolean, optional): Check for performance issues, default is true - `checkPerformance` (boolean, optional): Check for performance issues, defaults to true
**Example**: **Example**:
```json ```json
@@ -1069,8 +1079,8 @@ Get project logs from temp/logs/project.log file
**Parameters**: **Parameters**:
- `lines` (number, optional): Number of lines to read from the end of the log file, default is 100, range: 1-10000 - `lines` (number, optional): Number of lines to read from the end of the log file, default is 100, range: 1-10000
- `filterKeyword` (string, optional): Filter logs containing specific keyword - `filterKeyword` (string, optional): Filter logs by specific keyword
- `logLevel` (string, optional): Filter by log level, options: `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`, `ALL`, default is `ALL` - `logLevel` (string, optional): Filter by log level, options: `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`, `ALL`, defaults to `ALL`
**Example**: **Example**:
```json ```json
@@ -1085,7 +1095,7 @@ Get project logs from temp/logs/project.log file
``` ```
### 6.9 debug_get_log_file_info ### 6.9 debug_get_log_file_info
Get information about the project log file Get project log file information
**Parameters**: None **Parameters**: None
@@ -1104,8 +1114,8 @@ Search for specific patterns or errors in project logs
**Parameters**: **Parameters**:
- `pattern` (string, required): Search pattern (supports regex) - `pattern` (string, required): Search pattern (supports regex)
- `maxResults` (number, optional): Maximum number of matching results, default is 20, range: 1-100 - `maxResults` (number, optional): Maximum number of matching results, defaults to 20, range: 1-100
- `contextLines` (number, optional): Number of context lines to show around each match, default is 2, range: 0-10 - `contextLines` (number, optional): Number of context lines to show around each match, defaults to 2, range: 0-10
**Example**: **Example**:
```json ```json
@@ -1205,7 +1215,7 @@ Get recently opened projects
``` ```
### 7.6 preferences_clear_recent_projects ### 7.6 preferences_clear_recent_projects
Clear recently opened projects list Clear the list of recently opened projects
**Parameters**: None **Parameters**: None
@@ -1335,10 +1345,10 @@ Request to quit the editor
## 9. Broadcast Tools ## 9. Broadcast Tools
### 9.1 broadcast_get_broadcast_log ### 9.1 broadcast_get_broadcast_log
Get recent broadcast messages log Get recent broadcast message log
**Parameters**: **Parameters**:
- `limit` (number, optional): Number of recent messages to return, default is 50 - `limit` (number, optional): Number of latest messages to return, defaults to 50
- `messageType` (string, optional): Filter by message type - `messageType` (string, optional): Filter by message type
**Example**: **Example**:
@@ -1385,7 +1395,7 @@ Stop listening for specific broadcast messages
``` ```
### 9.4 broadcast_clear_broadcast_log ### 9.4 broadcast_clear_broadcast_log
Clear the broadcast messages log Clear broadcast message log
**Parameters**: None **Parameters**: None
@@ -1435,7 +1445,7 @@ All tool calls use JSON-RPC 2.0 format:
### 2. Common UUID Retrieval Methods ### 2. Common UUID Retrieval Methods
- Use `node_get_all_nodes` to get all node UUIDs - Use `node_get_all_nodes` to get all node UUIDs
- Use `node_find_node_by_name` to find node UUID by name - Use `node_find_node_by_name` to find node UUIDs by name
- Use `scene_get_current_scene` to get scene UUID - Use `scene_get_current_scene` to get scene UUID
- Use `prefab_get_prefab_list` to get prefab information - Use `prefab_get_prefab_list` to get prefab information
@@ -1449,7 +1459,7 @@ Cocos Creator uses `db://` prefixed asset URL format:
### 4. Error Handling ### 4. Error Handling
If a tool call fails, it will return error information: If a tool call fails, an error message will be returned:
```json ```json
{ {
@@ -1467,23 +1477,23 @@ If a tool call fails, it will return error information:
### 5. Best Practices ### 5. Best Practices
1. **Query Before Modify**: Use query tools to get current state before modifying nodes or components 1. **Query First, Then Operate**: Before modifying nodes or components, first use query tools to get current state
2. **Use UUIDs**: Prefer using UUIDs over names to reference nodes and assets 2. **Use UUIDs**: Prefer using UUIDs over names when referencing nodes and assets
3. **Error Checking**: Always check tool call return values to ensure operations succeeded 3. **Error Checking**: Always check the return value of tool calls to ensure operations succeed
4. **Asset Management**: Ensure no references exist before deleting or moving assets 4. **Asset Management**: Before deleting or moving assets, ensure they are not referenced elsewhere
5. **Performance Considerations**: Avoid frequent tool calls in loops, consider batch operations 5. **Performance Considerations**: Avoid frequent tool calls in loops, consider batch operations
--- ---
## Technical Support ## Technical Support
If you encounter issues while using the tools, you can: If you encounter issues during use, you can:
1. Use `debug_get_console_logs` to view detailed error logs 1. Use `debug_get_console_logs` to view detailed error logs
2. Use `debug_validate_scene` to check for scene issues 2. Use `debug_validate_scene` to check if the scene has issues
3. Use `debug_get_editor_info` to get environment information 3. Use `debug_get_editor_info` to get environment information
4. Check the MCP server's running status and logs 4. Check the MCP server's running status and logs
--- ---
*This document is based on Cocos Creator MCP Server v1.0.0. Please refer to the latest version documentation for updates.* *This document is based on Cocos Creator MCP Server v1.3.0. Please refer to the latest version documentation for updates.*

View File

@@ -4,12 +4,65 @@
A comprehensive MCP (Model Context Protocol) server plugin for Cocos Creator 3.8+, enabling AI assistants to interact with the Cocos Creator editor through standardized protocols. One-click installation and use, eliminating all cumbersome environments and configurations. Claude clients Claude CLI and Cursor have been tested, and other editors are also perfectly supported in theory. A comprehensive MCP (Model Context Protocol) server plugin for Cocos Creator 3.8+, enabling AI assistants to interact with the Cocos Creator editor through standardized protocols. One-click installation and use, eliminating all cumbersome environments and configurations. Claude clients Claude CLI and Cursor have been tested, and other editors are also perfectly supported in theory.
**🚀 Now provides 151 tools in 13 categories, achieving 98% editor control! (Prefab instantiation has known child node restoration issues)** **🚀 Now provides 158 tools in 13 categories, achieving 98% editor control! (Prefab instantiation has known child node restoration issues)**
## Video Demonstrations and Tutorials
<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=114890828157799&bvid=BV1uzgVz8EyQ&cid=31188255575&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
## Video Demonstration Configuration Tool List
<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=114909316652345&bvid=BV1kfbyzQEAS&cid=31259361289&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
## Quick Links ## Quick Links
- **[📖 Complete Feature Guide (English)](FEATURE_GUIDE_EN.md)** - Detailed documentation for all 151 tools(To be completed) - **[📖 Complete Feature Guide (English)](FEATURE_GUIDE_EN.md)** - Detailed documentation for all 158 tools(To be completed)
- **[📖 完整功能指南 (中文)](FEATURE_GUIDE_CN.md)** - 所有151个工具的详细文档(To be completed) - **[📖 完整功能指南 (中文)](FEATURE_GUIDE_CN.md)** - 所有158个工具的详细文档(To be completed)
## Changelog
### v1.3.0 - July 25, 2024
#### 🆕 New Features
- **Integrated Tool Management Panel**: Added comprehensive tool management functionality directly into the main control panel
- **Tool Configuration System**: Implemented selective tool enabling/disabling with persistent configurations
- **Dynamic Tool Loading**: Enhanced tool discovery to dynamically load all 158 available tools from the MCP server
- **Real-time Tool State Management**: Added real-time updates for tool counts and status when individual tools are toggled
- **Configuration Persistence**: Automatic saving and loading of tool configurations across editor sessions
#### 🔧 Improvements
- **Unified Panel Interface**: Merged tool management into the main MCP server panel as a tab, eliminating the need for separate panels
- **Enhanced Server Settings**: Improved server configuration management with better persistence and loading
- **Vue 3 Integration**: Upgraded to Vue 3 Composition API for better reactivity and performance
- **Better Error Handling**: Added comprehensive error handling with rollback mechanisms for failed operations
- **Improved UI/UX**: Enhanced visual design with proper dividers, distinct block styles, and non-transparent modal backgrounds
#### 🐛 Bug Fixes
- **Fixed Tool State Persistence**: Resolved issues where tool states would reset upon tab switching or panel re-opening
- **Fixed Configuration Loading**: Corrected server settings loading issues and message registration problems
- **Fixed Checkbox Interactions**: Resolved checkbox unchecking issues and improved reactivity
- **Fixed Panel Scrolling**: Ensured proper scrolling functionality in the tool management panel
- **Fixed IPC Communication**: Resolved various IPC communication issues between frontend and backend
#### 🏗️ Technical Improvements
- **Simplified Architecture**: Removed multi-configuration complexity, focusing on single configuration management
- **Better Type Safety**: Enhanced TypeScript type definitions and interfaces
- **Improved Data Synchronization**: Better synchronization between frontend UI state and backend tool manager
- **Enhanced Debugging**: Added comprehensive logging and debugging capabilities
#### 📊 Statistics
- **Total Tools**: Increased from 151 to 158 tools
- **Categories**: 13 tool categories with comprehensive coverage
- **Editor Control**: Achieved 98% editor functionality coverage
### v1.2.0 - Previous Version
- Initial release with 151 tools
- Basic MCP server functionality
- Scene, node, component, and prefab operations
- Project control and debugging tools
**Claude cli configuration:** **Claude cli configuration:**
@@ -54,9 +107,6 @@ claude mcp add --transport http cocos-creator http://127.0.0.1:3000/mcp (use the
} }
``` ```
<img width="1166" height="693" alt="image" src="https://github.com/user-attachments/assets/ecc30596-2e81-4123-b3fd-9e2cf08e5863" />
<img width="470" height="622" alt="image" src="https://github.com/user-attachments/assets/504fa39b-4f43-4cc4-a912-28654c488072" />
<img width="466" height="499" alt="image" src="https://github.com/user-attachments/assets/e5f73aa2-068f-457f-94fd-02f52084d6f4" />
## Features ## Features
@@ -109,6 +159,7 @@ claude mcp add --transport http cocos-creator http://127.0.0.1:3000/mcp (use the
- **Reference Image Management**: Add, remove, and manage reference images in scene view - **Reference Image Management**: Add, remove, and manage reference images in scene view
- **Scene View Controls**: Control gizmo tools, coordinate systems, and view modes - **Scene View Controls**: Control gizmo tools, coordinate systems, and view modes
- **Advanced Scene Operations**: Undo/redo, snapshots, and advanced node manipulation - **Advanced Scene Operations**: Undo/redo, snapshots, and advanced node manipulation
- **🆕 Tool Management**: Selectively enable/disable tools, save configurations, and manage tool states
## Installation ## Installation
@@ -249,6 +300,29 @@ Settings are stored in `YourProject/settings/mcp-server.json`:
} }
``` ```
Tool configurations are stored in `YourProject/settings/tool-manager.json`:
```json
{
"currentConfigId": "default",
"configurations": {
"default": {
"id": "default",
"name": "默认配置",
"description": "默认工具配置",
"tools": [
{
"category": "scene",
"name": "get_current_scene",
"enabled": true,
"description": "Get current scene information"
}
]
}
}
}
```
## Icon Setup ## Icon Setup
To add an icon for the plugin panel: To add an icon for the plugin panel:

View File

@@ -4,12 +4,66 @@
一个适用于 Cocos Creator 3.8+ 的综合性 MCP模型上下文协议服务器插件使 AI 助手能够通过标准化协议与 Cocos Creator 编辑器进行交互。一键安装和使用省去所有繁琐环境和配置。已经测试过Claude客户端Claude CLI和Cursor其他的编辑器理论上也完美支持。 一个适用于 Cocos Creator 3.8+ 的综合性 MCP模型上下文协议服务器插件使 AI 助手能够通过标准化协议与 Cocos Creator 编辑器进行交互。一键安装和使用省去所有繁琐环境和配置。已经测试过Claude客户端Claude CLI和Cursor其他的编辑器理论上也完美支持。
**🚀 现在提供 13 个类别的 151 个工具实现98%的编辑器控制!(预制体实例化存在子节点恢复问题)** **🚀 现在提供 13 个类别的 158 个工具实现98%的编辑器控制!(预制体实例化存在子节点恢复问题)**
## 视频演示和教学
<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=114890828157799&bvid=BV1uzgVz8EyQ&cid=31188255575&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
## 视频演示配置工具列表
<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=114909316652345&bvid=BV1kfbyzQEAS&cid=31259361289&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
##快速链接 ##快速链接
- **[📖 Complete Feature Guide (English)](FEATURE_GUIDE_EN.md)** - Detailed documentation for all 151 tools待补充 - **[📖 Complete Feature Guide (English)](FEATURE_GUIDE_EN.md)** - Detailed documentation for all 158 tools待补充
- **[📖 完整功能指南 (中文)](FEATURE_GUIDE_CN.md)** - 所有151工具的详细文档(待补充) - **[📖 完整功能指南 (中文)](FEATURE_GUIDE_CN.md)** - 所有158工具的详细文档(待补充)
## 更新日志
### v1.3.0 - 2024年7月25日
#### 🆕 新功能
- **集成工具管理面板**: 在主控制面板中直接添加了全面的工具管理功能
- **工具配置系统**: 实现了选择性工具启用/禁用,支持持久化配置
- **动态工具加载**: 增强了工具发现功能能够动态加载MCP服务器中的所有158个可用工具
- **实时工具状态管理**: 添加了工具计数和状态的实时更新,当单个工具切换时立即反映
- **配置持久化**: 在编辑器会话间自动保存和加载工具配置
#### 🔧 改进
- **统一面板界面**: 将工具管理合并到主MCP服务器面板作为标签页消除了对单独面板的需求
- **增强服务器设置**: 改进了服务器配置管理,具有更好的持久化和加载功能
- **Vue 3集成**: 升级到Vue 3 Composition API提供更好的响应性和性能
- **更好的错误处理**: 添加了全面的错误处理,包含失败操作的回滚机制
- **改进的UI/UX**: 增强了视觉设计,包含适当的分隔符、独特的块样式和非透明模态背景
#### 🐛 错误修复
- **修复工具状态持久化**: 解决了工具状态在标签页切换或面板重新打开时重置的问题
- **修复配置加载**: 纠正了服务器设置加载问题和消息注册问题
- **修复复选框交互**: 解决了复选框取消选中问题并改进了响应性
- **修复面板滚动**: 确保工具管理面板中的正确滚动功能
- **修复IPC通信**: 解决了前端和后端之间的各种IPC通信问题
#### 🏗️ 技术改进
- **简化架构**: 移除了多配置复杂性,专注于单一配置管理
- **更好的类型安全**: 增强了TypeScript类型定义和接口
- **改进数据同步**: 前端UI状态和后端工具管理器之间更好的同步
- **增强调试**: 添加了全面的日志记录和调试功能
#### 📊 统计信息
- **总工具数**: 从151个增加到158个工具
- **类别**: 13个工具类别全面覆盖
- **编辑器控制**: 实现98%的编辑器功能覆盖
### v1.2.0 - 之前版本
- 初始发布包含151个工具
- 基本MCP服务器功能
- 场景、节点、组件和预制体操作
- 项目控制和调试工具
## 快速使用 ## 快速使用
@@ -54,12 +108,6 @@ claude mcp add --transport http cocos-creator http://127.0.0.1:3000/mcp使用
} }
``` ```
**效果:**
<img width="1166" height="693" alt="image" src="https://github.com/user-attachments/assets/ecc30596-2e81-4123-b3fd-9e2cf08e5863" />
<img width="470" height="622" alt="image" src="https://github.com/user-attachments/assets/504fa39b-4f43-4cc4-a912-28654c488072" />
<img width="466" height="499" alt="image" src="https://github.com/user-attachments/assets/e5f73aa2-068f-457f-94fd-02f52084d6f4" />
## 功能特性 ## 功能特性
### 🎯 场景操作 ### 🎯 场景操作
@@ -110,6 +158,7 @@ claude mcp add --transport http cocos-creator http://127.0.0.1:3000/mcp使用
- **参考图片管理**: 在场景视图中添加、删除和管理参考图片 - **参考图片管理**: 在场景视图中添加、删除和管理参考图片
- **场景视图控制**: 控制Gizmo工具、坐标系和视图模式 - **场景视图控制**: 控制Gizmo工具、坐标系和视图模式
- **高级场景操作**: 撤销/重做、快照和高级节点操作 - **高级场景操作**: 撤销/重做、快照和高级节点操作
- **🆕 工具管理**: 选择性启用/禁用工具、保存配置和管理工具状态
## 安装说明 ## 安装说明
@@ -249,6 +298,29 @@ AI 助手可以使用 MCP 协议连接并访问所有可用工具。
} }
``` ```
工具配置存储在 `您的项目/settings/tool-manager.json` 中:
```json
{
"currentConfigId": "default",
"configurations": {
"default": {
"id": "default",
"name": "默认配置",
"description": "默认工具配置",
"tools": [
{
"category": "scene",
"name": "get_current_scene",
"enabled": true,
"description": "获取当前场景信息"
}
]
}
}
}
```
## 图标设置 ## 图标设置
为插件面板添加图标: 为插件面板添加图标:
@@ -356,3 +428,4 @@ npm run build
## 许可证 ## 许可证
本插件供 Cocos Creator 项目使用,并且源代码一并打包,可以用于学习和交流。没有加密。可以支持你自己二次开发优化,任何本项目代码或者衍生代码均不能用于任何商用、转售,如果需要商用,请联系本人。 本插件供 Cocos Creator 项目使用,并且源代码一并打包,可以用于学习和交流。没有加密。可以支持你自己二次开发优化,任何本项目代码或者衍生代码均不能用于任何商用、转售,如果需要商用,请联系本人。

157
dist/main.js vendored

File diff suppressed because one or more lines are too long

52
dist/mcp-server.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

512
dist/panels/tool-manager/index.js vendored Normal file

File diff suppressed because one or more lines are too long

60
dist/settings.js vendored

File diff suppressed because one or more lines are too long

405
dist/tools/tool-manager.js vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/types/index.js vendored
View File

@@ -1,3 +1,3 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdHlwZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBpbnRlcmZhY2UgTUNQU2VydmVyU2V0dGluZ3Mge1xuICAgIHBvcnQ6IG51bWJlcjtcbiAgICBhdXRvU3RhcnQ6IGJvb2xlYW47XG4gICAgZW5hYmxlRGVidWdMb2c6IGJvb2xlYW47XG4gICAgYWxsb3dlZE9yaWdpbnM6IHN0cmluZ1tdO1xuICAgIG1heENvbm5lY3Rpb25zOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VydmVyU3RhdHVzIHtcbiAgICBydW5uaW5nOiBib29sZWFuO1xuICAgIHBvcnQ6IG51bWJlcjtcbiAgICBjbGllbnRzOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVG9vbERlZmluaXRpb24ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICAgIGlucHV0U2NoZW1hOiBhbnk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVG9vbFJlc3BvbnNlIHtcbiAgICBzdWNjZXNzOiBib29sZWFuO1xuICAgIGRhdGE/OiBhbnk7XG4gICAgbWVzc2FnZT86IHN0cmluZztcbiAgICBlcnJvcj86IHN0cmluZztcbiAgICBpbnN0cnVjdGlvbj86IHN0cmluZztcbiAgICB3YXJuaW5nPzogc3RyaW5nO1xuICAgIHZlcmlmaWNhdGlvbkRhdGE/OiBhbnk7XG4gICAgdXBkYXRlZFByb3BlcnRpZXM/OiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOb2RlSW5mbyB7XG4gICAgdXVpZDogc3RyaW5nO1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBhY3RpdmU6IGJvb2xlYW47XG4gICAgcG9zaXRpb24/OiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXIgfTtcbiAgICByb3RhdGlvbj86IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xuICAgIHNjYWxlPzogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgejogbnVtYmVyIH07XG4gICAgcGFyZW50Pzogc3RyaW5nO1xuICAgIGNoaWxkcmVuPzogc3RyaW5nW107XG4gICAgY29tcG9uZW50cz86IENvbXBvbmVudEluZm9bXTtcbiAgICBsYXllcj86IG51bWJlcjtcbiAgICBtb2JpbGl0eT86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb21wb25lbnRJbmZvIHtcbiAgICB0eXBlOiBzdHJpbmc7XG4gICAgZW5hYmxlZDogYm9vbGVhbjtcbiAgICBwcm9wZXJ0aWVzPzogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTY2VuZUluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFByZWZhYkluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIGZvbGRlcjogc3RyaW5nO1xuICAgIGNyZWF0ZVRpbWU/OiBzdHJpbmc7XG4gICAgbW9kaWZ5VGltZT86IHN0cmluZztcbiAgICBkZXBlbmRlbmNpZXM/OiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBBc3NldEluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIHR5cGU6IHN0cmluZztcbiAgICBzaXplPzogbnVtYmVyO1xuICAgIGlzRGlyZWN0b3J5OiBib29sZWFuO1xuICAgIG1ldGE/OiB7XG4gICAgICAgIHZlcjogc3RyaW5nO1xuICAgICAgICBpbXBvcnRlcjogc3RyaW5nO1xuICAgIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJvamVjdEluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBwYXRoOiBzdHJpbmc7XG4gICAgdXVpZDogc3RyaW5nO1xuICAgIHZlcnNpb246IHN0cmluZztcbiAgICBjb2Nvc1ZlcnNpb246IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb25zb2xlTWVzc2FnZSB7XG4gICAgdGltZXN0YW1wOiBzdHJpbmc7XG4gICAgdHlwZTogJ2xvZycgfCAnd2FybicgfCAnZXJyb3InIHwgJ2luZm8nO1xuICAgIG1lc3NhZ2U6IHN0cmluZztcbiAgICBzdGFjaz86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQZXJmb3JtYW5jZVN0YXRzIHtcbiAgICBub2RlQ291bnQ6IG51bWJlcjtcbiAgICBjb21wb25lbnRDb3VudDogbnVtYmVyO1xuICAgIGRyYXdDYWxsczogbnVtYmVyO1xuICAgIHRyaWFuZ2xlczogbnVtYmVyO1xuICAgIG1lbW9yeTogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBWYWxpZGF0aW9uSXNzdWUge1xuICAgIHR5cGU6ICdlcnJvcicgfCAnd2FybmluZycgfCAnaW5mbyc7XG4gICAgY2F0ZWdvcnk6IHN0cmluZztcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgZGV0YWlscz86IGFueTtcbiAgICBzdWdnZXN0aW9uPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZhbGlkYXRpb25SZXN1bHQge1xuICAgIHZhbGlkOiBib29sZWFuO1xuICAgIGlzc3VlQ291bnQ6IG51bWJlcjtcbiAgICBpc3N1ZXM6IFZhbGlkYXRpb25Jc3N1ZVtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1DUENsaWVudCB7XG4gICAgaWQ6IHN0cmluZztcbiAgICBsYXN0QWN0aXZpdHk6IERhdGU7XG4gICAgdXNlckFnZW50Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRvb2xFeGVjdXRvciB7XG4gICAgZ2V0VG9vbHMoKTogVG9vbERlZmluaXRpb25bXTtcbiAgICBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPjtcbn0iXX0= //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdHlwZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBpbnRlcmZhY2UgTUNQU2VydmVyU2V0dGluZ3Mge1xuICAgIHBvcnQ6IG51bWJlcjtcbiAgICBhdXRvU3RhcnQ6IGJvb2xlYW47XG4gICAgZW5hYmxlRGVidWdMb2c6IGJvb2xlYW47XG4gICAgYWxsb3dlZE9yaWdpbnM6IHN0cmluZ1tdO1xuICAgIG1heENvbm5lY3Rpb25zOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VydmVyU3RhdHVzIHtcbiAgICBydW5uaW5nOiBib29sZWFuO1xuICAgIHBvcnQ6IG51bWJlcjtcbiAgICBjbGllbnRzOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVG9vbERlZmluaXRpb24ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICAgIGlucHV0U2NoZW1hOiBhbnk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVG9vbFJlc3BvbnNlIHtcbiAgICBzdWNjZXNzOiBib29sZWFuO1xuICAgIGRhdGE/OiBhbnk7XG4gICAgbWVzc2FnZT86IHN0cmluZztcbiAgICBlcnJvcj86IHN0cmluZztcbiAgICBpbnN0cnVjdGlvbj86IHN0cmluZztcbiAgICB3YXJuaW5nPzogc3RyaW5nO1xuICAgIHZlcmlmaWNhdGlvbkRhdGE/OiBhbnk7XG4gICAgdXBkYXRlZFByb3BlcnRpZXM/OiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOb2RlSW5mbyB7XG4gICAgdXVpZDogc3RyaW5nO1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBhY3RpdmU6IGJvb2xlYW47XG4gICAgcG9zaXRpb24/OiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXIgfTtcbiAgICByb3RhdGlvbj86IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xuICAgIHNjYWxlPzogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgejogbnVtYmVyIH07XG4gICAgcGFyZW50Pzogc3RyaW5nO1xuICAgIGNoaWxkcmVuPzogc3RyaW5nW107XG4gICAgY29tcG9uZW50cz86IENvbXBvbmVudEluZm9bXTtcbiAgICBsYXllcj86IG51bWJlcjtcbiAgICBtb2JpbGl0eT86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb21wb25lbnRJbmZvIHtcbiAgICB0eXBlOiBzdHJpbmc7XG4gICAgZW5hYmxlZDogYm9vbGVhbjtcbiAgICBwcm9wZXJ0aWVzPzogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTY2VuZUluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFByZWZhYkluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIGZvbGRlcjogc3RyaW5nO1xuICAgIGNyZWF0ZVRpbWU/OiBzdHJpbmc7XG4gICAgbW9kaWZ5VGltZT86IHN0cmluZztcbiAgICBkZXBlbmRlbmNpZXM/OiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBBc3NldEluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIHR5cGU6IHN0cmluZztcbiAgICBzaXplPzogbnVtYmVyO1xuICAgIGlzRGlyZWN0b3J5OiBib29sZWFuO1xuICAgIG1ldGE/OiB7XG4gICAgICAgIHZlcjogc3RyaW5nO1xuICAgICAgICBpbXBvcnRlcjogc3RyaW5nO1xuICAgIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJvamVjdEluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBwYXRoOiBzdHJpbmc7XG4gICAgdXVpZDogc3RyaW5nO1xuICAgIHZlcnNpb246IHN0cmluZztcbiAgICBjb2Nvc1ZlcnNpb246IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb25zb2xlTWVzc2FnZSB7XG4gICAgdGltZXN0YW1wOiBzdHJpbmc7XG4gICAgdHlwZTogJ2xvZycgfCAnd2FybicgfCAnZXJyb3InIHwgJ2luZm8nO1xuICAgIG1lc3NhZ2U6IHN0cmluZztcbiAgICBzdGFjaz86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQZXJmb3JtYW5jZVN0YXRzIHtcbiAgICBub2RlQ291bnQ6IG51bWJlcjtcbiAgICBjb21wb25lbnRDb3VudDogbnVtYmVyO1xuICAgIGRyYXdDYWxsczogbnVtYmVyO1xuICAgIHRyaWFuZ2xlczogbnVtYmVyO1xuICAgIG1lbW9yeTogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBWYWxpZGF0aW9uSXNzdWUge1xuICAgIHR5cGU6ICdlcnJvcicgfCAnd2FybmluZycgfCAnaW5mbyc7XG4gICAgY2F0ZWdvcnk6IHN0cmluZztcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgZGV0YWlscz86IGFueTtcbiAgICBzdWdnZXN0aW9uPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZhbGlkYXRpb25SZXN1bHQge1xuICAgIHZhbGlkOiBib29sZWFuO1xuICAgIGlzc3VlQ291bnQ6IG51bWJlcjtcbiAgICBpc3N1ZXM6IFZhbGlkYXRpb25Jc3N1ZVtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1DUENsaWVudCB7XG4gICAgaWQ6IHN0cmluZztcbiAgICBsYXN0QWN0aXZpdHk6IERhdGU7XG4gICAgdXNlckFnZW50Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRvb2xFeGVjdXRvciB7XG4gICAgZ2V0VG9vbHMoKTogVG9vbERlZmluaXRpb25bXTtcbiAgICBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPjtcbn1cblxuLy8g5bel5YW36YWN572u566h55CG55u45YWz5o6l5Y+jXG5leHBvcnQgaW50ZXJmYWNlIFRvb2xDb25maWcge1xuICAgIGNhdGVnb3J5OiBzdHJpbmc7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIGVuYWJsZWQ6IGJvb2xlYW47XG4gICAgZGVzY3JpcHRpb246IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUb29sQ29uZmlndXJhdGlvbiB7XG4gICAgaWQ6IHN0cmluZztcbiAgICBuYW1lOiBzdHJpbmc7XG4gICAgZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gICAgdG9vbHM6IFRvb2xDb25maWdbXTtcbiAgICBjcmVhdGVkQXQ6IHN0cmluZztcbiAgICB1cGRhdGVkQXQ6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUb29sTWFuYWdlclNldHRpbmdzIHtcbiAgICBjb25maWd1cmF0aW9uczogVG9vbENvbmZpZ3VyYXRpb25bXTtcbiAgICBjdXJyZW50Q29uZmlnSWQ6IHN0cmluZztcbiAgICBtYXhDb25maWdTbG90czogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRvb2xNYW5hZ2VyU3RhdGUge1xuICAgIGF2YWlsYWJsZVRvb2xzOiBUb29sQ29uZmlnW107XG4gICAgY3VycmVudENvbmZpZ3VyYXRpb246IFRvb2xDb25maWd1cmF0aW9uIHwgbnVsbDtcbiAgICBjb25maWd1cmF0aW9uczogVG9vbENvbmZpZ3VyYXRpb25bXTtcbn0iXX0=

View File

@@ -27,5 +27,53 @@ module.exports = {
"failed_to_start": "Failed to start server", "failed_to_start": "Failed to start server",
"failed_to_stop": "Failed to stop server", "failed_to_stop": "Failed to stop server",
"failed_to_save": "Failed to save settings", "failed_to_save": "Failed to save settings",
"url_copied": "HTTP URL copied to clipboard" "url_copied": "HTTP URL copied to clipboard",
"tool_manager": "Tool Manager",
"open_tool_manager": "Open Tool Manager",
"create_config": "Create Configuration",
"edit_config": "Edit Configuration",
"delete_config": "Delete Configuration",
"import_config": "Import Configuration",
"export_config": "Export Configuration",
"apply_config": "Apply Configuration",
"select_all": "Select All",
"deselect_all": "Deselect All",
"save_changes": "Save Changes",
"config_name": "Configuration Name",
"config_description": "Configuration Description",
"current_config": "Current Configuration",
"tool_management": "Tool Management",
"total_tools": "Total Tools",
"enabled_tools": "Enabled",
"disabled_tools": "Disabled",
"no_config_selected": "No Configuration Selected",
"select_config_first": "Please select a configuration or create a new one first",
"config_created": "Configuration created successfully",
"config_updated": "Configuration updated successfully",
"config_deleted": "Configuration deleted successfully",
"config_applied": "Configuration applied successfully",
"config_exported": "Configuration exported successfully",
"config_imported": "Configuration imported successfully",
"confirm_delete": "Confirm Delete",
"delete_config_confirm": "Are you sure you want to delete configuration \"{0}\"? This action cannot be undone.",
"max_config_slots_reached": "Maximum configuration slots reached ({0})",
"invalid_config_format": "Invalid configuration format",
"invalid_json_format": "Invalid JSON format or configuration structure",
"server_tab": "Server",
"tools_tab": "Tool Management",
"available_tools": "Available Tools",
"scene_tools": "Scene Tools",
"node_tools": "Node Tools",
"component_tools": "Component Tools",
"prefab_tools": "Prefab Tools",
"project_tools": "Project Tools",
"debug_tools": "Debug Tools",
"preferences_tools": "Preferences",
"server_tools": "Server Tools",
"broadcast_tools": "Broadcast Tools",
"scene_advanced_tools": "Advanced Scene Tools",
"scene_view_tools": "Scene View Tools",
"reference_image_tools": "Reference Image Tools",
"asset_advanced_tools": "Advanced Asset Tools",
"validation_tools": "Validation Tools"
}; };

View File

@@ -27,5 +27,53 @@ module.exports = {
"failed_to_start": "启动服务器失败", "failed_to_start": "启动服务器失败",
"failed_to_stop": "停止服务器失败", "failed_to_stop": "停止服务器失败",
"failed_to_save": "保存设置失败", "failed_to_save": "保存设置失败",
"url_copied": "HTTP 地址已复制到剪贴板" "url_copied": "HTTP 地址已复制到剪贴板",
"tool_manager": "工具管理器",
"open_tool_manager": "打开工具管理器",
"create_config": "新建配置",
"edit_config": "编辑配置",
"delete_config": "删除配置",
"import_config": "导入配置",
"export_config": "导出配置",
"apply_config": "应用配置",
"select_all": "全选",
"deselect_all": "取消全选",
"save_changes": "保存更改",
"config_name": "配置名称",
"config_description": "配置描述",
"current_config": "当前配置",
"tool_management": "工具管理",
"total_tools": "总工具数",
"enabled_tools": "已启用",
"disabled_tools": "已禁用",
"no_config_selected": "没有选择配置",
"select_config_first": "请先选择一个配置或创建新配置",
"config_created": "配置创建成功",
"config_updated": "配置更新成功",
"config_deleted": "配置删除成功",
"config_applied": "配置应用成功",
"config_exported": "配置导出成功",
"config_imported": "配置导入成功",
"confirm_delete": "确认删除",
"delete_config_confirm": "确定要删除配置 \"{0}\" 吗?此操作不可撤销。",
"max_config_slots_reached": "已达到最大配置槽位数量 ({0})",
"invalid_config_format": "无效的配置格式",
"invalid_json_format": "无效的JSON格式或配置结构",
"server_tab": "服务器",
"tools_tab": "工具管理",
"available_tools": "可用工具",
"scene_tools": "场景工具",
"node_tools": "节点工具",
"component_tools": "组件工具",
"prefab_tools": "预制体工具",
"project_tools": "项目工具",
"debug_tools": "调试工具",
"preferences_tools": "偏好设置",
"server_tools": "服务器工具",
"broadcast_tools": "广播工具",
"scene_advanced_tools": "高级场景工具",
"scene_view_tools": "场景视图工具",
"reference_image_tools": "参考图片工具",
"asset_advanced_tools": "高级资源工具",
"validation_tools": "验证工具"
}; };

277
package-lock.json generated
View File

@@ -11,17 +11,62 @@
"dependencies": { "dependencies": {
"fs-extra": "^11.3.0", "fs-extra": "^11.3.0",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"ws": "^8.14.2" "vue": "^3.1.4"
}, },
"devDependencies": { "devDependencies": {
"@cocos/creator-types": "^3.8.6", "@cocos/creator-types": "^3.8.6",
"@types/fs-extra": "^9.0.5", "@types/fs-extra": "^9.0.5",
"@types/node": "^18.17.1", "@types/node": "^18.17.1",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@types/ws": "^8.5.10",
"typescript": "^5.8.2" "typescript": "^5.8.2"
} }
}, },
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.0"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@cocos/creator-types": { "node_modules/@cocos/creator-types": {
"version": "3.8.6", "version": "3.8.6",
"resolved": "https://registry.npmjs.org/@cocos/creator-types/-/creator-types-3.8.6.tgz", "resolved": "https://registry.npmjs.org/@cocos/creator-types/-/creator-types-3.8.6.tgz",
@@ -29,6 +74,12 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
"license": "MIT"
},
"node_modules/@types/fs-extra": { "node_modules/@types/fs-extra": {
"version": "9.0.13", "version": "9.0.13",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
@@ -56,16 +107,130 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/ws": { "node_modules/@vue/compiler-core": {
"version": "8.18.1", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/node": "*" "@babel/parser": "^7.28.0",
"@vue/shared": "3.5.18",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-dom": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
"integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.18",
"@vue/shared": "3.5.18"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
"integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.0",
"@vue/compiler-core": "3.5.18",
"@vue/compiler-dom": "3.5.18",
"@vue/compiler-ssr": "3.5.18",
"@vue/shared": "3.5.18",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.17",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
"integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.18",
"@vue/shared": "3.5.18"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz",
"integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==",
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.18"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz",
"integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.18",
"@vue/shared": "3.5.18"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz",
"integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.18",
"@vue/runtime-core": "3.5.18",
"@vue/shared": "3.5.18",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz",
"integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==",
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.18",
"@vue/shared": "3.5.18"
},
"peerDependencies": {
"vue": "3.5.18"
}
},
"node_modules/@vue/shared": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz",
"integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
"license": "MIT"
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/fs-extra": { "node_modules/fs-extra": {
"version": "11.3.0", "version": "11.3.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
@@ -98,11 +263,81 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"node_modules/magic-string": {
"version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.8.3", "version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
@@ -141,23 +376,23 @@
"uuid": "dist/bin/uuid" "uuid": "dist/bin/uuid"
} }
}, },
"node_modules/ws": { "node_modules/vue": {
"version": "8.18.3", "version": "3.5.18",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==",
"license": "MIT", "license": "MIT",
"engines": { "dependencies": {
"node": ">=10.0.0" "@vue/compiler-dom": "3.5.18",
"@vue/compiler-sfc": "3.5.18",
"@vue/runtime-dom": "3.5.18",
"@vue/server-renderer": "3.5.18",
"@vue/shared": "3.5.18"
}, },
"peerDependencies": { "peerDependencies": {
"bufferutil": "^4.0.1", "typescript": "*"
"utf-8-validate": ">=5.0.2"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"bufferutil": { "typescript": {
"optional": true
},
"utf-8-validate": {
"optional": true "optional": true
} }
} }

View File

@@ -14,7 +14,8 @@
"main": "./dist/main.js", "main": "./dist/main.js",
"dependencies": { "dependencies": {
"fs-extra": "^11.3.0", "fs-extra": "^11.3.0",
"uuid": "^9.0.1" "uuid": "^9.0.1",
"vue": "^3.1.4"
}, },
"devDependencies": { "devDependencies": {
"@cocos/creator-types": "^3.8.6", "@cocos/creator-types": "^3.8.6",
@@ -38,7 +39,7 @@
} }
}, },
"contributions": { "contributions": {
"menu": [ "menu": [
{ {
"path": "i18n:menu.extension/Cocos MCP Server", "path": "i18n:menu.extension/Cocos MCP Server",
"label": "i18n:cocos-mcp-server.open_panel", "label": "i18n:cocos-mcp-server.open_panel",
@@ -51,6 +52,11 @@
"openPanel" "openPanel"
] ]
}, },
"open-tool-manager": {
"methods": [
"openToolManager"
]
},
"start-server": { "start-server": {
"methods": [ "methods": [
"startServer" "startServer"
@@ -80,6 +86,56 @@
"methods": [ "methods": [
"getServerSettings" "getServerSettings"
] ]
},
"getToolManagerState": {
"methods": [
"getToolManagerState"
]
},
"createToolConfiguration": {
"methods": [
"createToolConfiguration"
]
},
"updateToolConfiguration": {
"methods": [
"updateToolConfiguration"
]
},
"deleteToolConfiguration": {
"methods": [
"deleteToolConfiguration"
]
},
"setCurrentToolConfiguration": {
"methods": [
"setCurrentToolConfiguration"
]
},
"updateToolStatus": {
"methods": [
"updateToolStatus"
]
},
"updateToolStatusBatch": {
"methods": [
"updateToolStatusBatch"
]
},
"exportToolConfiguration": {
"methods": [
"exportToolConfiguration"
]
},
"importToolConfiguration": {
"methods": [
"importToolConfiguration"
]
},
"getEnabledTools": {
"methods": [
"getEnabledTools"
]
} }
}, },
"scene": { "scene": {

View File

@@ -1,8 +1,10 @@
import { MCPServer } from './mcp-server'; import { MCPServer } from './mcp-server';
import { readSettings, saveSettings } from './settings'; import { readSettings, saveSettings } from './settings';
import { MCPServerSettings } from './types'; import { MCPServerSettings } from './types';
import { ToolManager } from './tools/tool-manager';
let mcpServer: MCPServer | null = null; let mcpServer: MCPServer | null = null;
let toolManager: ToolManager;
/** /**
* @en Registration method for the main process of Extension * @en Registration method for the main process of Extension
@@ -17,12 +19,17 @@ export const methods: { [key: string]: (...any: any) => any } = {
Editor.Panel.open('cocos-mcp-server'); Editor.Panel.open('cocos-mcp-server');
}, },
/** /**
* @en Start the MCP server * @en Start the MCP server
* @zh 启动 MCP 服务器 * @zh 启动 MCP 服务器
*/ */
async startServer() { async startServer() {
if (mcpServer) { if (mcpServer) {
// 确保使用最新的工具配置
const enabledTools = toolManager.getEnabledTools();
mcpServer.updateEnabledTools(enabledTools);
await mcpServer.start(); await mcpServer.start();
} else { } else {
console.warn('[MCP插件] mcpServer 未初始化'); console.warn('[MCP插件] mcpServer 未初始化');
@@ -46,7 +53,12 @@ export const methods: { [key: string]: (...any: any) => any } = {
* @zh 获取服务器状态 * @zh 获取服务器状态
*/ */
getServerStatus() { getServerStatus() {
return mcpServer ? mcpServer.getStatus() : { running: false, port: 0, clients: 0 }; const status = mcpServer ? mcpServer.getStatus() : { running: false, port: 0, clients: 0 };
const settings = mcpServer ? mcpServer.getSettings() : readSettings();
return {
...status,
settings: settings
};
}, },
/** /**
@@ -72,12 +84,136 @@ export const methods: { [key: string]: (...any: any) => any } = {
getToolsList() { getToolsList() {
return mcpServer ? mcpServer.getAvailableTools() : []; return mcpServer ? mcpServer.getAvailableTools() : [];
}, },
getFilteredToolsList() {
if (!mcpServer) return [];
// 获取当前启用的工具
const enabledTools = toolManager.getEnabledTools();
// 更新MCP服务器的启用工具列表
mcpServer.updateEnabledTools(enabledTools);
return mcpServer.getFilteredTools(enabledTools);
},
/** /**
* @en Get server settings * @en Get server settings
* @zh 获取服务器设置 * @zh 获取服务器设置
*/ */
getServerSettings() { async getServerSettings() {
return mcpServer ? mcpServer.getSettings() : readSettings(); return mcpServer ? mcpServer.getSettings() : readSettings();
},
/**
* @en Get server settings (alternative method)
* @zh 获取服务器设置(替代方法)
*/
async getSettings() {
return mcpServer ? mcpServer.getSettings() : readSettings();
},
// 工具管理器相关方法
async getToolManagerState() {
return toolManager.getToolManagerState();
},
async createToolConfiguration(name: string, description?: string) {
try {
const config = toolManager.createConfiguration(name, description);
return { success: true, id: config.id, config };
} catch (error: any) {
throw new Error(`创建配置失败: ${error.message}`);
}
},
async updateToolConfiguration(configId: string, updates: any) {
try {
return toolManager.updateConfiguration(configId, updates);
} catch (error: any) {
throw new Error(`更新配置失败: ${error.message}`);
}
},
async deleteToolConfiguration(configId: string) {
try {
toolManager.deleteConfiguration(configId);
return { success: true };
} catch (error: any) {
throw new Error(`删除配置失败: ${error.message}`);
}
},
async setCurrentToolConfiguration(configId: string) {
try {
toolManager.setCurrentConfiguration(configId);
return { success: true };
} catch (error: any) {
throw new Error(`设置当前配置失败: ${error.message}`);
}
},
async updateToolStatus(category: string, toolName: string, enabled: boolean) {
try {
const currentConfig = toolManager.getCurrentConfiguration();
if (!currentConfig) {
throw new Error('没有当前配置');
}
toolManager.updateToolStatus(currentConfig.id, category, toolName, enabled);
// 更新MCP服务器的工具列表
if (mcpServer) {
const enabledTools = toolManager.getEnabledTools();
mcpServer.updateEnabledTools(enabledTools);
}
return { success: true };
} catch (error: any) {
throw new Error(`更新工具状态失败: ${error.message}`);
}
},
async updateToolStatusBatch(updates: any[]) {
try {
console.log(`[Main] updateToolStatusBatch called with updates count:`, updates ? updates.length : 0);
const currentConfig = toolManager.getCurrentConfiguration();
if (!currentConfig) {
throw new Error('没有当前配置');
}
toolManager.updateToolStatusBatch(currentConfig.id, updates);
// 更新MCP服务器的工具列表
if (mcpServer) {
const enabledTools = toolManager.getEnabledTools();
mcpServer.updateEnabledTools(enabledTools);
}
return { success: true };
} catch (error: any) {
throw new Error(`批量更新工具状态失败: ${error.message}`);
}
},
async exportToolConfiguration(configId: string) {
try {
return { configJson: toolManager.exportConfiguration(configId) };
} catch (error: any) {
throw new Error(`导出配置失败: ${error.message}`);
}
},
async importToolConfiguration(configJson: string) {
try {
return toolManager.importConfiguration(configJson);
} catch (error: any) {
throw new Error(`导入配置失败: ${error.message}`);
}
},
async getEnabledTools() {
return toolManager.getEnabledTools();
} }
}; };
@@ -86,24 +222,24 @@ export const methods: { [key: string]: (...any: any) => any } = {
* @zh 扩展启动时触发的方法 * @zh 扩展启动时触发的方法
*/ */
export function load() { export function load() {
console.log('[MCP Plugin] Loading MCP server plugin...'); console.log('Cocos MCP Server extension loaded');
try {
const settings = readSettings(); // 初始化工具管理器
console.log('[MCP Plugin] Settings loaded:', settings); toolManager = new ToolManager();
mcpServer = new MCPServer(settings);
// 读取设置
// 如果设置了自动启动,则启动服务器 const settings = readSettings();
if (settings.autoStart) { mcpServer = new MCPServer(settings);
console.log('[MCP Plugin] Auto-starting MCP server...');
mcpServer.start().catch(error => { // 初始化MCP服务器的工具列表
console.error('[MCP Plugin] Failed to auto-start server:', error); const enabledTools = toolManager.getEnabledTools();
}); mcpServer.updateEnabledTools(enabledTools);
} else {
console.log('[MCP Plugin] MCP server created but not started (autoStart=false)'); // 如果设置了自动启动,则启动服务器
console.log('[MCP Plugin] Use the MCP panel or call startServer() to start the server'); if (settings.autoStart) {
} mcpServer.start().catch(err => {
} catch (error) { console.error('Failed to auto-start MCP server:', err);
console.error('[MCP Plugin] Failed to load MCP server:', error); });
} }
} }

View File

@@ -23,6 +23,7 @@ export class MCPServer {
private clients: Map<string, MCPClient> = new Map(); private clients: Map<string, MCPClient> = new Map();
private tools: Record<string, any> = {}; private tools: Record<string, any> = {};
private toolsList: ToolDefinition[] = []; private toolsList: ToolDefinition[] = [];
private enabledTools: any[] = []; // 存储启用的工具列表
constructor(settings: MCPServerSettings) { constructor(settings: MCPServerSettings) {
this.settings = settings; this.settings = settings;
@@ -90,17 +91,47 @@ export class MCPServer {
private setupTools(): void { private setupTools(): void {
this.toolsList = []; this.toolsList = [];
for (const [category, toolSet] of Object.entries(this.tools)) { // 如果没有启用工具配置,返回所有工具
const tools = toolSet.getTools(); if (!this.enabledTools || this.enabledTools.length === 0) {
for (const tool of tools) { for (const [category, toolSet] of Object.entries(this.tools)) {
this.toolsList.push({ const tools = toolSet.getTools();
name: `${category}_${tool.name}`, for (const tool of tools) {
description: tool.description, this.toolsList.push({
inputSchema: tool.inputSchema name: `${category}_${tool.name}`,
}); description: tool.description,
inputSchema: tool.inputSchema
});
}
}
} else {
// 根据启用的工具配置过滤
const enabledToolNames = new Set(this.enabledTools.map(tool => `${tool.category}_${tool.name}`));
for (const [category, toolSet] of Object.entries(this.tools)) {
const tools = toolSet.getTools();
for (const tool of tools) {
const toolName = `${category}_${tool.name}`;
if (enabledToolNames.has(toolName)) {
this.toolsList.push({
name: toolName,
description: tool.description,
inputSchema: tool.inputSchema
});
}
}
} }
} }
console.log(`[MCPServer] Setup tools: ${this.toolsList.length} tools available`);
}
public getFilteredTools(enabledTools: any[]): ToolDefinition[] {
if (!enabledTools || enabledTools.length === 0) {
return this.toolsList; // 如果没有过滤配置,返回所有工具
}
const enabledToolNames = new Set(enabledTools.map(tool => `${tool.category}_${tool.name}`));
return this.toolsList.filter(tool => enabledToolNames.has(tool.name));
} }
public async executeToolCall(toolName: string, args: any): Promise<any> { public async executeToolCall(toolName: string, args: any): Promise<any> {
@@ -122,6 +153,12 @@ export class MCPServer {
return this.toolsList; return this.toolsList;
} }
public updateEnabledTools(enabledTools: any[]): void {
console.log(`[MCPServer] Updating enabled tools: ${enabledTools.length} tools`);
this.enabledTools = enabledTools;
this.setupTools(); // 重新设置工具列表
}
public getSettings(): MCPServerSettings { public getSettings(): MCPServerSettings {
return this.settings; return this.settings;
} }

View File

@@ -1,229 +1,386 @@
import { readSettings } from '../../settings'; /* eslint-disable vue/one-component-per-file */
import { readFileSync } from 'fs-extra'; import { readFileSync } from 'fs-extra';
import { join } from 'path'; import { join } from 'path';
import { createApp, App, defineComponent, ref, computed, onMounted, watch, nextTick } from 'vue';
/** const panelDataMap = new WeakMap<any, App>();
* @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码
* @en You can add the code below if you want compatibility with versions prior to 3.3 // 定义工具配置接口
*/ interface ToolConfig {
// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options } category: string;
name: string;
enabled: boolean;
description: string;
}
// 定义配置接口
interface Configuration {
id: string;
name: string;
description: string;
tools: ToolConfig[];
createdAt: string;
updatedAt: string;
}
// 定义服务器设置接口
interface ServerSettings {
port: number;
autoStart: boolean;
debugLog: boolean;
maxConnections: number;
}
module.exports = Editor.Panel.define({ module.exports = Editor.Panel.define({
listeners: { listeners: {
show() { console.log('MCP Server panel shown'); }, show() {
hide() { console.log('MCP Server panel hidden'); } console.log('[MCP Panel] Panel shown');
},
hide() {
console.log('[MCP Panel] Panel hidden');
},
}, },
template: readFileSync(join(__dirname, '../../../static/template/default/index.html'), 'utf-8'), template: readFileSync(join(__dirname, '../../../static/template/default/index.html'), 'utf-8'),
style: readFileSync(join(__dirname, '../../../static/style/default/index.css'), 'utf-8'), style: readFileSync(join(__dirname, '../../../static/style/default/index.css'), 'utf-8'),
$: { $: {
app: '#app',
panelTitle: '#panelTitle', panelTitle: '#panelTitle',
serverStatusLabel: '#serverStatusLabel',
serverStatusLabelProp: '#serverStatusLabelProp',
serverStatusValue: '#serverStatusValue',
connectedLabel: '#connectedLabel',
connectedClients: '#connectedClients',
toggleServerBtn: '#toggleServerBtn',
settingsLabel: '#settingsLabel',
portLabel: '#portLabel',
autoStartLabel: '#autoStartLabel',
debugLogLabel: '#debugLogLabel',
maxConnectionsLabel: '#maxConnectionsLabel',
connectionInfoLabel: '#connectionInfoLabel',
httpUrlLabel: '#httpUrlLabel',
httpUrlInput: '#httpUrlInput',
copyBtn: '#copyBtn',
saveSettingsBtn: '#saveSettingsBtn',
// 新增输入控件id
portInput: '#portInput',
maxConnInput: '#maxConnInput',
autoStartInput: '#autoStartInput',
debugLogInput: '#debugLogInput',
},
methods: {
async updateServerStatus(this: any) {
try {
const status = await Editor.Message.request('cocos-mcp-server', 'get-server-status');
this.serverRunning = status.running;
this.connectedClients = status.clients || 0;
this.serverStatus = this.serverRunning ?
Editor.I18n.t('cocos-mcp-server.connected') :
Editor.I18n.t('cocos-mcp-server.disconnected');
this.statusClass = this.serverRunning ? 'running' : 'stopped';
this.buttonText = this.serverRunning ?
Editor.I18n.t('cocos-mcp-server.stop_server') :
Editor.I18n.t('cocos-mcp-server.start_server');
// 刷新UI
this.$.serverStatusValue.innerText = this.serverStatus;
this.$.connectedClients.innerText = this.connectedClients;
this.$.toggleServerBtn.innerText = this.buttonText;
if (this.serverRunning) {
this.httpUrl = `http://localhost:${this.settings.port}/mcp`;
this.$.httpUrlInput.value = this.httpUrl;
} else {
this.httpUrl = '';
this.$.httpUrlInput.value = '';
}
} catch (err) {
console.error('Failed to update server status:', err);
}
},
async toggleServer(this: any) {
this.isProcessing = true;
try {
if (this.serverRunning) {
await this.stopServer();
} else {
await this.startServer();
}
} finally {
this.isProcessing = false;
}
},
async startServer(this: any) {
try {
await Editor.Message.request('cocos-mcp-server', 'start-server');
Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.server_started'), {
detail: Editor.I18n.t('cocos-mcp-server.server_running').replace('{0}', this.settings.port.toString())
});
await this.updateServerStatus();
} catch (err: any) {
Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_start'), err.message);
}
},
async stopServer(this: any) {
try {
await Editor.Message.request('cocos-mcp-server', 'stop-server');
Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.server_stopped_msg'), {
detail: Editor.I18n.t('cocos-mcp-server.server_stopped')
});
await this.updateServerStatus();
} catch (err: any) {
Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_stop'), err.message);
}
},
async saveSettings(this: any) {
try {
// 直接用 this.$ 获取所有输入控件的当前值
const port = this.$.portInput ? Number((this.$.portInput as any).value) : 3000;
const maxConnections = this.$.maxConnInput ? Number((this.$.maxConnInput as any).value) : 10;
const autoStart = this.$.autoStartInput ? !!(this.$.autoStartInput as any).checked : false;
const enableDebugLog = this.$.debugLogInput ? !!(this.$.debugLogInput as any).checked : false;
// 组装 settings
const settings = {
...this.settings,
port,
maxConnections,
autoStart,
enableDebugLog,
};
await Editor.Message.request('cocos-mcp-server', 'update-settings', settings);
// 重新拉取设置
const newSettings = await Editor.Message.request('cocos-mcp-server', 'get-server-settings');
this.settings = newSettings;
this.originalSettings = JSON.stringify(newSettings);
Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.settings_saved'));
} catch (err: any) {
Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_save'), err.message);
}
},
copyUrl(this: any) {
Editor.Clipboard.write('text', this.httpUrl);
Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.url_copied'));
},
settingsChanged(this: any) {
return JSON.stringify(this.settings) !== this.originalSettings;
},
bindSettingsEvents(this: any) {
// 端口输入框
const portInput = document.querySelectorAll('ui-num-input[slot="content"]')[0];
if (portInput) {
portInput.addEventListener('change', (e: any) => {
this.settings.port = Number(e.detail.value);
});
}
// 最大连接数
const maxConnInput = document.querySelectorAll('ui-num-input[slot="content"]')[1];
if (maxConnInput) {
maxConnInput.addEventListener('change', (e: any) => {
this.settings.maxConnections = Number(e.detail.value);
});
}
// 复选框
const checkboxes = document.querySelectorAll('ui-checkbox[slot="content"]');
if (checkboxes && checkboxes.length >= 2) {
checkboxes[0].addEventListener('change', (e: any) => {
this.settings.autoStart = !!e.detail.value;
});
checkboxes[1].addEventListener('change', (e: any) => {
this.settings.enableDebugLog = !!e.detail.value;
});
}
},
}, },
ready() { ready() {
Editor.Message.request('cocos-mcp-server', 'get-server-settings').then((settings) => { if (this.$.app) {
this.settings = settings; const app = createApp({});
this.originalSettings = JSON.stringify(settings); app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-');
// 本地化label赋值
this.$.panelTitle.innerText = Editor.I18n.t('cocos-mcp-server.panel_title'); // 创建主应用组件
this.$.serverStatusLabel.innerText = Editor.I18n.t('cocos-mcp-server.server_status'); app.component('McpServerApp', defineComponent({
this.$.serverStatusLabelProp.innerText = Editor.I18n.t('cocos-mcp-server.server_status'); setup() {
this.$.connectedLabel.innerText = Editor.I18n.t('cocos-mcp-server.connected'); // 响应式数据
this.$.settingsLabel.innerText = Editor.I18n.t('cocos-mcp-server.settings'); const activeTab = ref('server');
this.$.portLabel.innerText = Editor.I18n.t('cocos-mcp-server.port'); const serverRunning = ref(false);
this.$.autoStartLabel.innerText = Editor.I18n.t('cocos-mcp-server.auto_start'); const serverStatus = ref('已停止');
this.$.debugLogLabel.innerText = Editor.I18n.t('cocos-mcp-server.debug_log'); const connectedClients = ref(0);
this.$.maxConnectionsLabel.innerText = Editor.I18n.t('cocos-mcp-server.max_connections'); const httpUrl = ref('');
this.$.connectionInfoLabel.innerText = Editor.I18n.t('cocos-mcp-server.connection_info'); const isProcessing = ref(false);
this.$.httpUrlLabel.innerText = Editor.I18n.t('cocos-mcp-server.http_url');
this.$.copyBtn.innerText = Editor.I18n.t('cocos-mcp-server.copy'); const settings = ref<ServerSettings>({
this.$.saveSettingsBtn.innerText = Editor.I18n.t('cocos-mcp-server.save_settings'); port: 3000,
// 动态内容初始化 autoStart: false,
this.$.serverStatusValue.innerText = ''; debugLog: false,
this.$.connectedClients.innerText = ''; maxConnections: 10
this.$.toggleServerBtn.innerText = ''; });
this.$.httpUrlInput.value = '';
// 绑定按钮事件 const availableTools = ref<ToolConfig[]>([]);
this.$.toggleServerBtn.addEventListener('confirm', this.toggleServer.bind(this)); const toolCategories = ref<string[]>([]);
this.$.saveSettingsBtn.addEventListener('confirm', this.saveSettings.bind(this));
this.$.copyBtn.addEventListener('confirm', this.copyUrl.bind(this));
// 延迟绑定事件,确保 UI 组件已渲染
setTimeout(() => { // 计算属性
this.bindSettingsEvents(); const statusClass = computed(() => ({
}, 100); 'status-running': serverRunning.value,
// Set up periodic status updates 'status-stopped': !serverRunning.value
(this as any).statusInterval = setInterval(() => { }));
(this as any).updateServerStatus();
}, 2000); const totalTools = computed(() => availableTools.value.length);
// 不再自动启动服务器,用户点击才启动 const enabledTools = computed(() => availableTools.value.filter(t => t.enabled).length);
(this as any).updateServerStatus(); const disabledTools = computed(() => totalTools.value - enabledTools.value);
});
},
beforeClose() {
if ((this as any).statusInterval) { const settingsChanged = ref(false);
clearInterval((this as any).statusInterval);
// 方法
const switchTab = (tabName: string) => {
activeTab.value = tabName;
if (tabName === 'tools') {
loadToolManagerState();
}
};
const toggleServer = async () => {
try {
if (serverRunning.value) {
await Editor.Message.request('cocos-mcp-server', 'stop-server');
} else {
// 启动服务器时使用当前面板设置
const currentSettings = {
port: settings.value.port,
autoStart: settings.value.autoStart,
enableDebugLog: settings.value.debugLog,
maxConnections: settings.value.maxConnections
};
await Editor.Message.request('cocos-mcp-server', 'update-settings', currentSettings);
await Editor.Message.request('cocos-mcp-server', 'start-server');
}
console.log('[Vue App] Server toggled');
} catch (error) {
console.error('[Vue App] Failed to toggle server:', error);
}
};
const saveSettings = async () => {
try {
// 创建一个简单的对象,避免克隆错误
const settingsData = {
port: settings.value.port,
autoStart: settings.value.autoStart,
debugLog: settings.value.debugLog,
maxConnections: settings.value.maxConnections
};
const result = await Editor.Message.request('cocos-mcp-server', 'update-settings', settingsData);
console.log('[Vue App] Save settings result:', result);
settingsChanged.value = false;
} catch (error) {
console.error('[Vue App] Failed to save settings:', error);
}
};
const copyUrl = async () => {
try {
await navigator.clipboard.writeText(httpUrl.value);
console.log('[Vue App] URL copied to clipboard');
} catch (error) {
console.error('[Vue App] Failed to copy URL:', error);
}
};
const loadToolManagerState = async () => {
try {
const result = await Editor.Message.request('cocos-mcp-server', 'getToolManagerState');
if (result && result.success) {
// 总是加载后端状态,确保数据是最新的
availableTools.value = result.availableTools || [];
console.log('[Vue App] Loaded tools:', availableTools.value.length);
// 更新工具分类
const categories = new Set(availableTools.value.map(tool => tool.category));
toolCategories.value = Array.from(categories);
}
} catch (error) {
console.error('[Vue App] Failed to load tool manager state:', error);
}
};
const updateToolStatus = async (category: string, name: string, enabled: boolean) => {
try {
console.log('[Vue App] updateToolStatus called:', category, name, enabled);
// 先更新本地状态
const toolIndex = availableTools.value.findIndex(t => t.category === category && t.name === name);
if (toolIndex !== -1) {
availableTools.value[toolIndex].enabled = enabled;
// 强制触发响应式更新
availableTools.value = [...availableTools.value];
console.log('[Vue App] Local state updated, tool enabled:', availableTools.value[toolIndex].enabled);
}
// 调用后端更新
const result = await Editor.Message.request('cocos-mcp-server', 'updateToolStatus', category, name, enabled);
if (!result || !result.success) {
// 如果后端更新失败,回滚本地状态
if (toolIndex !== -1) {
availableTools.value[toolIndex].enabled = !enabled;
availableTools.value = [...availableTools.value];
}
console.error('[Vue App] Backend update failed, rolled back local state');
} else {
console.log('[Vue App] Backend update successful');
}
} catch (error) {
// 如果发生错误,回滚本地状态
const toolIndex = availableTools.value.findIndex(t => t.category === category && t.name === name);
if (toolIndex !== -1) {
availableTools.value[toolIndex].enabled = !enabled;
availableTools.value = [...availableTools.value];
}
console.error('[Vue App] Failed to update tool status:', error);
}
};
const selectAllTools = async () => {
try {
// 直接更新本地状态,然后保存
availableTools.value.forEach(tool => tool.enabled = true);
await saveChanges();
} catch (error) {
console.error('[Vue App] Failed to select all tools:', error);
}
};
const deselectAllTools = async () => {
try {
// 直接更新本地状态,然后保存
availableTools.value.forEach(tool => tool.enabled = false);
await saveChanges();
} catch (error) {
console.error('[Vue App] Failed to deselect all tools:', error);
}
};
const saveChanges = async () => {
try {
// 创建普通对象避免Vue3响应式对象克隆错误
const updates = availableTools.value.map(tool => ({
category: String(tool.category),
name: String(tool.name),
enabled: Boolean(tool.enabled)
}));
console.log('[Vue App] Sending updates:', updates.length, 'tools');
const result = await Editor.Message.request('cocos-mcp-server', 'updateToolStatusBatch', updates);
if (result && result.success) {
console.log('[Vue App] Tool changes saved successfully');
}
} catch (error) {
console.error('[Vue App] Failed to save tool changes:', error);
}
};
const toggleCategoryTools = async (category: string, enabled: boolean) => {
try {
// 直接更新本地状态,然后保存
availableTools.value.forEach(tool => {
if (tool.category === category) {
tool.enabled = enabled;
}
});
await saveChanges();
} catch (error) {
console.error('[Vue App] Failed to toggle category tools:', error);
}
};
const getToolsByCategory = (category: string) => {
return availableTools.value.filter(tool => tool.category === category);
};
const getCategoryDisplayName = (category: string): string => {
const categoryNames: { [key: string]: string } = {
'scene': '场景工具',
'node': '节点工具',
'component': '组件工具',
'prefab': '预制体工具',
'project': '项目工具',
'debug': '调试工具',
'preferences': '偏好设置工具',
'server': '服务器工具',
'broadcast': '广播工具',
'sceneAdvanced': '高级场景工具',
'sceneView': '场景视图工具',
'referenceImage': '参考图片工具',
'assetAdvanced': '高级资源工具',
'validation': '验证工具'
};
return categoryNames[category] || category;
};
// 监听设置变化
watch(settings, () => {
settingsChanged.value = true;
}, { deep: true });
// 组件挂载时加载数据
onMounted(async () => {
// 加载工具管理器状态
await loadToolManagerState();
// 从服务器状态获取设置信息
try {
const serverStatus = await Editor.Message.request('cocos-mcp-server', 'get-server-status');
if (serverStatus && serverStatus.settings) {
settings.value = {
port: serverStatus.settings.port || 3000,
autoStart: serverStatus.settings.autoStart || false,
debugLog: serverStatus.settings.enableDebugLog || false,
maxConnections: serverStatus.settings.maxConnections || 10
};
console.log('[Vue App] Server settings loaded from status:', serverStatus.settings);
} else if (serverStatus && serverStatus.port) {
// 兼容旧版本,只获取端口信息
settings.value.port = serverStatus.port;
console.log('[Vue App] Port loaded from server status:', serverStatus.port);
}
} catch (error) {
console.error('[Vue App] Failed to get server status:', error);
console.log('[Vue App] Using default server settings');
}
// 定期更新服务器状态
setInterval(async () => {
try {
const result = await Editor.Message.request('cocos-mcp-server', 'get-server-status');
if (result) {
serverRunning.value = result.running;
serverStatus.value = result.running ? '运行中' : '已停止';
connectedClients.value = result.clients || 0;
httpUrl.value = result.running ? `http://localhost:${result.port}` : '';
isProcessing.value = false;
}
} catch (error) {
console.error('[Vue App] Failed to get server status:', error);
}
}, 2000);
});
return {
// 数据
activeTab,
serverRunning,
serverStatus,
connectedClients,
httpUrl,
isProcessing,
settings,
availableTools,
toolCategories,
settingsChanged,
// 计算属性
statusClass,
totalTools,
enabledTools,
disabledTools,
// 方法
switchTab,
toggleServer,
saveSettings,
copyUrl,
loadToolManagerState,
updateToolStatus,
selectAllTools,
deselectAllTools,
saveChanges,
toggleCategoryTools,
getToolsByCategory,
getCategoryDisplayName
};
},
template: readFileSync(join(__dirname, '../../../static/template/vue/mcp-server-app.html'), 'utf-8'),
}));
app.mount(this.$.app);
panelDataMap.set(this, app);
console.log('[MCP Panel] Vue3 app mounted successfully');
} }
}, },
beforeClose() { },
close() { close() {
// Panel close cleanup const app = panelDataMap.get(this);
if (app) {
app.unmount();
}
}, },
});
// Direct properties for data access
serverRunning: false,
connectedClients: 0,
serverStatus: '',
statusClass: 'stopped',
buttonText: '',
isProcessing: false,
settings: {},
httpUrl: '',
statusInterval: null as any,
originalSettings: ''
} as any);

View File

@@ -0,0 +1,585 @@
import { readFileSync } from 'fs-extra';
import { join } from 'path';
module.exports = Editor.Panel.define({
listeners: {
show() { console.log('Tool Manager panel shown'); },
hide() { console.log('Tool Manager panel hidden'); }
},
template: readFileSync(join(__dirname, '../../../static/template/default/tool-manager.html'), 'utf-8'),
style: readFileSync(join(__dirname, '../../../static/style/default/index.css'), 'utf-8'),
$: {
panelTitle: '#panelTitle',
createConfigBtn: '#createConfigBtn',
importConfigBtn: '#importConfigBtn',
exportConfigBtn: '#exportConfigBtn',
configSelector: '#configSelector',
applyConfigBtn: '#applyConfigBtn',
editConfigBtn: '#editConfigBtn',
deleteConfigBtn: '#deleteConfigBtn',
toolsContainer: '#toolsContainer',
selectAllBtn: '#selectAllBtn',
deselectAllBtn: '#deselectAllBtn',
saveChangesBtn: '#saveChangesBtn',
totalToolsCount: '#totalToolsCount',
enabledToolsCount: '#enabledToolsCount',
disabledToolsCount: '#disabledToolsCount',
configModal: '#configModal',
modalTitle: '#modalTitle',
configForm: '#configForm',
configName: '#configName',
configDescription: '#configDescription',
closeModal: '#closeModal',
cancelConfigBtn: '#cancelConfigBtn',
saveConfigBtn: '#saveConfigBtn',
importModal: '#importModal',
importConfigJson: '#importConfigJson',
closeImportModal: '#closeImportModal',
cancelImportBtn: '#cancelImportBtn',
confirmImportBtn: '#confirmImportBtn'
},
methods: {
async loadToolManagerState(this: any) {
try {
this.toolManagerState = await Editor.Message.request('cocos-mcp-server', 'getToolManagerState');
this.currentConfiguration = this.toolManagerState.currentConfiguration;
this.configurations = this.toolManagerState.configurations;
this.availableTools = this.toolManagerState.availableTools;
this.updateUI();
} catch (error) {
console.error('Failed to load tool manager state:', error);
this.showError('加载工具管理器状态失败');
}
},
updateUI(this: any) {
this.updateConfigSelector();
this.updateToolsDisplay();
this.updateStatusBar();
this.updateButtons();
},
updateConfigSelector(this: any) {
const selector = this.$.configSelector;
selector.innerHTML = '<option value="">选择配置...</option>';
this.configurations.forEach((config: any) => {
const option = document.createElement('option');
option.value = config.id;
option.textContent = config.name;
if (this.currentConfiguration && config.id === this.currentConfiguration.id) {
option.selected = true;
}
selector.appendChild(option);
});
},
updateToolsDisplay(this: any) {
const container = this.$.toolsContainer;
if (!this.currentConfiguration) {
container.innerHTML = `
<div class="empty-state">
<h3>没有选择配置</h3>
<p>请先选择一个配置或创建新配置</p>
</div>
`;
return;
}
const toolsByCategory: any = {};
this.currentConfiguration.tools.forEach((tool: any) => {
if (!toolsByCategory[tool.category]) {
toolsByCategory[tool.category] = [];
}
toolsByCategory[tool.category].push(tool);
});
container.innerHTML = '';
Object.entries(toolsByCategory).forEach(([category, tools]: [string, any]) => {
const categoryDiv = document.createElement('div');
categoryDiv.className = 'tool-category';
const enabledCount = tools.filter((t: any) => t.enabled).length;
const totalCount = tools.length;
categoryDiv.innerHTML = `
<div class="category-header">
<div class="category-name">${this.getCategoryDisplayName(category)}</div>
<div class="category-toggle">
<span>${enabledCount}/${totalCount}</span>
<input type="checkbox" class="checkbox category-checkbox"
data-category="${category}"
${enabledCount === totalCount ? 'checked' : ''}>
</div>
</div>
<div class="tool-list">
${tools.map((tool: any) => `
<div class="tool-item">
<div class="tool-info">
<div class="tool-name">${tool.name}</div>
<div class="tool-description">${tool.description}</div>
</div>
<div class="tool-toggle">
<input type="checkbox" class="checkbox tool-checkbox"
data-category="${tool.category}"
data-name="${tool.name}"
${tool.enabled ? 'checked' : ''}>
</div>
</div>
`).join('')}
</div>
`;
container.appendChild(categoryDiv);
});
this.bindToolEvents();
},
bindToolEvents(this: any) {
document.querySelectorAll('.category-checkbox').forEach((checkbox: any) => {
checkbox.addEventListener('change', (e: any) => {
const category = e.target.dataset.category;
const checked = e.target.checked;
this.toggleCategoryTools(category, checked);
});
});
document.querySelectorAll('.tool-checkbox').forEach((checkbox: any) => {
checkbox.addEventListener('change', (e: any) => {
const category = e.target.dataset.category;
const name = e.target.dataset.name;
const enabled = e.target.checked;
this.updateToolStatus(category, name, enabled);
});
});
},
async toggleCategoryTools(this: any, category: string, enabled: boolean) {
if (!this.currentConfiguration) return;
console.log(`Toggling category tools: ${category} = ${enabled}`);
const categoryTools = this.currentConfiguration.tools.filter((tool: any) => tool.category === category);
if (categoryTools.length === 0) return;
const updates = categoryTools.map((tool: any) => ({
category: tool.category,
name: tool.name,
enabled: enabled
}));
try {
// 先更新本地状态
categoryTools.forEach((tool: any) => {
tool.enabled = enabled;
});
console.log(`Updated local category state: ${category} = ${enabled}`);
// 立即更新UI
this.updateStatusBar();
this.updateCategoryCounts();
this.updateToolCheckboxes(category, enabled);
// 然后发送到后端
await Editor.Message.request('cocos-mcp-server', 'updateToolStatusBatch',
this.currentConfiguration.id, updates);
} catch (error) {
console.error('Failed to toggle category tools:', error);
this.showError('切换类别工具失败');
// 如果后端更新失败,回滚本地状态
categoryTools.forEach((tool: any) => {
tool.enabled = !enabled;
});
this.updateStatusBar();
this.updateCategoryCounts();
this.updateToolCheckboxes(category, !enabled);
}
},
async updateToolStatus(this: any, category: string, name: string, enabled: boolean) {
if (!this.currentConfiguration) return;
console.log(`Updating tool status: ${category}.${name} = ${enabled}`);
console.log(`Current config ID: ${this.currentConfiguration.id}`);
// 先更新本地状态
const tool = this.currentConfiguration.tools.find((t: any) =>
t.category === category && t.name === name);
if (!tool) {
console.error(`Tool not found: ${category}.${name}`);
return;
}
try {
tool.enabled = enabled;
console.log(`Updated local tool state: ${tool.name} = ${tool.enabled}`);
// 立即更新UI只更新统计信息不重新渲染工具列表
this.updateStatusBar();
this.updateCategoryCounts();
// 然后发送到后端
console.log(`Sending to backend: configId=${this.currentConfiguration.id}, category=${category}, name=${name}, enabled=${enabled}`);
const result = await Editor.Message.request('cocos-mcp-server', 'updateToolStatus',
this.currentConfiguration.id, category, name, enabled);
console.log('Backend response:', result);
} catch (error) {
console.error('Failed to update tool status:', error);
this.showError('更新工具状态失败');
// 如果后端更新失败,回滚本地状态
tool.enabled = !enabled;
this.updateStatusBar();
this.updateCategoryCounts();
}
},
updateStatusBar(this: any) {
if (!this.currentConfiguration) {
this.$.totalToolsCount.textContent = '0';
this.$.enabledToolsCount.textContent = '0';
this.$.disabledToolsCount.textContent = '0';
return;
}
const total = this.currentConfiguration.tools.length;
const enabled = this.currentConfiguration.tools.filter((t: any) => t.enabled).length;
const disabled = total - enabled;
console.log(`Status bar update: total=${total}, enabled=${enabled}, disabled=${disabled}`);
this.$.totalToolsCount.textContent = total.toString();
this.$.enabledToolsCount.textContent = enabled.toString();
this.$.disabledToolsCount.textContent = disabled.toString();
},
updateCategoryCounts(this: any) {
if (!this.currentConfiguration) return;
// 更新每个类别的计数显示
document.querySelectorAll('.category-checkbox').forEach((checkbox: any) => {
const category = checkbox.dataset.category;
const categoryTools = this.currentConfiguration.tools.filter((t: any) => t.category === category);
const enabledCount = categoryTools.filter((t: any) => t.enabled).length;
const totalCount = categoryTools.length;
// 更新计数显示
const countSpan = checkbox.parentElement.querySelector('span');
if (countSpan) {
countSpan.textContent = `${enabledCount}/${totalCount}`;
}
// 更新类别复选框状态
checkbox.checked = enabledCount === totalCount;
});
},
updateToolCheckboxes(this: any, category: string, enabled: boolean) {
// 更新特定类别的所有工具复选框
document.querySelectorAll(`.tool-checkbox[data-category="${category}"]`).forEach((checkbox: any) => {
checkbox.checked = enabled;
});
},
updateButtons(this: any) {
const hasCurrentConfig = !!this.currentConfiguration;
this.$.editConfigBtn.disabled = !hasCurrentConfig;
this.$.deleteConfigBtn.disabled = !hasCurrentConfig;
this.$.exportConfigBtn.disabled = !hasCurrentConfig;
this.$.applyConfigBtn.disabled = !hasCurrentConfig;
},
async createConfiguration(this: any) {
this.editingConfig = null;
this.$.modalTitle.textContent = '新建配置';
this.$.configName.value = '';
this.$.configDescription.value = '';
this.showModal('configModal');
},
async editConfiguration(this: any) {
if (!this.currentConfiguration) return;
this.editingConfig = this.currentConfiguration;
this.$.modalTitle.textContent = '编辑配置';
this.$.configName.value = this.currentConfiguration.name;
this.$.configDescription.value = this.currentConfiguration.description || '';
this.showModal('configModal');
},
async saveConfiguration(this: any) {
const name = this.$.configName.value.trim();
const description = this.$.configDescription.value.trim();
if (!name) {
this.showError('配置名称不能为空');
return;
}
try {
if (this.editingConfig) {
await Editor.Message.request('cocos-mcp-server', 'updateToolConfiguration',
this.editingConfig.id, { name, description });
} else {
await Editor.Message.request('cocos-mcp-server', 'createToolConfiguration', name, description);
}
this.hideModal('configModal');
await this.loadToolManagerState();
} catch (error) {
console.error('Failed to save configuration:', error);
this.showError('保存配置失败');
}
},
async deleteConfiguration(this: any) {
if (!this.currentConfiguration) return;
const confirmed = await Editor.Dialog.warn('确认删除', {
detail: `确定要删除配置 "${this.currentConfiguration.name}" 吗?此操作不可撤销。`
});
if (confirmed) {
try {
await Editor.Message.request('cocos-mcp-server', 'deleteToolConfiguration',
this.currentConfiguration.id);
await this.loadToolManagerState();
} catch (error) {
console.error('Failed to delete configuration:', error);
this.showError('删除配置失败');
}
}
},
async applyConfiguration(this: any) {
const configId = this.$.configSelector.value;
if (!configId) return;
try {
await Editor.Message.request('cocos-mcp-server', 'setCurrentToolConfiguration', configId);
await this.loadToolManagerState();
} catch (error) {
console.error('Failed to apply configuration:', error);
this.showError('应用配置失败');
}
},
async exportConfiguration(this: any) {
if (!this.currentConfiguration) return;
try {
const result = await Editor.Message.request('cocos-mcp-server', 'exportToolConfiguration',
this.currentConfiguration.id);
Editor.Clipboard.write('text', result.configJson);
Editor.Dialog.info('导出成功', { detail: '配置已复制到剪贴板' });
} catch (error) {
console.error('Failed to export configuration:', error);
this.showError('导出配置失败');
}
},
async importConfiguration(this: any) {
this.$.importConfigJson.value = '';
this.showModal('importModal');
},
async confirmImport(this: any) {
const configJson = this.$.importConfigJson.value.trim();
if (!configJson) {
this.showError('请输入配置JSON');
return;
}
try {
await Editor.Message.request('cocos-mcp-server', 'importToolConfiguration', configJson);
this.hideModal('importModal');
await this.loadToolManagerState();
Editor.Dialog.info('导入成功', { detail: '配置已成功导入' });
} catch (error) {
console.error('Failed to import configuration:', error);
this.showError('导入配置失败');
}
},
async selectAllTools(this: any) {
if (!this.currentConfiguration) return;
console.log('Selecting all tools');
const updates = this.currentConfiguration.tools.map((tool: any) => ({
category: tool.category,
name: tool.name,
enabled: true
}));
try {
// 先更新本地状态
this.currentConfiguration.tools.forEach((tool: any) => {
tool.enabled = true;
});
console.log('Updated local state: all tools enabled');
// 立即更新UI
this.updateStatusBar();
this.updateToolsDisplay();
// 然后发送到后端
await Editor.Message.request('cocos-mcp-server', 'updateToolStatusBatch',
this.currentConfiguration.id, updates);
} catch (error) {
console.error('Failed to select all tools:', error);
this.showError('全选工具失败');
// 如果后端更新失败,回滚本地状态
this.currentConfiguration.tools.forEach((tool: any) => {
tool.enabled = false;
});
this.updateStatusBar();
this.updateToolsDisplay();
}
},
async deselectAllTools(this: any) {
if (!this.currentConfiguration) return;
console.log('Deselecting all tools');
const updates = this.currentConfiguration.tools.map((tool: any) => ({
category: tool.category,
name: tool.name,
enabled: false
}));
try {
// 先更新本地状态
this.currentConfiguration.tools.forEach((tool: any) => {
tool.enabled = false;
});
console.log('Updated local state: all tools disabled');
// 立即更新UI
this.updateStatusBar();
this.updateToolsDisplay();
// 然后发送到后端
await Editor.Message.request('cocos-mcp-server', 'updateToolStatusBatch',
this.currentConfiguration.id, updates);
} catch (error) {
console.error('Failed to deselect all tools:', error);
this.showError('取消全选工具失败');
// 如果后端更新失败,回滚本地状态
this.currentConfiguration.tools.forEach((tool: any) => {
tool.enabled = true;
});
this.updateStatusBar();
this.updateToolsDisplay();
}
},
getCategoryDisplayName(this: any, category: string): string {
const categoryNames: any = {
'scene': '场景工具',
'node': '节点工具',
'component': '组件工具',
'prefab': '预制体工具',
'project': '项目工具',
'debug': '调试工具',
'preferences': '偏好设置工具',
'server': '服务器工具',
'broadcast': '广播工具',
'sceneAdvanced': '高级场景工具',
'sceneView': '场景视图工具',
'referenceImage': '参考图片工具',
'assetAdvanced': '高级资源工具',
'validation': '验证工具'
};
return categoryNames[category] || category;
},
showModal(this: any, modalId: string) {
this.$[modalId].style.display = 'block';
},
hideModal(this: any, modalId: string) {
this.$[modalId].style.display = 'none';
},
showError(this: any, message: string) {
Editor.Dialog.error('错误', { detail: message });
},
async saveChanges(this: any) {
if (!this.currentConfiguration) {
this.showError('没有选择配置');
return;
}
try {
// 确保当前配置已保存到后端
await Editor.Message.request('cocos-mcp-server', 'updateToolConfiguration',
this.currentConfiguration.id, {
name: this.currentConfiguration.name,
description: this.currentConfiguration.description,
tools: this.currentConfiguration.tools
});
Editor.Dialog.info('保存成功', { detail: '配置更改已保存' });
} catch (error) {
console.error('Failed to save changes:', error);
this.showError('保存更改失败');
}
},
bindEvents(this: any) {
this.$.createConfigBtn.addEventListener('click', this.createConfiguration.bind(this));
this.$.editConfigBtn.addEventListener('click', this.editConfiguration.bind(this));
this.$.deleteConfigBtn.addEventListener('click', this.deleteConfiguration.bind(this));
this.$.applyConfigBtn.addEventListener('click', this.applyConfiguration.bind(this));
this.$.exportConfigBtn.addEventListener('click', this.exportConfiguration.bind(this));
this.$.importConfigBtn.addEventListener('click', this.importConfiguration.bind(this));
this.$.selectAllBtn.addEventListener('click', this.selectAllTools.bind(this));
this.$.deselectAllBtn.addEventListener('click', this.deselectAllTools.bind(this));
this.$.saveChangesBtn.addEventListener('click', this.saveChanges.bind(this));
this.$.closeModal.addEventListener('click', () => this.hideModal('configModal'));
this.$.cancelConfigBtn.addEventListener('click', () => this.hideModal('configModal'));
this.$.configForm.addEventListener('submit', (e: any) => {
e.preventDefault();
this.saveConfiguration();
});
this.$.closeImportModal.addEventListener('click', () => this.hideModal('importModal'));
this.$.cancelImportBtn.addEventListener('click', () => this.hideModal('importModal'));
this.$.confirmImportBtn.addEventListener('click', this.confirmImport.bind(this));
this.$.configSelector.addEventListener('change', this.applyConfiguration.bind(this));
}
},
ready() {
(this as any).toolManagerState = null;
(this as any).currentConfiguration = null;
(this as any).configurations = [];
(this as any).availableTools = [];
(this as any).editingConfig = null;
(this as any).bindEvents();
(this as any).loadToolManagerState();
},
beforeClose() {
// 清理工作
},
close() {
// 面板关闭清理
}
} as any);

View File

@@ -1,6 +1,6 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { MCPServerSettings } from './types'; import { MCPServerSettings, ToolManagerSettings, ToolConfiguration, ToolConfig } from './types';
const DEFAULT_SETTINGS: MCPServerSettings = { const DEFAULT_SETTINGS: MCPServerSettings = {
port: 3000, port: 3000,
@@ -10,10 +10,20 @@ const DEFAULT_SETTINGS: MCPServerSettings = {
maxConnections: 10 maxConnections: 10
}; };
const DEFAULT_TOOL_MANAGER_SETTINGS: ToolManagerSettings = {
configurations: [],
currentConfigId: '',
maxConfigSlots: 5
};
function getSettingsPath(): string { function getSettingsPath(): string {
return path.join(Editor.Project.path, 'settings', 'mcp-server.json'); return path.join(Editor.Project.path, 'settings', 'mcp-server.json');
} }
function getToolManagerSettingsPath(): string {
return path.join(Editor.Project.path, 'settings', 'tool-manager.json');
}
function ensureSettingsDir(): void { function ensureSettingsDir(): void {
const settingsDir = path.dirname(getSettingsPath()); const settingsDir = path.dirname(getSettingsPath());
if (!fs.existsSync(settingsDir)) { if (!fs.existsSync(settingsDir)) {
@@ -46,4 +56,48 @@ export function saveSettings(settings: MCPServerSettings): void {
} }
} }
export { DEFAULT_SETTINGS }; // 工具管理器设置相关函数
export function readToolManagerSettings(): ToolManagerSettings {
try {
ensureSettingsDir();
const settingsFile = getToolManagerSettingsPath();
if (fs.existsSync(settingsFile)) {
const content = fs.readFileSync(settingsFile, 'utf8');
return { ...DEFAULT_TOOL_MANAGER_SETTINGS, ...JSON.parse(content) };
}
} catch (e) {
console.error('Failed to read tool manager settings:', e);
}
return DEFAULT_TOOL_MANAGER_SETTINGS;
}
export function saveToolManagerSettings(settings: ToolManagerSettings): void {
try {
ensureSettingsDir();
const settingsFile = getToolManagerSettingsPath();
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
} catch (e) {
console.error('Failed to save tool manager settings:', e);
throw e;
}
}
export function exportToolConfiguration(config: ToolConfiguration): string {
return JSON.stringify(config, null, 2);
}
export function importToolConfiguration(configJson: string): ToolConfiguration {
try {
const config = JSON.parse(configJson);
// 验证配置格式
if (!config.id || !config.name || !Array.isArray(config.tools)) {
throw new Error('Invalid configuration format');
}
return config;
} catch (e) {
console.error('Failed to parse tool configuration:', e);
throw new Error('Invalid JSON format or configuration structure');
}
}
export { DEFAULT_SETTINGS, DEFAULT_TOOL_MANAGER_SETTINGS };

View File

@@ -0,0 +1,425 @@
import { v4 as uuidv4 } from 'uuid';
import { ToolConfig, ToolConfiguration, ToolManagerSettings, ToolDefinition } from '../types';
import * as fs from 'fs';
import * as path from 'path';
export class ToolManager {
private settings: ToolManagerSettings;
private availableTools: ToolConfig[] = [];
constructor() {
this.settings = this.readToolManagerSettings();
this.initializeAvailableTools();
// 如果没有配置,自动创建一个默认配置
if (this.settings.configurations.length === 0) {
console.log('[ToolManager] No configurations found, creating default configuration...');
this.createConfiguration('默认配置', '自动创建的默认工具配置');
}
}
private getToolManagerSettingsPath(): string {
return path.join(Editor.Project.path, 'settings', 'tool-manager.json');
}
private ensureSettingsDir(): void {
const settingsDir = path.dirname(this.getToolManagerSettingsPath());
if (!fs.existsSync(settingsDir)) {
fs.mkdirSync(settingsDir, { recursive: true });
}
}
private readToolManagerSettings(): ToolManagerSettings {
const DEFAULT_TOOL_MANAGER_SETTINGS: ToolManagerSettings = {
configurations: [],
currentConfigId: '',
maxConfigSlots: 5
};
try {
this.ensureSettingsDir();
const settingsFile = this.getToolManagerSettingsPath();
if (fs.existsSync(settingsFile)) {
const content = fs.readFileSync(settingsFile, 'utf8');
return { ...DEFAULT_TOOL_MANAGER_SETTINGS, ...JSON.parse(content) };
}
} catch (e) {
console.error('Failed to read tool manager settings:', e);
}
return DEFAULT_TOOL_MANAGER_SETTINGS;
}
private saveToolManagerSettings(settings: ToolManagerSettings): void {
try {
this.ensureSettingsDir();
const settingsFile = this.getToolManagerSettingsPath();
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
} catch (e) {
console.error('Failed to save tool manager settings:', e);
throw e;
}
}
private exportToolConfiguration(config: ToolConfiguration): string {
return JSON.stringify(config, null, 2);
}
private importToolConfiguration(configJson: string): ToolConfiguration {
try {
const config = JSON.parse(configJson);
// 验证配置格式
if (!config.id || !config.name || !Array.isArray(config.tools)) {
throw new Error('Invalid configuration format');
}
return config;
} catch (e) {
console.error('Failed to parse tool configuration:', e);
throw new Error('Invalid JSON format or configuration structure');
}
}
private initializeAvailableTools(): void {
// 从MCP服务器获取真实的工具列表
try {
// 导入所有工具类
const { SceneTools } = require('./scene-tools');
const { NodeTools } = require('./node-tools');
const { ComponentTools } = require('./component-tools');
const { PrefabTools } = require('./prefab-tools');
const { ProjectTools } = require('./project-tools');
const { DebugTools } = require('./debug-tools');
const { PreferencesTools } = require('./preferences-tools');
const { ServerTools } = require('./server-tools');
const { BroadcastTools } = require('./broadcast-tools');
const { SceneAdvancedTools } = require('./scene-advanced-tools');
const { SceneViewTools } = require('./scene-view-tools');
const { ReferenceImageTools } = require('./reference-image-tools');
const { AssetAdvancedTools } = require('./asset-advanced-tools');
const { ValidationTools } = require('./validation-tools');
// 初始化工具实例
const tools = {
scene: new SceneTools(),
node: new NodeTools(),
component: new ComponentTools(),
prefab: new PrefabTools(),
project: new ProjectTools(),
debug: new DebugTools(),
preferences: new PreferencesTools(),
server: new ServerTools(),
broadcast: new BroadcastTools(),
sceneAdvanced: new SceneAdvancedTools(),
sceneView: new SceneViewTools(),
referenceImage: new ReferenceImageTools(),
assetAdvanced: new AssetAdvancedTools(),
validation: new ValidationTools()
};
// 从每个工具类获取工具列表
this.availableTools = [];
for (const [category, toolSet] of Object.entries(tools)) {
const toolDefinitions = toolSet.getTools();
toolDefinitions.forEach((tool: any) => {
this.availableTools.push({
category: category,
name: tool.name,
enabled: true, // 默认启用
description: tool.description
});
});
}
console.log(`[ToolManager] Initialized ${this.availableTools.length} tools from MCP server`);
} catch (error) {
console.error('[ToolManager] Failed to initialize tools from MCP server:', error);
// 如果获取失败,使用默认工具列表作为后备
this.initializeDefaultTools();
}
}
private initializeDefaultTools(): void {
// 默认工具列表作为后备方案
const toolCategories = [
{ category: 'scene', name: '场景工具', tools: [
{ name: 'getCurrentSceneInfo', description: '获取当前场景信息' },
{ name: 'getSceneHierarchy', description: '获取场景层级结构' },
{ name: 'createNewScene', description: '创建新场景' },
{ name: 'saveScene', description: '保存场景' },
{ name: 'loadScene', description: '加载场景' }
]},
{ category: 'node', name: '节点工具', tools: [
{ name: 'getAllNodes', description: '获取所有节点' },
{ name: 'findNodeByName', description: '根据名称查找节点' },
{ name: 'createNode', description: '创建节点' },
{ name: 'deleteNode', description: '删除节点' },
{ name: 'setNodeProperty', description: '设置节点属性' },
{ name: 'getNodeInfo', description: '获取节点信息' }
]},
{ category: 'component', name: '组件工具', tools: [
{ name: 'addComponentToNode', description: '添加组件到节点' },
{ name: 'removeComponentFromNode', description: '从节点移除组件' },
{ name: 'setComponentProperty', description: '设置组件属性' },
{ name: 'getComponentInfo', description: '获取组件信息' }
]},
{ category: 'prefab', name: '预制体工具', tools: [
{ name: 'createPrefabFromNode', description: '从节点创建预制体' },
{ name: 'instantiatePrefab', description: '实例化预制体' },
{ name: 'getPrefabInfo', description: '获取预制体信息' },
{ name: 'savePrefab', description: '保存预制体' }
]},
{ category: 'project', name: '项目工具', tools: [
{ name: 'getProjectInfo', description: '获取项目信息' },
{ name: 'getAssetList', description: '获取资源列表' },
{ name: 'createAsset', description: '创建资源' },
{ name: 'deleteAsset', description: '删除资源' }
]},
{ category: 'debug', name: '调试工具', tools: [
{ name: 'getConsoleLogs', description: '获取控制台日志' },
{ name: 'getPerformanceStats', description: '获取性能统计' },
{ name: 'validateScene', description: '验证场景' },
{ name: 'getErrorLogs', description: '获取错误日志' }
]},
{ category: 'preferences', name: '偏好设置工具', tools: [
{ name: 'getPreferences', description: '获取偏好设置' },
{ name: 'setPreferences', description: '设置偏好设置' },
{ name: 'resetPreferences', description: '重置偏好设置' }
]},
{ category: 'server', name: '服务器工具', tools: [
{ name: 'getServerStatus', description: '获取服务器状态' },
{ name: 'getConnectedClients', description: '获取连接的客户端' },
{ name: 'getServerLogs', description: '获取服务器日志' }
]},
{ category: 'broadcast', name: '广播工具', tools: [
{ name: 'broadcastMessage', description: '广播消息' },
{ name: 'getBroadcastHistory', description: '获取广播历史' }
]},
{ category: 'sceneAdvanced', name: '高级场景工具', tools: [
{ name: 'optimizeScene', description: '优化场景' },
{ name: 'analyzeScene', description: '分析场景' },
{ name: 'batchOperation', description: '批量操作' }
]},
{ category: 'sceneView', name: '场景视图工具', tools: [
{ name: 'getViewportInfo', description: '获取视口信息' },
{ name: 'setViewportCamera', description: '设置视口相机' },
{ name: 'focusOnNode', description: '聚焦到节点' }
]},
{ category: 'referenceImage', name: '参考图片工具', tools: [
{ name: 'addReferenceImage', description: '添加参考图片' },
{ name: 'removeReferenceImage', description: '移除参考图片' },
{ name: 'getReferenceImages', description: '获取参考图片列表' }
]},
{ category: 'assetAdvanced', name: '高级资源工具', tools: [
{ name: 'importAsset', description: '导入资源' },
{ name: 'exportAsset', description: '导出资源' },
{ name: 'processAsset', description: '处理资源' }
]},
{ category: 'validation', name: '验证工具', tools: [
{ name: 'validateProject', description: '验证项目' },
{ name: 'validateAssets', description: '验证资源' },
{ name: 'generateReport', description: '生成报告' }
]}
];
this.availableTools = [];
toolCategories.forEach(category => {
category.tools.forEach(tool => {
this.availableTools.push({
category: category.category,
name: tool.name,
enabled: true, // 默认启用
description: tool.description
});
});
});
console.log(`[ToolManager] Initialized ${this.availableTools.length} default tools`);
}
public getAvailableTools(): ToolConfig[] {
return [...this.availableTools];
}
public getConfigurations(): ToolConfiguration[] {
return [...this.settings.configurations];
}
public getCurrentConfiguration(): ToolConfiguration | null {
if (!this.settings.currentConfigId) {
return null;
}
return this.settings.configurations.find(config => config.id === this.settings.currentConfigId) || null;
}
public createConfiguration(name: string, description?: string): ToolConfiguration {
if (this.settings.configurations.length >= this.settings.maxConfigSlots) {
throw new Error(`已达到最大配置槽位数量 (${this.settings.maxConfigSlots})`);
}
const config: ToolConfiguration = {
id: uuidv4(),
name,
description,
tools: this.availableTools.map(tool => ({ ...tool })),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
this.settings.configurations.push(config);
this.settings.currentConfigId = config.id;
this.saveSettings();
return config;
}
public updateConfiguration(configId: string, updates: Partial<ToolConfiguration>): ToolConfiguration {
const configIndex = this.settings.configurations.findIndex(config => config.id === configId);
if (configIndex === -1) {
throw new Error('配置不存在');
}
const config = this.settings.configurations[configIndex];
const updatedConfig: ToolConfiguration = {
...config,
...updates,
updatedAt: new Date().toISOString()
};
this.settings.configurations[configIndex] = updatedConfig;
this.saveSettings();
return updatedConfig;
}
public deleteConfiguration(configId: string): void {
const configIndex = this.settings.configurations.findIndex(config => config.id === configId);
if (configIndex === -1) {
throw new Error('配置不存在');
}
this.settings.configurations.splice(configIndex, 1);
// 如果删除的是当前配置清空当前配置ID
if (this.settings.currentConfigId === configId) {
this.settings.currentConfigId = this.settings.configurations.length > 0
? this.settings.configurations[0].id
: '';
}
this.saveSettings();
}
public setCurrentConfiguration(configId: string): void {
const config = this.settings.configurations.find(config => config.id === configId);
if (!config) {
throw new Error('配置不存在');
}
this.settings.currentConfigId = configId;
this.saveSettings();
}
public updateToolStatus(configId: string, category: string, toolName: string, enabled: boolean): void {
console.log(`Backend: Updating tool status - configId: ${configId}, category: ${category}, toolName: ${toolName}, enabled: ${enabled}`);
const config = this.settings.configurations.find(config => config.id === configId);
if (!config) {
console.error(`Backend: Config not found with ID: ${configId}`);
throw new Error('配置不存在');
}
console.log(`Backend: Found config: ${config.name}`);
const tool = config.tools.find(t => t.category === category && t.name === toolName);
if (!tool) {
console.error(`Backend: Tool not found - category: ${category}, name: ${toolName}`);
throw new Error('工具不存在');
}
console.log(`Backend: Found tool: ${tool.name}, current enabled: ${tool.enabled}, new enabled: ${enabled}`);
tool.enabled = enabled;
config.updatedAt = new Date().toISOString();
console.log(`Backend: Tool updated, saving settings...`);
this.saveSettings();
console.log(`Backend: Settings saved successfully`);
}
public updateToolStatusBatch(configId: string, updates: { category: string; name: string; enabled: boolean }[]): void {
console.log(`Backend: updateToolStatusBatch called with configId: ${configId}`);
console.log(`Backend: Current configurations count: ${this.settings.configurations.length}`);
console.log(`Backend: Current config IDs:`, this.settings.configurations.map(c => c.id));
const config = this.settings.configurations.find(config => config.id === configId);
if (!config) {
console.error(`Backend: Config not found with ID: ${configId}`);
console.error(`Backend: Available config IDs:`, this.settings.configurations.map(c => c.id));
throw new Error('配置不存在');
}
console.log(`Backend: Found config: ${config.name}, updating ${updates.length} tools`);
updates.forEach(update => {
const tool = config.tools.find(t => t.category === update.category && t.name === update.name);
if (tool) {
tool.enabled = update.enabled;
}
});
config.updatedAt = new Date().toISOString();
this.saveSettings();
console.log(`Backend: Batch update completed successfully`);
}
public exportConfiguration(configId: string): string {
const config = this.settings.configurations.find(config => config.id === configId);
if (!config) {
throw new Error('配置不存在');
}
return this.exportToolConfiguration(config);
}
public importConfiguration(configJson: string): ToolConfiguration {
const config = this.importToolConfiguration(configJson);
// 生成新的ID和时间戳
config.id = uuidv4();
config.createdAt = new Date().toISOString();
config.updatedAt = new Date().toISOString();
if (this.settings.configurations.length >= this.settings.maxConfigSlots) {
throw new Error(`已达到最大配置槽位数量 (${this.settings.maxConfigSlots})`);
}
this.settings.configurations.push(config);
this.saveSettings();
return config;
}
public getEnabledTools(): ToolConfig[] {
const currentConfig = this.getCurrentConfiguration();
if (!currentConfig) {
return this.availableTools.filter(tool => tool.enabled);
}
return currentConfig.tools.filter(tool => tool.enabled);
}
public getToolManagerState() {
const currentConfig = this.getCurrentConfiguration();
return {
success: true,
availableTools: currentConfig ? currentConfig.tools : this.getAvailableTools(),
selectedConfigId: this.settings.currentConfigId,
configurations: this.getConfigurations(),
maxConfigSlots: this.settings.maxConfigSlots
};
}
private saveSettings(): void {
console.log(`Backend: Saving settings, current configs count: ${this.settings.configurations.length}`);
this.saveToolManagerSettings(this.settings);
console.log(`Backend: Settings saved to file`);
}
}

View File

@@ -124,4 +124,33 @@ export interface MCPClient {
export interface ToolExecutor { export interface ToolExecutor {
getTools(): ToolDefinition[]; getTools(): ToolDefinition[];
execute(toolName: string, args: any): Promise<ToolResponse>; execute(toolName: string, args: any): Promise<ToolResponse>;
}
// 工具配置管理相关接口
export interface ToolConfig {
category: string;
name: string;
enabled: boolean;
description: string;
}
export interface ToolConfiguration {
id: string;
name: string;
description?: string;
tools: ToolConfig[];
createdAt: string;
updatedAt: string;
}
export interface ToolManagerSettings {
configurations: ToolConfiguration[];
currentConfigId: string;
maxConfigSlots: number;
}
export interface ToolManagerState {
availableTools: ToolConfig[];
currentConfiguration: ToolConfiguration | null;
configurations: ToolConfiguration[];
} }

View File

@@ -14,6 +14,54 @@ header h2 {
font-size: 18px; font-size: 18px;
} }
/* Vue3 应用样式 */
.mcp-app {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
/* 选项卡导航样式 */
.tab-navigation {
display: flex;
border-bottom: 1px solid var(--color-normal-border);
margin-bottom: 20px;
background: var(--color-panel);
}
.tab-button {
background: none;
border: none;
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
color: var(--color-normal-text);
font-size: 14px;
transition: all 0.2s ease;
border-radius: 4px 4px 0 0;
}
.tab-button:hover {
background: var(--color-normal-fill-emphasis);
}
.tab-button.active {
border-bottom-color: var(--color-primary);
color: var(--color-primary);
font-weight: 600;
background: var(--color-panel);
}
/* 选项卡内容样式 */
.tab-content {
display: flex;
flex-direction: column;
flex: 1;
overflow-y: auto;
padding: 20px;
}
section { section {
margin-bottom: 20px; margin-bottom: 20px;
padding: 15px; padding: 15px;
@@ -62,4 +110,283 @@ footer {
ui-prop { ui-prop {
margin-bottom: 10px; margin-bottom: 10px;
}
/* 工具管理器样式 */
.tool-manager {
display: flex;
flex-direction: column;
height: 100%;
}
.tool-manager-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.tool-manager-controls {
display: flex;
gap: 10px;
}
.config-selector-section {
margin-bottom: 20px;
}
.tools-section {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
.tools-section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid var(--color-normal-border);
}
.tools-section-title {
display: flex;
flex-direction: column;
gap: 5px;
}
.tools-section-title h4 {
margin: 0;
font-size: 14px;
font-weight: 600;
}
.tools-stats {
font-size: 12px;
color: var(--color-normal-text-secondary);
}
.tools-section-controls {
display: flex;
gap: 10px;
align-items: center;
}
.tools-container {
flex: 1;
overflow-y: auto;
padding-right: 8px;
max-height: 400px;
}
.tools-container::-webkit-scrollbar {
width: 6px;
}
.tools-container::-webkit-scrollbar-track {
background: var(--color-normal-fill-emphasis);
border-radius: 3px;
}
.tools-container::-webkit-scrollbar-thumb {
background: var(--color-normal-border);
border-radius: 3px;
}
.tools-container::-webkit-scrollbar-thumb:hover {
background: var(--color-normal-text-secondary);
}
.tool-category {
margin-bottom: 20px;
background: var(--color-normal-fill);
border-radius: 6px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.category-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 15px;
background: linear-gradient(135deg, var(--color-primary-fill) 0%, var(--color-primary-fill-emphasis) 100%);
color: var(--color-primary-text);
font-weight: 600;
}
.category-name {
font-size: 14px;
}
.category-toggle {
display: flex;
align-items: center;
gap: 10px;
font-size: 12px;
}
.tool-list {
padding: 10px 15px;
}
.tool-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid var(--color-normal-border);
}
.tool-item:last-child {
border-bottom: none;
}
.tool-info {
flex: 1;
}
.tool-name {
font-size: 13px;
font-weight: 500;
color: var(--color-normal-text);
}
.tool-description {
font-size: 11px;
color: var(--color-normal-text-secondary);
margin-top: 2px;
}
.tool-toggle {
display: flex;
align-items: center;
}
.checkbox {
width: 16px;
height: 16px;
cursor: pointer;
}
.tool-manager-footer {
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid var(--color-normal-border);
}
.config-actions {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 模态框样式 */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(4px);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: var(--color-normal-fill);
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
min-width: 400px;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 20px 0 20px;
border-bottom: 1px solid var(--color-normal-border);
padding-bottom: 15px;
}
.modal-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: var(--color-normal-text-secondary);
padding: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.modal-close:hover {
background: var(--color-normal-fill-emphasis);
color: var(--color-normal-text);
}
.modal-body {
padding: 20px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 0 20px 20px 20px;
border-top: 1px solid var(--color-normal-border);
padding-top: 15px;
}
/* 按钮样式 */
ui-button.small {
font-size: 12px;
padding: 4px 8px;
min-width: auto;
}
ui-button.secondary {
background: var(--color-normal-fill-emphasis);
color: var(--color-normal-text);
border: 1px solid var(--color-normal-border);
}
ui-button.secondary:hover {
background: var(--color-normal-fill);
}
/* 空状态样式 */
.empty-state {
text-align: center;
padding: 40px 20px;
color: var(--color-normal-text-secondary);
}
.empty-state h3 {
margin: 0 0 10px 0;
font-size: 16px;
}
.empty-state p {
margin: 0;
font-size: 14px;
} }

View File

@@ -1,64 +1,10 @@
<div class="mcp-server-panel"> <div class="mcp-server-panel">
<header> <header>
<h2 id="panelTitle"></h2> <h2 id="panelTitle">MCP 服务器</h2>
</header> </header>
<section class="server-status"> <!-- Vue3 应用挂载点 -->
<h3 id="serverStatusLabel"></h3> <div id="app">
<div class="status-info"> <mcp-server-app></mcp-server-app>
<ui-prop> </div>
<ui-label id="serverStatusLabelProp" slot="label"></ui-label>
<ui-label id="serverStatusValue" slot="content" class="status-value" :class="statusClass"></ui-label>
</ui-prop>
<ui-prop v-if="serverRunning">
<ui-label id="connectedLabel" slot="label"></ui-label>
<ui-label id="connectedClients" slot="content"></ui-label>
</ui-prop>
</div>
</section>
<section class="server-controls">
<ui-button id="toggleServerBtn" @click="toggleServer" :disabled="isProcessing" class="primary"></ui-button>
</section>
<section class="server-settings">
<h3 id="settingsLabel"></h3>
<ui-prop>
<ui-label id="portLabel" slot="label"></ui-label>
<ui-num-input id="portInput" slot="content"
:min="1024" :max="65535" :step="1"
:disabled="serverRunning">
</ui-num-input>
</ui-prop>
<ui-prop>
<ui-label id="autoStartLabel" slot="label"></ui-label>
<ui-checkbox id="autoStartInput" slot="content"></ui-checkbox>
</ui-prop>
<ui-prop>
<ui-label id="debugLogLabel" slot="label"></ui-label>
<ui-checkbox id="debugLogInput" slot="content"></ui-checkbox>
</ui-prop>
<ui-prop>
<ui-label id="maxConnectionsLabel" slot="label"></ui-label>
<ui-num-input id="maxConnInput" slot="content"
:min="1" :max="100" :step="1">
</ui-num-input>
</ui-prop>
</section>
<section class="server-info" v-if="serverRunning">
<h3 id="connectionInfoLabel"></h3>
<div class="connection-details">
<ui-prop>
<ui-label id="httpUrlLabel" slot="label"></ui-label>
<ui-input id="httpUrlInput" slot="content" :value="httpUrl" readonly>
<ui-button id="copyBtn" slot="suffix" @click="copyUrl"></ui-button>
</ui-input>
</ui-prop>
</div>
</section>
<footer>
<ui-button id="saveSettingsBtn" @click="saveSettings" :disabled="!settingsChanged"></ui-button>
</footer>
</div> </div>

View File

@@ -0,0 +1,582 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>工具管理器</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
background: #2a2a2a;
color: #e0e0e0;
}
.container {
display: flex;
flex-direction: column;
height: calc(100vh - 40px);
gap: 20px;
max-width: 1200px;
margin: 0 auto;
overflow-y: auto;
padding-right: 8px;
}
/* 容器滚动条样式 */
.container::-webkit-scrollbar {
width: 8px;
}
.container::-webkit-scrollbar-track {
background: #2a2a2a;
border-radius: 4px;
}
.container::-webkit-scrollbar-thumb {
background: #5a5a5a;
border-radius: 4px;
}
.container::-webkit-scrollbar-thumb:hover {
background: #6a6a6a;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
background: linear-gradient(135deg, #3a3a3a 0%, #2d2d2d 100%);
border: 1px solid #4a4a4a;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.header h2 {
margin: 0;
font-size: 20px;
font-weight: 600;
color: #ffffff;
}
.config-section {
background: linear-gradient(135deg, #323232 0%, #2a2a2a 100%);
border: 1px solid #4a4a4a;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.config-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 2px solid #4a4a4a;
}
.config-title {
font-weight: 600;
font-size: 16px;
color: #ffffff;
}
.config-controls {
display: flex;
gap: 10px;
}
.config-selector {
display: flex;
gap: 12px;
margin-bottom: 20px;
align-items: center;
}
.config-selector select {
flex: 1;
padding: 10px 12px;
border: 1px solid #5a5a5a;
border-radius: 6px;
background: #3a3a3a;
color: #e0e0e0;
font-size: 14px;
transition: all 0.2s;
}
.config-selector select:focus {
outline: none;
border-color: #007acc;
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
}
.btn {
padding: 8px 16px;
border: 1px solid #5a5a5a;
border-radius: 6px;
background: linear-gradient(135deg, #4a4a4a 0%, #3a3a3a 100%);
color: #e0e0e0;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.btn:hover {
background: linear-gradient(135deg, #5a5a5a 0%, #4a4a4a 100%);
border-color: #6a6a6a;
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
.btn:active {
transform: translateY(0);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.btn-primary {
background: linear-gradient(135deg, #007acc 0%, #005a9e 100%);
color: white;
border-color: #007acc;
}
.btn-primary:hover {
background: linear-gradient(135deg, #0088e6 0%, #0066cc 100%);
border-color: #0088e6;
}
.btn-danger {
background: linear-gradient(135deg, #d32f2f 0%, #b71c1c 100%);
color: white;
border-color: #d32f2f;
}
.btn-danger:hover {
background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%);
border-color: #f44336;
}
.tools-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 16px;
max-height: 500px;
overflow-y: auto;
padding: 4px;
}
.tool-category {
background: linear-gradient(135deg, #383838 0%, #2f2f2f 100%);
border: 1px solid #4a4a4a;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
transition: all 0.2s;
}
.tool-category:hover {
border-color: #5a5a5a;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transform: translateY(-2px);
}
.category-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding-bottom: 10px;
border-bottom: 2px solid #4a4a4a;
}
.category-name {
font-weight: 600;
font-size: 14px;
color: #ffffff;
}
.category-toggle {
display: flex;
align-items: center;
gap: 4px;
}
.tool-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.tool-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
margin: 4px 0;
background: rgba(255, 255, 255, 0.02);
border-radius: 6px;
transition: all 0.2s;
}
.tool-item:hover {
background: rgba(255, 255, 255, 0.05);
transform: translateX(2px);
}
.tool-info {
flex: 1;
margin-right: 12px;
}
.tool-name {
font-size: 13px;
font-weight: 500;
color: #e0e0e0;
}
.tool-description {
font-size: 12px;
color: #b0b0b0;
margin-top: 4px;
line-height: 1.4;
}
.tool-toggle {
display: flex;
align-items: center;
}
.checkbox {
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #007acc;
}
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
background: linear-gradient(135deg, #323232 0%, #2a2a2a 100%);
border: 1px solid #4a4a4a;
border-radius: 8px;
font-size: 13px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.status-info {
display: flex;
gap: 20px;
color: #b0b0b0;
}
.status-info span {
font-weight: 500;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(4px);
z-index: 1000;
}
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: linear-gradient(135deg, #3a3a3a 0%, #2d2d2d 100%);
border: 1px solid #5a5a5a;
border-radius: 12px;
padding: 24px;
min-width: 400px;
max-width: 600px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
backdrop-filter: blur(8px);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #4a4a4a;
}
.modal-title {
font-weight: 600;
font-size: 18px;
color: #ffffff;
}
.modal-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #b0b0b0;
padding: 4px;
border-radius: 4px;
transition: all 0.2s;
}
.modal-close:hover {
color: #ffffff;
background: rgba(255, 255, 255, 0.1);
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
margin-bottom: 6px;
font-size: 13px;
font-weight: 500;
color: #e0e0e0;
}
.form-input {
width: 100%;
padding: 10px 12px;
border: 1px solid #5a5a5a;
border-radius: 6px;
background: #3a3a3a;
color: #e0e0e0;
font-size: 14px;
transition: all 0.2s;
box-sizing: border-box;
}
.form-input:focus {
outline: none;
border-color: #007acc;
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
}
.form-textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid #5a5a5a;
border-radius: 6px;
background: #3a3a3a;
color: #e0e0e0;
font-size: 14px;
resize: vertical;
min-height: 80px;
transition: all 0.2s;
box-sizing: border-box;
}
.form-textarea:focus {
outline: none;
border-color: #007acc;
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 16px;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #b0b0b0;
background: linear-gradient(135deg, #383838 0%, #2f2f2f 100%);
border: 2px dashed #4a4a4a;
border-radius: 12px;
margin: 20px 0;
}
.empty-state h3 {
margin: 0 0 12px 0;
font-size: 18px;
color: #e0e0e0;
font-weight: 600;
}
.empty-state p {
margin: 0;
font-size: 14px;
line-height: 1.5;
}
/* 滚动条样式 */
.tools-container::-webkit-scrollbar {
width: 8px;
}
.tools-container::-webkit-scrollbar-track {
background: #2a2a2a;
border-radius: 4px;
}
.tools-container::-webkit-scrollbar-thumb {
background: #5a5a5a;
border-radius: 4px;
}
.tools-container::-webkit-scrollbar-thumb:hover {
background: #6a6a6a;
}
/* 工具管理区域标题 */
.tools-section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding: 16px 20px;
background: linear-gradient(135deg, #323232 0%, #2a2a2a 100%);
border: 1px solid #4a4a4a;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.tools-section-title {
font-weight: 600;
font-size: 16px;
color: #ffffff;
}
.tools-section-controls {
display: flex;
gap: 10px;
}
.loading {
text-align: center;
padding: 40px 20px;
color: #b0b0b0;
font-size: 14px;
}
.error {
background: linear-gradient(135deg, #d32f2f 0%, #b71c1c 100%);
color: white;
padding: 12px 16px;
border-radius: 6px;
font-size: 13px;
margin-bottom: 16px;
border: 1px solid #f44336;
}
</style>
</head>
<body>
<div class="container">
<!-- 头部 -->
<div class="header">
<h2 id="panelTitle">工具管理器</h2>
<div class="config-controls">
<button class="btn btn-primary" id="createConfigBtn">新建配置</button>
<button class="btn" id="importConfigBtn">导入配置</button>
<button class="btn" id="exportConfigBtn">导出配置</button>
</div>
</div>
<!-- 配置选择器 -->
<div class="config-section">
<div class="config-header">
<div class="config-title">当前配置</div>
<div class="config-controls">
<button class="btn" id="editConfigBtn">编辑</button>
<button class="btn btn-danger" id="deleteConfigBtn">删除</button>
</div>
</div>
<div class="config-selector">
<select id="configSelector">
<option value="">选择配置...</option>
</select>
<button class="btn btn-primary" id="applyConfigBtn">应用</button>
</div>
</div>
<!-- 工具列表 -->
<div class="config-section">
<div class="tools-section-header">
<div class="tools-section-title">工具管理</div>
<div class="tools-section-controls">
<button class="btn" id="selectAllBtn">全选</button>
<button class="btn" id="deselectAllBtn">取消全选</button>
</div>
</div>
<div id="toolsContainer" class="tools-container">
<div class="loading">加载中...</div>
</div>
</div>
<!-- 状态栏 -->
<div class="status-bar">
<div class="status-info">
<span>总工具数: <span id="totalToolsCount">0</span></span>
<span>已启用: <span id="enabledToolsCount">0</span></span>
<span>已禁用: <span id="disabledToolsCount">0</span></span>
</div>
<div>
<button class="btn" id="saveChangesBtn">保存更改</button>
</div>
</div>
</div>
<!-- 新建/编辑配置模态框 -->
<div id="configModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title" id="modalTitle">新建配置</div>
<button class="modal-close" id="closeModal">&times;</button>
</div>
<form id="configForm">
<div class="form-group">
<label class="form-label" for="configName">配置名称 *</label>
<input type="text" id="configName" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label" for="configDescription">描述</label>
<textarea id="configDescription" class="form-textarea" placeholder="可选:添加配置描述"></textarea>
</div>
<div class="modal-actions">
<button type="button" class="btn" id="cancelConfigBtn">取消</button>
<button type="submit" class="btn btn-primary" id="saveConfigBtn">保存</button>
</div>
</form>
</div>
</div>
<!-- 导入配置模态框 -->
<div id="importModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title">导入配置</div>
<button class="modal-close" id="closeImportModal">&times;</button>
</div>
<div class="form-group">
<label class="form-label" for="importConfigJson">配置JSON</label>
<textarea id="importConfigJson" class="form-textarea" placeholder="粘贴配置JSON内容"></textarea>
</div>
<div class="modal-actions">
<button type="button" class="btn" id="cancelImportBtn">取消</button>
<button type="button" class="btn btn-primary" id="confirmImportBtn">导入</button>
</div>
</div>
</div>
<script>
// 这里将包含面板的JavaScript逻辑
</script>
</body>
</html>

View File

@@ -0,0 +1,126 @@
<div class="mcp-app">
<!-- 选项卡导航 -->
<div class="tab-navigation">
<button class="tab-button" :class="{ active: activeTab === 'server' }" @click="switchTab('server')">
<span>服务器</span>
</button>
<button class="tab-button" :class="{ active: activeTab === 'tools' }" @click="switchTab('tools')">
<span>工具管理</span>
</button>
</div>
<!-- 服务器选项卡 -->
<div class="tab-content" v-show="activeTab === 'server'">
<section class="server-status">
<h3>服务器状态</h3>
<div class="status-info">
<ui-prop>
<ui-label slot="label">状态</ui-label>
<ui-label slot="content" class="status-value" :class="statusClass">{{ serverStatus }}</ui-label>
</ui-prop>
<ui-prop v-if="serverRunning">
<ui-label slot="label">连接数</ui-label>
<ui-label slot="content">{{ connectedClients }}</ui-label>
</ui-prop>
</div>
</section>
<section class="server-controls">
<ui-button @click="toggleServer" :disabled="isProcessing" class="primary">
{{ serverRunning ? '停止服务器' : '启动服务器' }}
</ui-button>
</section>
<section class="server-settings">
<h3>服务器设置</h3>
<ui-prop>
<ui-label slot="label">端口</ui-label>
<ui-num-input slot="content" v-model="settings.port" :min="1024" :max="65535" :step="1" :disabled="serverRunning">
</ui-num-input>
</ui-prop>
<ui-prop>
<ui-label slot="label">自动启动</ui-label>
<ui-checkbox slot="content" v-model="settings.autoStart"></ui-checkbox>
</ui-prop>
<ui-prop>
<ui-label slot="label">调试日志</ui-label>
<ui-checkbox slot="content" v-model="settings.debugLog"></ui-checkbox>
</ui-prop>
<ui-prop>
<ui-label slot="label">最大连接数</ui-label>
<ui-num-input slot="content" v-model="settings.maxConnections" :min="1" :max="100" :step="1">
</ui-num-input>
</ui-prop>
</section>
<section class="server-info" v-if="serverRunning">
<h3>连接信息</h3>
<div class="connection-details">
<ui-prop>
<ui-label slot="label">HTTP URL</ui-label>
<ui-input slot="content" :value="httpUrl" readonly>
<ui-button slot="suffix" @click="copyUrl">复制</ui-button>
</ui-input>
</ui-prop>
</div>
</section>
<footer>
<ui-button @click="saveSettings" :disabled="!settingsChanged">保存设置</ui-button>
</footer>
</div>
<!-- 工具管理选项卡 -->
<div class="tab-content" v-show="activeTab === 'tools'">
<section class="tool-manager">
<div class="tool-manager-header">
<h3>工具管理</h3>
</div>
<div class="tools-section">
<div class="tools-section-header">
<div class="tools-section-title">
<h4>可用工具</h4>
<div class="tools-stats">
{{ totalTools }} 个工具
({{ enabledTools }} 启用 / {{ disabledTools }} 禁用)
</div>
</div>
<div class="tools-section-controls">
<ui-button @click="selectAllTools" class="small">全选</ui-button>
<ui-button @click="deselectAllTools" class="small">取消全选</ui-button>
<ui-button @click="saveChanges" class="primary">保存更改</ui-button>
</div>
</div>
<div class="tools-container">
<div v-for="category in toolCategories" :key="category" class="tool-category">
<div class="category-header">
<h5>{{ getCategoryDisplayName(category) }}</h5>
<div class="category-controls">
<ui-button @click="toggleCategoryTools(category, true)" class="small">全选</ui-button>
<ui-button @click="toggleCategoryTools(category, false)" class="small">取消全选</ui-button>
</div>
</div>
<div class="tool-items">
<div v-for="tool in getToolsByCategory(category)" :key="tool.name" class="tool-item">
<ui-checkbox
:value="tool.enabled"
@change="(event) => updateToolStatus(category, tool.name, event.target.checked)"
></ui-checkbox>
<div class="tool-info">
<div class="tool-name">{{ tool.name }}</div>
<div class="tool-description">{{ tool.description }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>