/** * MCP Tool Executor * * 도구 실행 + 훅 + 에러 처리 통합 */ import type { ManduConfig } from "@mandujs/core"; import { mcpToolRegistry } from "../registry/mcp-tool-registry.js"; import { mcpHookRegistry, type McpToolContext } from "../hooks/mcp-hooks.js"; import { createToolResponse, logToolError, type McpToolResponse } from "./error-handler.js"; /** * Tool Executor 옵션 */ export interface ToolExecutorOptions { /** 프로젝트 루트 */ projectRoot: string; /** Mandu 설정 */ config?: ManduConfig; /** 활동 모니터 로깅 함수 */ logTool?: (name: string, args?: Record, result?: unknown, error?: string) => void; /** 결과 로깅 함수 */ logResult?: (name: string, result: unknown) => void; } /** * 도구 실행 결과 */ export interface ExecutionResult { success: boolean; response: McpToolResponse; duration: number; toolName: string; } /** * 도구 실행기 * * DNA 기능들(플러그인, 훅, 에러 처리)을 통합한 도구 실행 */ export class ToolExecutor { private options: ToolExecutorOptions; constructor(options: ToolExecutorOptions) { this.options = options; } /** * 도구 실행 * * @param name - 도구 이름 * @param args - 도구 인자 * @returns 실행 결과 */ async execute(name: string, args: Record = {}): Promise { const startTime = Date.now(); // 도구 조회 const tool = mcpToolRegistry.get(name); if (!tool) { const response = createToolResponse(name, null, new Error(`Unknown tool: ${name}`)); return { success: false, response, duration: Date.now() - startTime, toolName: name, }; } // 실행 컨텍스트 생성 const ctx: McpToolContext = { toolName: name, args, projectRoot: this.options.projectRoot, config: this.options.config, startTime, }; try { // Pre-Tool 훅 실행 await mcpHookRegistry.runPreHooks(ctx); // 활동 로깅 (호출) this.options.logTool?.(name, args); // 도구 실행 const result = await tool.execute(args); // 활동 로깅 (결과) this.options.logResult?.(name, result); // Post-Tool 훅 실행 await mcpHookRegistry.runPostHooks(ctx, result); const response = createToolResponse(name, result); return { success: true, response, duration: Date.now() - startTime, toolName: name, }; } catch (error) { // 에러 로깅 const errorMsg = error instanceof Error ? error.message : String(error); this.options.logTool?.(name, args, null, errorMsg); logToolError(name, error, args); // Post-Tool 훅 실행 (에러와 함께) await mcpHookRegistry.runPostHooks(ctx, null, error); const response = createToolResponse(name, null, error); return { success: false, response, duration: Date.now() - startTime, toolName: name, }; } } /** * 설정 업데이트 */ updateConfig(config: ManduConfig): void { this.options.config = config; } /** * 도구 존재 여부 확인 */ hasTool(name: string): boolean { return mcpToolRegistry.has(name); } /** * 등록된 도구 목록 */ getToolNames(): string[] { return mcpToolRegistry.names; } } /** * Tool Executor 팩토리 함수 */ export function createToolExecutor(options: ToolExecutorOptions): ToolExecutor { return new ToolExecutor(options); }