Skip to main content
Glama

Office MCP Server

by walkingzzzy
ToolExecutionEngine.ts9.05 kB
import MCPClient from './MCPClient'; import MCPFunctionConverter from './MCPFunctionConverter'; import logger from '../utils/logger'; export interface ToolExecutionResult { success: boolean; result?: any; error?: string; executionTime: number; toolName: string; } export interface ToolExecutionContext { conversationId?: string; userId?: string; sessionId?: string; metadata?: Record<string, any>; } export class ToolExecutionEngine { private mcpClient: any; private converter: MCPFunctionConverter; private executionHistory = new Map<string, ToolExecutionResult[]>(); constructor(mcpClient: any, converter: MCPFunctionConverter) { this.mcpClient = mcpClient; this.converter = converter; logger.info('工具执行引擎初始化完成'); } /** * 执行工具 */ async executeTool( toolName: string, args: any, context?: ToolExecutionContext ): Promise<ToolExecutionResult> { const startTime = Date.now(); try { // 验证工具是否存在 const convertedFunction = this.converter.getConvertedFunction(toolName); if (!convertedFunction) { throw new Error(`工具 ${toolName} 不存在`); } // 验证参数 const validation = this.converter.validateFunctionArgs(toolName, args); if (!validation.valid) { throw new Error(`参数验证失败: ${validation.errors.join(', ')}`); } logger.info('开始执行工具', { toolName, args, context }); // 执行MCP工具 const result = await this.mcpClient.callTool(toolName, args); const executionTime = Date.now() - startTime; const executionResult: ToolExecutionResult = { success: true, result: result.content, executionTime, toolName }; // 记录执行历史 this.recordExecution(context?.conversationId || 'default', executionResult); logger.info('工具执行成功', { toolName, executionTime, resultSize: JSON.stringify(result.content).length }); return executionResult; } catch (error: any) { const executionTime = Date.now() - startTime; const executionResult: ToolExecutionResult = { success: false, error: error.message, executionTime, toolName }; // 记录执行历史 this.recordExecution(context?.conversationId || 'default', executionResult); logger.error('工具执行失败', { toolName, error: error.message, executionTime }); return executionResult; } } /** * 批量执行工具 */ async executeTools( toolCalls: Array<{ name: string; args: any }>, context?: ToolExecutionContext ): Promise<ToolExecutionResult[]> { const results: ToolExecutionResult[] = []; for (const toolCall of toolCalls) { const result = await this.executeTool(toolCall.name, toolCall.args, context); results.push(result); // 如果有工具执行失败,可以选择继续或停止 if (!result.success) { logger.warn('工具执行失败,继续执行其他工具', { failedTool: toolCall.name, error: result.error }); } } return results; } /** * 并行执行工具 */ async executeToolsParallel( toolCalls: Array<{ name: string; args: any }>, context?: ToolExecutionContext ): Promise<ToolExecutionResult[]> { const promises = toolCalls.map(toolCall => this.executeTool(toolCall.name, toolCall.args, context) ); return Promise.all(promises); } /** * 获取可用工具列表 */ getAvailableTools(): Array<{ name: string; description: string; category: string; }> { return this.converter.getAllConvertedFunctions().map(func => ({ name: func.original.name, description: func.original.description, category: func.original.name.split('_')[0] })); } /** * 搜索工具 */ searchTools(query: string): Array<{ name: string; description: string; relevance: number; }> { const tools = this.converter.searchTools(query); return tools.map(tool => ({ name: tool.original.name, description: tool.original.description, relevance: this.calculateRelevance(query, tool.original) })).sort((a, b) => b.relevance - a.relevance); } /** * 获取工具详情 */ getToolDetails(toolName: string): { name: string; description: string; parameters: any; examples?: any[]; } | null { const convertedFunction = this.converter.getConvertedFunction(toolName); if (!convertedFunction) return null; return { name: convertedFunction.original.name, description: convertedFunction.original.description, parameters: convertedFunction.original.inputSchema, examples: this.generateExamples(convertedFunction.original) }; } /** * 获取执行历史 */ getExecutionHistory(conversationId: string = 'default'): ToolExecutionResult[] { return this.executionHistory.get(conversationId) || []; } /** * 获取执行统计 */ getExecutionStats(conversationId?: string): { totalExecutions: number; successfulExecutions: number; failedExecutions: number; averageExecutionTime: number; mostUsedTools: Array<{ name: string; count: number }>; } { let allResults: ToolExecutionResult[] = []; if (conversationId) { allResults = this.executionHistory.get(conversationId) || []; } else { for (const results of this.executionHistory.values()) { allResults.push(...results); } } const totalExecutions = allResults.length; const successfulExecutions = allResults.filter(r => r.success).length; const failedExecutions = totalExecutions - successfulExecutions; const averageExecutionTime = totalExecutions > 0 ? allResults.reduce((sum, r) => sum + r.executionTime, 0) / totalExecutions : 0; // 统计最常用的工具 const toolCounts = new Map<string, number>(); for (const result of allResults) { toolCounts.set(result.toolName, (toolCounts.get(result.toolName) || 0) + 1); } const mostUsedTools = Array.from(toolCounts.entries()) .map(([name, count]) => ({ name, count })) .sort((a, b) => b.count - a.count) .slice(0, 10); return { totalExecutions, successfulExecutions, failedExecutions, averageExecutionTime: Math.round(averageExecutionTime), mostUsedTools }; } /** * 清理执行历史 */ clearExecutionHistory(conversationId?: string): void { if (conversationId) { this.executionHistory.delete(conversationId); } else { this.executionHistory.clear(); } logger.info('清理工具执行历史', { conversationId }); } /** * 记录执行结果 */ private recordExecution(conversationId: string, result: ToolExecutionResult): void { if (!this.executionHistory.has(conversationId)) { this.executionHistory.set(conversationId, []); } const history = this.executionHistory.get(conversationId)!; history.push(result); // 限制历史记录数量 const maxHistory = 100; if (history.length > maxHistory) { history.splice(0, history.length - maxHistory); } } /** * 计算相关性分数 */ private calculateRelevance(query: string, tool: any): number { const queryLower = query.toLowerCase(); let score = 0; // 名称匹配 if (tool.name.toLowerCase().includes(queryLower)) { score += 10; } // 描述匹配 const descriptionWords = tool.description.toLowerCase().split(' '); const queryWords = queryLower.split(' '); for (const queryWord of queryWords) { for (const descWord of descriptionWords) { if (descWord.includes(queryWord)) { score += 1; } } } return score; } /** * 生成工具使用示例 */ private generateExamples(tool: any): any[] { const examples: any[] = []; // 基于参数schema生成示例 if (tool.inputSchema && tool.inputSchema.properties) { const example: any = {}; for (const [key, prop] of Object.entries(tool.inputSchema.properties)) { const propSchema = prop as any; example[key] = this.generateExampleValue(propSchema); } examples.push({ description: `基本使用示例`, parameters: example }); } return examples; } /** * 生成示例值 */ private generateExampleValue(schema: any): any { switch (schema.type) { case 'string': return schema.example || 'example_string'; case 'number': return schema.example || 42; case 'boolean': return schema.example || true; case 'array': return schema.example || []; case 'object': return schema.example || {}; default: return null; } } } export default ToolExecutionEngine;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/walkingzzzy/office-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server