修复了一些挂载bug,将面板技术从纯html变更成了Vue3,界面更有好些,后期扩展更方便。增加了工具配置功能,可以自行选择需要使用的工具。
This commit is contained in:
176
source/main.ts
176
source/main.ts
@@ -1,8 +1,10 @@
|
||||
import { MCPServer } from './mcp-server';
|
||||
import { readSettings, saveSettings } from './settings';
|
||||
import { MCPServerSettings } from './types';
|
||||
import { ToolManager } from './tools/tool-manager';
|
||||
|
||||
let mcpServer: MCPServer | null = null;
|
||||
let toolManager: ToolManager;
|
||||
|
||||
/**
|
||||
* @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');
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @en Start the MCP server
|
||||
* @zh 启动 MCP 服务器
|
||||
*/
|
||||
async startServer() {
|
||||
if (mcpServer) {
|
||||
// 确保使用最新的工具配置
|
||||
const enabledTools = toolManager.getEnabledTools();
|
||||
mcpServer.updateEnabledTools(enabledTools);
|
||||
await mcpServer.start();
|
||||
} else {
|
||||
console.warn('[MCP插件] mcpServer 未初始化');
|
||||
@@ -46,7 +53,12 @@ export const methods: { [key: string]: (...any: any) => any } = {
|
||||
* @zh 获取服务器状态
|
||||
*/
|
||||
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() {
|
||||
return mcpServer ? mcpServer.getAvailableTools() : [];
|
||||
},
|
||||
|
||||
getFilteredToolsList() {
|
||||
if (!mcpServer) return [];
|
||||
|
||||
// 获取当前启用的工具
|
||||
const enabledTools = toolManager.getEnabledTools();
|
||||
|
||||
// 更新MCP服务器的启用工具列表
|
||||
mcpServer.updateEnabledTools(enabledTools);
|
||||
|
||||
return mcpServer.getFilteredTools(enabledTools);
|
||||
},
|
||||
/**
|
||||
* @en Get server settings
|
||||
* @zh 获取服务器设置
|
||||
*/
|
||||
getServerSettings() {
|
||||
async getServerSettings() {
|
||||
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 扩展启动时触发的方法
|
||||
*/
|
||||
export function load() {
|
||||
console.log('[MCP Plugin] Loading MCP server plugin...');
|
||||
try {
|
||||
const settings = readSettings();
|
||||
console.log('[MCP Plugin] Settings loaded:', settings);
|
||||
mcpServer = new MCPServer(settings);
|
||||
|
||||
// 如果设置了自动启动,则启动服务器
|
||||
if (settings.autoStart) {
|
||||
console.log('[MCP Plugin] Auto-starting MCP server...');
|
||||
mcpServer.start().catch(error => {
|
||||
console.error('[MCP Plugin] Failed to auto-start server:', error);
|
||||
});
|
||||
} 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');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[MCP Plugin] Failed to load MCP server:', error);
|
||||
console.log('Cocos MCP Server extension loaded');
|
||||
|
||||
// 初始化工具管理器
|
||||
toolManager = new ToolManager();
|
||||
|
||||
// 读取设置
|
||||
const settings = readSettings();
|
||||
mcpServer = new MCPServer(settings);
|
||||
|
||||
// 初始化MCP服务器的工具列表
|
||||
const enabledTools = toolManager.getEnabledTools();
|
||||
mcpServer.updateEnabledTools(enabledTools);
|
||||
|
||||
// 如果设置了自动启动,则启动服务器
|
||||
if (settings.autoStart) {
|
||||
mcpServer.start().catch(err => {
|
||||
console.error('Failed to auto-start MCP server:', err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ export class MCPServer {
|
||||
private clients: Map<string, MCPClient> = new Map();
|
||||
private tools: Record<string, any> = {};
|
||||
private toolsList: ToolDefinition[] = [];
|
||||
private enabledTools: any[] = []; // 存储启用的工具列表
|
||||
|
||||
constructor(settings: MCPServerSettings) {
|
||||
this.settings = settings;
|
||||
@@ -90,17 +91,47 @@ export class MCPServer {
|
||||
private setupTools(): void {
|
||||
this.toolsList = [];
|
||||
|
||||
for (const [category, toolSet] of Object.entries(this.tools)) {
|
||||
const tools = toolSet.getTools();
|
||||
for (const tool of tools) {
|
||||
this.toolsList.push({
|
||||
name: `${category}_${tool.name}`,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema
|
||||
});
|
||||
// 如果没有启用工具配置,返回所有工具
|
||||
if (!this.enabledTools || this.enabledTools.length === 0) {
|
||||
for (const [category, toolSet] of Object.entries(this.tools)) {
|
||||
const tools = toolSet.getTools();
|
||||
for (const tool of tools) {
|
||||
this.toolsList.push({
|
||||
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> {
|
||||
@@ -122,6 +153,12 @@ export class MCPServer {
|
||||
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 {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
@@ -1,229 +1,386 @@
|
||||
import { readSettings } from '../../settings';
|
||||
/* eslint-disable vue/one-component-per-file */
|
||||
|
||||
import { readFileSync } from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import { createApp, App, defineComponent, ref, computed, onMounted, watch, nextTick } from 'vue';
|
||||
|
||||
/**
|
||||
* @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码
|
||||
* @en You can add the code below if you want compatibility with versions prior to 3.3
|
||||
*/
|
||||
// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options }
|
||||
const panelDataMap = new WeakMap<any, App>();
|
||||
|
||||
// 定义工具配置接口
|
||||
interface ToolConfig {
|
||||
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({
|
||||
listeners: {
|
||||
show() { console.log('MCP Server panel shown'); },
|
||||
hide() { console.log('MCP Server panel hidden'); }
|
||||
show() {
|
||||
console.log('[MCP Panel] Panel shown');
|
||||
},
|
||||
hide() {
|
||||
console.log('[MCP Panel] Panel hidden');
|
||||
},
|
||||
},
|
||||
template: readFileSync(join(__dirname, '../../../static/template/default/index.html'), 'utf-8'),
|
||||
style: readFileSync(join(__dirname, '../../../static/style/default/index.css'), 'utf-8'),
|
||||
$: {
|
||||
app: '#app',
|
||||
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() {
|
||||
Editor.Message.request('cocos-mcp-server', 'get-server-settings').then((settings) => {
|
||||
this.settings = settings;
|
||||
this.originalSettings = JSON.stringify(settings);
|
||||
// 本地化label赋值
|
||||
this.$.panelTitle.innerText = Editor.I18n.t('cocos-mcp-server.panel_title');
|
||||
this.$.serverStatusLabel.innerText = Editor.I18n.t('cocos-mcp-server.server_status');
|
||||
this.$.serverStatusLabelProp.innerText = Editor.I18n.t('cocos-mcp-server.server_status');
|
||||
this.$.connectedLabel.innerText = Editor.I18n.t('cocos-mcp-server.connected');
|
||||
this.$.settingsLabel.innerText = Editor.I18n.t('cocos-mcp-server.settings');
|
||||
this.$.portLabel.innerText = Editor.I18n.t('cocos-mcp-server.port');
|
||||
this.$.autoStartLabel.innerText = Editor.I18n.t('cocos-mcp-server.auto_start');
|
||||
this.$.debugLogLabel.innerText = Editor.I18n.t('cocos-mcp-server.debug_log');
|
||||
this.$.maxConnectionsLabel.innerText = Editor.I18n.t('cocos-mcp-server.max_connections');
|
||||
this.$.connectionInfoLabel.innerText = Editor.I18n.t('cocos-mcp-server.connection_info');
|
||||
this.$.httpUrlLabel.innerText = Editor.I18n.t('cocos-mcp-server.http_url');
|
||||
this.$.copyBtn.innerText = Editor.I18n.t('cocos-mcp-server.copy');
|
||||
this.$.saveSettingsBtn.innerText = Editor.I18n.t('cocos-mcp-server.save_settings');
|
||||
// 动态内容初始化
|
||||
this.$.serverStatusValue.innerText = '';
|
||||
this.$.connectedClients.innerText = '';
|
||||
this.$.toggleServerBtn.innerText = '';
|
||||
this.$.httpUrlInput.value = '';
|
||||
// 绑定按钮事件
|
||||
this.$.toggleServerBtn.addEventListener('confirm', this.toggleServer.bind(this));
|
||||
this.$.saveSettingsBtn.addEventListener('confirm', this.saveSettings.bind(this));
|
||||
this.$.copyBtn.addEventListener('confirm', this.copyUrl.bind(this));
|
||||
// 延迟绑定事件,确保 UI 组件已渲染
|
||||
setTimeout(() => {
|
||||
this.bindSettingsEvents();
|
||||
}, 100);
|
||||
// Set up periodic status updates
|
||||
(this as any).statusInterval = setInterval(() => {
|
||||
(this as any).updateServerStatus();
|
||||
}, 2000);
|
||||
// 不再自动启动服务器,用户点击才启动
|
||||
(this as any).updateServerStatus();
|
||||
});
|
||||
},
|
||||
beforeClose() {
|
||||
if ((this as any).statusInterval) {
|
||||
clearInterval((this as any).statusInterval);
|
||||
if (this.$.app) {
|
||||
const app = createApp({});
|
||||
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-');
|
||||
|
||||
// 创建主应用组件
|
||||
app.component('McpServerApp', defineComponent({
|
||||
setup() {
|
||||
// 响应式数据
|
||||
const activeTab = ref('server');
|
||||
const serverRunning = ref(false);
|
||||
const serverStatus = ref('已停止');
|
||||
const connectedClients = ref(0);
|
||||
const httpUrl = ref('');
|
||||
const isProcessing = ref(false);
|
||||
|
||||
const settings = ref<ServerSettings>({
|
||||
port: 3000,
|
||||
autoStart: false,
|
||||
debugLog: false,
|
||||
maxConnections: 10
|
||||
});
|
||||
|
||||
const availableTools = ref<ToolConfig[]>([]);
|
||||
const toolCategories = ref<string[]>([]);
|
||||
|
||||
|
||||
|
||||
// 计算属性
|
||||
const statusClass = computed(() => ({
|
||||
'status-running': serverRunning.value,
|
||||
'status-stopped': !serverRunning.value
|
||||
}));
|
||||
|
||||
const totalTools = computed(() => availableTools.value.length);
|
||||
const enabledTools = computed(() => availableTools.value.filter(t => t.enabled).length);
|
||||
const disabledTools = computed(() => totalTools.value - enabledTools.value);
|
||||
|
||||
|
||||
|
||||
const settingsChanged = ref(false);
|
||||
|
||||
// 方法
|
||||
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() {
|
||||
// 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);
|
||||
});
|
||||
585
source/panels/tool-manager/index.ts
Normal file
585
source/panels/tool-manager/index.ts
Normal 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);
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { MCPServerSettings } from './types';
|
||||
import { MCPServerSettings, ToolManagerSettings, ToolConfiguration, ToolConfig } from './types';
|
||||
|
||||
const DEFAULT_SETTINGS: MCPServerSettings = {
|
||||
port: 3000,
|
||||
@@ -10,10 +10,20 @@ const DEFAULT_SETTINGS: MCPServerSettings = {
|
||||
maxConnections: 10
|
||||
};
|
||||
|
||||
const DEFAULT_TOOL_MANAGER_SETTINGS: ToolManagerSettings = {
|
||||
configurations: [],
|
||||
currentConfigId: '',
|
||||
maxConfigSlots: 5
|
||||
};
|
||||
|
||||
function getSettingsPath(): string {
|
||||
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 {
|
||||
const settingsDir = path.dirname(getSettingsPath());
|
||||
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 };
|
||||
425
source/tools/tool-manager.ts
Normal file
425
source/tools/tool-manager.ts
Normal 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`);
|
||||
}
|
||||
}
|
||||
@@ -124,4 +124,33 @@ export interface MCPClient {
|
||||
export interface ToolExecutor {
|
||||
getTools(): ToolDefinition[];
|
||||
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[];
|
||||
}
|
||||
Reference in New Issue
Block a user