Cocos MCP Log Bridge

by czh2774
Verified
# Cocos MCP 扩展开发指南 本文档面向希望维护、调试或扩展 Cocos MCP 功能的开发者。 ## 项目架构 ### 整体结构 ``` cocos-mcp/ ├── Editor/ # Cocos Creator 编辑器扩展代码 │ ├── @types/ # TypeScript 类型定义 │ ├── LogBridge.ts # 日志桥接核心实现 │ ├── config/ # 配置文件 │ └── main.ts # 扩展入口点 ├── Python/ # Python MCP 服务器 │ ├── server.py # 服务器实现 │ ├── cocos_connection.py # Cocos TCP 客户端 │ ├── log_client.py # 日志客户端 │ ├── config/ # 配置文件 │ └── tools/ # 工具函数 ├── dist/ # 编译后的 JavaScript 文件 ├── node_modules/ # Node.js 依赖 ├── package.json # 项目配置 └── tsconfig.json # TypeScript 配置 ``` ### 运行流程 ```mermaid graph TD A[Cocos Creator 编辑器] -->|TCP 通信| B[LogBridge 扩展] B -->|日志查询| C[Editor.Logger API] D[Python MCP 服务器] -->|TCP 客户端| B E[Cursor AI] -->|WebSocket| D C -->|返回日志| B B -->|返回结果| D D -->|返回结果| E ``` 1. Cocos Creator 编辑器启动并加载 cocos-mcp 扩展 2. LogBridge 初始化并启动 TCP 服务器(端口 6400) 3. Python MCP 服务器连接到 TCP 服务器 4. Cursor AI 通过 WebSocket 连接到 Python MCP 服务器 5. 用户发送命令,流经 Cursor AI -> Python -> LogBridge -> Cocos Creator 6. 结果按相反顺序返回 ## 开发环境设置 ### 编辑器扩展开发 1. **安装 Node.js 依赖** ```bash cd /path/to/cocos-mcp npm install ``` 2. **编译 TypeScript 代码** ```bash cd /path/to/cocos-mcp tsc ``` 3. **启用实时编译(可选)** ```bash cd /path/to/cocos-mcp tsc --watch ``` ### Python 服务器开发 1. **创建虚拟环境(可选但推荐)** ```bash cd /path/to/cocos-mcp/Python uv venv source .venv/bin/activate # 在 Linux/macOS 上 # 或在 Windows 上: # .venv\Scripts\activate ``` 2. **安装依赖** ```bash uv pip install -r requirements.txt ``` ## 核心模块详解 ### LogBridge 类 (LogBridge.ts) LogBridge 是扩展的核心类,负责创建 TCP 服务器并处理命令。 主要方法: - `getInstance()`: 单例模式获取实例 - `startTcpServer()`: 启动 TCP 服务器 - `setupCommandHandlers()`: 注册命令处理器 - `handleQueryLogs()`: 处理日志查询命令 - `handleClearLogs()`: 处理日志清除命令 示例:如何修改 TCP 端口: ```typescript // 在 config/Config.ts 中修改 export const Config = { TCP_HOST: 'localhost', TCP_PORT: 8000 // 修改为新端口 }; ``` ### Python MCP 服务器 (server.py) Python MCP 服务器负责连接 Cursor AI 和 Cocos Creator 扩展。 主要组件: - `MCPServer`: WebSocket 服务器,处理 Cursor AI 请求 - `CocosTCPClient`: TCP 客户端,连接到 LogBridge - 命令处理器:处理查询日志、清除日志等命令 示例:如何修改 WebSocket 端口: ```python # 在 config/config.py 中修改 WEBSOCKET_HOST = "localhost" WEBSOCKET_PORT = 9000 # 修改为新端口 ``` ## 编码规范 ### TypeScript - 使用 2 空格缩进 - 使用 camelCase 命名变量和函数 - 使用 PascalCase 命名类和接口 - 尽可能添加类型注解 - 添加合适的注释,特别是对于公共 API 示例: ```typescript /** * 处理日志查询命令 * @param params 查询参数 * @returns 查询结果 */ private async handleQueryLogs(params: QueryLogsParams): Promise<QueryLogsResult> { try { const showLogs = params.show_logs !== false; const showWarnings = params.show_warnings !== false; const showErrors = params.show_errors !== false; const searchTerm = params.search_term || ''; // 获取日志并过滤 const logs = await Editor.Logger.query() || []; // ... 过滤代码 ... return { logs: filteredLogs }; } catch (error: any) { console.error('Error querying logs:', error); throw error; } } ``` ### Python - 遵循 PEP 8 规范 - 使用 4 空格缩进 - 使用蛇形命名法(snake_case)命名变量和函数 - 使用类型注解(Python 3.7+) 示例: ```python async def handle_query_logs(self, params: Dict[str, Any]) -> Dict[str, Any]: """ 处理日志查询请求 Args: params: 查询参数 Returns: 查询结果 """ try: # 发送查询到 Cocos TCP 服务器 response = await self.cocos_client.send_command("QUERY_LOGS", params) return response.get("result", {"logs": []}) except Exception as e: logging.error(f"Error querying logs: {e}") return {"error": str(e)} ``` ## 调试技巧 ### 调试编辑器扩展 1. **启用 Cocos Creator 开发者工具** 在 Cocos Creator 中,按下 `F12` 或选择 `开发者 -> 开发者工具` 打开 Chrome DevTools。 2. **检查日志输出** 在 DevTools 的 Console 面板中查看日志输出。你也可以添加 `console.debug()` 语句来追踪代码执行。 ```typescript // 添加调试日志 console.debug('LogBridge: Received command:', commandType, params); ``` 3. **重新加载扩展** 修改代码后,在 `扩展管理器` 中重新加载扩展,或重启 Cocos Creator。 ### 调试 Python 服务器 1. **启用详细日志** 在 `server.py` 中增加日志输出级别: ```python import logging logging.basicConfig(level=logging.DEBUG) ``` 2. **手动测试 TCP 连接** 使用 `telnet` 或其他 TCP 客户端工具测试连接: ```bash telnet localhost 6400 ``` 然后输入: ``` {"type":"ping","params":{}} ``` 应该收到: ``` {"status":"success","result":{"message":"pong"}} ``` 3. **添加断点调试** 使用 VSCode 或 PyCharm 添加断点,调试 Python 代码: ```python # 在 VSCode 中,你可以在代码中添加以下行来触发断点 import debugpy; debugpy.breakpoint() ``` ## 扩展功能 ### 添加新的命令类型 要添加新的命令类型,需要同时修改 TypeScript 和 Python 代码。 #### 1. 在 LogBridge.ts 中添加新的命令处理函数: ```typescript /** * 处理获取场景信息的命令 */ private async handleGetSceneInfo(params: any): Promise<any> { try { // 使用 Editor API 获取场景信息 const sceneInfo = { activeSceneName: Editor.Scene?.currentScene?.name || '', nodeCount: Editor.Scene?.currentScene?.children.length || 0, // 其他场景信息... }; return sceneInfo; } catch (error: any) { console.error('Error getting scene info:', error); throw error; } } ``` #### 2. 在 setupCommandHandlers 方法中注册命令: ```typescript private setupCommandHandlers() { // 现有命令 this.commandHandlers.set('QUERY_LOGS', this.handleQueryLogs.bind(this)); this.commandHandlers.set('CLEAR_LOGS', this.handleClearLogs.bind(this)); // 添加新命令 this.commandHandlers.set('GET_SCENE_INFO', this.handleGetSceneInfo.bind(this)); } ``` #### 3. 在 Python 服务器中添加相应处理器: ```python async def handle_get_scene_info(self, params): """处理获取场景信息的命令""" try: response = await self.cocos_client.send_command("GET_SCENE_INFO", params) return response.get("result", {}) except Exception as e: logging.error(f"Error getting scene info: {e}") return {"error": str(e)} ``` #### 4. 更新 Python MCP 服务器命令映射: ```python def setup_command_handlers(self): """设置命令处理器映射""" self.command_handlers = { "query_logs": self.handle_query_logs, "clear_logs": self.handle_clear_logs, "connection_status": self.handle_connection_status, "get_scene_info": self.handle_get_scene_info, # 新命令 } ``` ### 改进日志查询 可以通过添加以下功能来改进日志查询: #### 1. 添加分页功能: ```typescript private async handleQueryLogs(params: any): Promise<any> { // 添加分页参数 const page = params.page || 1; const pageSize = params.page_size || 50; // 获取日志后进行分页 const allLogs = await Editor.Logger.query() || []; const startIdx = (page - 1) * pageSize; const endIdx = startIdx + pageSize; // 分页过滤 const pagedLogs = allLogs.slice(startIdx, endIdx); return { logs: pagedLogs, total: allLogs.length, page: page, pageSize: pageSize, totalPages: Math.ceil(allLogs.length / pageSize) }; } ``` #### 2. 添加时间范围过滤: ```typescript // 添加按时间范围过滤 const startTime = params.start_time || 0; const endTime = params.end_time || Date.now(); const timeFilteredLogs = filteredLogs.filter((log: LogEntry) => { return log.time >= startTime && log.time <= endTime; }); ``` #### 3. 添加高级搜索选项: ```typescript // 添加高级搜索选项 const advancedSearch = params.advanced_search || {}; const moduleFilter = advancedSearch.module; const severityFilter = advancedSearch.min_severity; // 例如: 'log', 'warn', 'error' let advancedFilteredLogs = filteredLogs; if (moduleFilter) { advancedFilteredLogs = advancedFilteredLogs.filter((log: LogEntry) => { return log.message.includes(`[${moduleFilter}]`); }); } if (severityFilter) { const severityLevels = { 'log': 0, 'warn': 1, 'error': 2 }; const minLevel = severityLevels[severityFilter] || 0; advancedFilteredLogs = advancedFilteredLogs.filter((log: LogEntry) => { const logLevel = severityLevels[log.type.toLowerCase()] || 0; return logLevel >= minLevel; }); } ``` ## 性能优化 ### 缓存日志结果 为了减少对 Editor.Logger.query() 的频繁调用,可以实现日志缓存: ```typescript private logCache: LogEntry[] = []; private lastQueryTime: number = 0; private readonly CACHE_TTL = 2000; // 缓存有效期(毫秒) private async getCachedLogs(): Promise<LogEntry[]> { const now = Date.now(); // 如果缓存有效,直接返回缓存 if (this.logCache.length > 0 && now - this.lastQueryTime < this.CACHE_TTL) { return this.logCache; } // 缓存过期,重新查询 this.logCache = await Editor.Logger.query() || []; this.lastQueryTime = now; return this.logCache; } private async handleQueryLogs(params: any): Promise<any> { try { // 使用缓存方法获取日志 const logs = await this.getCachedLogs(); // 其余过滤逻辑保持不变... } catch (error: any) { console.error('Error querying logs:', error); throw error; } } ``` ### 优化 TCP 通信 减少 TCP 通信的开销: ```typescript // 批量处理命令 private pendingCommands: Map<string, { resolve: Function, reject: Function }> = new Map(); private readonly BATCH_TIMEOUT = 50; // 毫秒 public async sendCommand(type: string, params: any): Promise<any> { return new Promise((resolve, reject) => { const commandId = generateUniqueId(); this.pendingCommands.set(commandId, { resolve, reject }); // 添加到批处理队列 if (!this.batchTimeout) { this.batchTimeout = setTimeout(() => this.processBatch(), this.BATCH_TIMEOUT); } this.commandBatch.push({ id: commandId, type, params }); }); } private async processBatch() { const batch = this.commandBatch; this.commandBatch = []; this.batchTimeout = null; // 发送批处理请求 try { const response = await this.cocosTCPClient.sendBatch(batch); // 处理响应 for (const result of response.results) { const handler = this.pendingCommands.get(result.id); if (handler) { if (result.error) { handler.reject(new Error(result.error)); } else { handler.resolve(result.data); } this.pendingCommands.delete(result.id); } } } catch (error) { // 出错时拒绝所有挂起的命令 for (const [id, handler] of this.pendingCommands.entries()) { handler.reject(error); this.pendingCommands.delete(id); } } } ``` ## 发布新版本 1. **更新版本号** 在 `package.json` 中更新版本号: ```json { "version": "1.0.1" } ``` 2. **编译代码** ```bash tsc ``` 3. **测试功能** 在 Cocos Creator 中加载扩展,确保所有功能正常工作。 4. **更新文档** 更新 README.md、USAGE.md 和其他文档,反映新功能或更改。 5. **创建 tag(可选)** ```bash git tag v1.0.1 git push origin v1.0.1 ``` 6. **发布到 npm(可选)** ```bash npm publish ``` ## 与 Cocos Creator API 交互 本扩展使用 Cocos Creator 编辑器的官方 API 与编辑器交互。主要使用的 API 包括: ### 日志 API ```typescript // 查询日志 const logs = await Editor.Logger.query(); // 清除日志 await Editor.Logger.clear(); ``` ### 消息 API ```typescript // 监听日志消息 Editor.Message.addBroadcastListener('console:log', (log) => { // 处理日志 }); // 发送命令到场景 await Editor.Message.request('scene', 'query-node', uuid); ``` ### 场景 API ```typescript // 获取当前场景 const currentScene = Editor.Scene.currentScene; // 查询节点 const node = await Editor.Message.request('scene', 'query-node', uuid); ``` 更多 API 请参考 [Cocos Creator 官方文档](https://docs.cocos.com/creator/3.8/manual/zh/editor/extension/api/)。 ## 常见问题 ### 类型定义问题 如果遇到类型定义问题,检查 `@types/editor.d.ts` 文件是否包含所需 API 的类型定义。可能需要添加更多类型定义: ```typescript // 在 @types/editor.d.ts 中添加 declare global { const Editor: { // 现有类型 Logger: { ... }, Message: { ... }, // 新增类型 Scene: { callSceneScript(name: string, method: string, ...args: any[]): Promise<any>; currentScene: { name: string; children: any[]; } } }; } ``` ### 通信问题 如果 TCP 通信出现问题: 1. 确保端口没有被占用 2. 检查防火墙设置 3. 尝试使用不同的端口(需要同时修改编辑器扩展和 Python 服务器) ### 兼容性问题 不同版本的 Cocos Creator 可能有 API 差异。如果你的扩展需要支持多个版本的 Cocos Creator,请考虑使用条件逻辑: ```typescript // 检查 Creator 版本 const version = Editor.App.version; if (version.startsWith('3.8.')) { // 适用于 3.8.x 的代码 } else if (version.startsWith('3.7.')) { // 适用于 3.7.x 的代码 } else { console.warn(`Unsupported Cocos Creator version: ${version}`); } ``` ## 高级功能开发示例 ### 添加场景节点查询功能 扩展功能,允许查询场景中的节点: ```typescript /** * 处理查询场景节点的命令 */ private async handleQuerySceneNodes(params: any): Promise<any> { try { const uuid = params.uuid; // 节点UUID,如果为空则查询根节点 let result: any; if (uuid) { // 查询特定节点 result = await Editor.Message.request('scene', 'query-node', uuid); } else { // 查询根节点及其子节点 const sceneUuid = await Editor.Message.request('scene', 'query-current-scene'); result = await Editor.Message.request('scene', 'query-node', sceneUuid); } // 转换为更友好的格式 return { node: { uuid: result.uuid, name: result.name, children: result.children.map(child => ({ uuid: child.uuid, name: child.name })), components: result.components.map(comp => ({ uuid: comp.uuid, name: comp.__type__ })) } }; } catch (error: any) { console.error('Error querying scene nodes:', error); throw error; } } ``` ### 添加资源管理功能 扩展功能,允许查询项目资源: ```typescript /** * 处理查询项目资源的命令 */ private async handleQueryAssets(params: any): Promise<any> { try { const type = params.type; // 资源类型,如 'texture', 'audio', 'prefab' 等 const path = params.path; // 资源路径,为空则查询所有 // 查询资源 const assets = await Editor.Message.request('asset-db', 'query-assets', { type: type, pattern: path ? `${path}/**/*` : '**/*' }); return { assets: assets.map(asset => ({ uuid: asset.uuid, name: asset.name, path: asset.path, type: asset.type })) }; } catch (error: any) { console.error('Error querying assets:', error); throw error; } } ``` ## 未来计划 未来版本计划添加的功能: 1. **资源管理功能**:查询、导入和管理项目资源 2. **场景操作功能**:创建、修改和删除场景节点 3. **项目构建功能**:启动项目构建并获取构建结果 4. **脚本编辑功能**:创建和编辑脚本文件 5. **组件检查器**:查询和修改组件属性 ## 贡献指南 欢迎贡献代码、报告问题或提出新功能建议! 1. Fork 仓库 2. 创建功能分支 (`git checkout -b feature/amazing-feature`) 3. 提交修改 (`git commit -m 'Add amazing feature'`) 4. 推送分支 (`git push origin feature/amazing-feature`) 5. 创建 Pull Request 请确保你的代码遵循项目的编码规范,并添加适当的测试和文档。