All files / src/llm-orchestration/action-handlers mcp-tool-action.handler.ts

100% Statements 15/15
100% Branches 3/3
100% Functions 4/4
100% Lines 15/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 9219x                                 19x   13x     13x 13x 13x 13x   13x       1x               1x                     10x 10x           8x   8x                           2x                                    
import { Logger } from '@nestjs/common';
import { ActionHandler } from './action-handler.interface';
import {
  ActionExecutionResult,
  PlanExecutionContext,
  ToolMetadata,
} from '../llm-orchestration.interfaces';
import { McpService } from '../../mcp/mcp.service';
 
/**
 * Generic action handler for individual MCP tools.
 * Each MCP tool is surfaced as its own native tool definition with the name
 * pattern `{server_name}__{tool_name}` (double underscore).
 *
 * This handler is created dynamically for each active MCP tool discovered
 * via McpService.
 */
export class McpToolActionHandler implements ActionHandler {
  readonly toolName: string;
  private readonly logger = new Logger(McpToolActionHandler.name);
 
  constructor(
    private readonly serverName: string,
    private readonly toolNameRaw: string,
    private readonly mcpService: McpService,
    private readonly toolDescription: string,
  ) {
    this.toolName = `${serverName}__${toolNameRaw}`;
  }
 
  getMetadata(): ToolMetadata {
    return {
      name: this.toolName,
      description: this.toolDescription,
      arguments: [],
    };
  }
 
  getDefinition(): string {
    return `## ${this.toolName}
 
${this.toolDescription}
 
Executed via MCP server "${this.serverName}", tool "${this.toolNameRaw}".`;
  }
 
  async execute(
    args: Record<string, any>,
    _context: PlanExecutionContext,
  ): Promise<ActionExecutionResult> {
    try {
      const { stdout, stderr } = await this.mcpService.executeMcpTool(
        this.serverName,
        this.toolNameRaw,
        args,
      );
 
      const unifiedOutput = stdout || stderr || '(Tool produced no output)';
 
      return {
        status: 'SUCCESS',
        summary: `MCP tool "${this.serverName}.${this.toolNameRaw}" executed.`,
        persisted_args: {
          server_name: this.serverName,
          tool_name: this.toolNameRaw,
          arguments: JSON.stringify(args),
          plain: unifiedOutput,
        },
        execution_log: {
          output: unifiedOutput,
        },
      };
    } catch (error) {
      return {
        status: 'FAILURE',
        summary: `MCP tool "${this.serverName}.${this.toolNameRaw}" failed.`,
        error_message: error.message,
        persisted_args: {
          server_name: this.serverName,
          tool_name: this.toolNameRaw,
          arguments: JSON.stringify(args),
          plain: `Error: ${error.message}`,
        },
        execution_log: {
          output: '',
          error_message: error.message,
        },
      };
    }
  }
}