UNPKG

@gohcltech/bitbucket-mcp

Version:

Bitbucket integration for Claude via Model Context Protocol

221 lines 8.34 kB
/** * @fileoverview Abstract base class for consolidated tools with action routing. * * This module provides the foundation for all v2.1 consolidated tools, * implementing the action-based consolidation pattern where multiple operations * are grouped into a single tool with an `action` parameter. * * Key features: * - Action routing to appropriate handlers * - Automatic parameter validation per action * - Consistent error handling and logging * - Type-safe with TypeScript generics * - Extends BaseTool for all inherited functionality */ import { BaseTool } from './base-tool.js'; /** * Abstract base class for consolidated tools with action-based routing. * * Consolidated tools group related operations under a single tool with an `action` * parameter that determines which handler to invoke. This reduces the number of tools * while maintaining all functionality. * * Example: Instead of `list_issues`, `get_issue`, `create_issue`, etc., we have * a single `manage_issues` tool with action='list', action='get', action='create', etc. * * @template TAction - Union type of all valid action values (e.g., 'list' | 'get' | 'create') * * @example * ```typescript * type IssueAction = 'list' | 'get' | 'create' | 'update' | 'delete'; * * class ManageIssuesTool extends ConsolidatedBaseTool<IssueAction> { * protected getActionHandler(action: IssueAction): ToolHandler | null { * const handlers: Record<IssueAction, ToolHandler> = { * list: (args) => this.handleListIssues(args), * get: (args) => this.handleGetIssue(args), * create: (args) => this.handleCreateIssue(args), * update: (args) => this.handleUpdateIssue(args), * delete: (args) => this.handleDeleteIssue(args), * }; * return handlers[action] || null; * } * * protected validateActionParams(action: IssueAction, args: any): void { * switch (action) { * case 'list': * this.validateRequired(args, ['workspaceSlug', 'repoSlug'], 'list_issues'); * break; * case 'get': * this.validateRequired(args, ['workspaceSlug', 'repoSlug', 'issueId'], 'get_issue'); * break; * // ... etc * } * } * } * ``` */ export class ConsolidatedBaseTool extends BaseTool { /** * Gets the handlers for this consolidated tool. * * Returns a map with the tool name as key, delegating to action-based routing. * This implementation automatically wraps action handlers so they work with the * MCP protocol's tool name-based invocation system. * * @returns Map from tool name to the main consolidated handler */ getHandlers() { const handlers = new Map(); const tools = this.getTools(); if (tools.length === 0) { throw new Error('Consolidated tool must define at least one tool in getTools()'); } const toolName = tools[0].name; // Create the main handler that dispatches to action-based routing const mainHandler = async (args) => { const action = args.action; if (!action) { return this.createErrorResponse(new Error('Missing required parameter: action'), toolName); } return this.handleAction(toolName, action, args); }; handlers.set(toolName, mainHandler); return handlers; } /** * Main handler that routes to action-specific handlers. * * This is the primary entry point for tool execution. It: * 1. Logs the action invocation * 2. Looks up the appropriate handler for the action * 3. Validates action-specific parameters * 4. Executes the handler with error handling * * @param toolName - The name of the consolidated tool (e.g., 'manage_issues') * @param action - The action to perform (e.g., 'list', 'get', 'create') * @param args - Tool arguments including the action and all action-specific parameters * @returns Promise resolving to a formatted tool response * * @example * ```typescript * const response = await tool.handleAction('manage_issues', 'list', { * action: 'list', * workspaceSlug: 'my-workspace', * repoSlug: 'my-repo' * }); * ``` */ async handleAction(toolName, action, args) { // Log the action invocation for debugging and monitoring this.logActionInvocation(toolName, action, args); // Get the handler function for this action const handler = this.getActionHandler(action); if (!handler) { return this.createUnknownActionError(action, toolName); } // Validate parameters required for this specific action try { this.validateActionParams(action, args); } catch (error) { return this.createErrorResponse(error, `${toolName}.${action}`); } // Execute the handler with error handling try { return await handler(args); } catch (error) { return this.createErrorResponse(error, `${toolName}.${action}`); } } /** * Validates that required parameters are present in the arguments. * * Helper method for parameter validation. Checks that all specified fields * are present in args and are not null or undefined. * * @param args - The arguments object to validate * @param fields - Array of required field names * @param operation - Name of the operation (for error messages) * @throws {Error} If any required fields are missing * * @example * ```typescript * this.validateRequired( * args, * ['workspaceSlug', 'repoSlug', 'issueId'], * 'get_issue' * ); * // Throws: "Missing required parameter(s) for get_issue: issueId" * ``` */ validateRequired(args, fields, operation) { const missing = fields.filter(f => args[f] === undefined || args[f] === null); if (missing.length > 0) { throw new Error(`Missing required parameter(s) for ${operation}: ${missing.join(', ')}`); } } /** * Validates that a value is one of the allowed enum values. * * Helper method for validating enum-like parameters. * * @template T - The type of the enum values * @param value - The value to validate * @param enumValues - Array of allowed values * @param paramName - Name of the parameter (for error messages) * @throws {Error} If value is not in enumValues * * @example * ```typescript * this.validateEnum( * args.action, * ['list', 'get', 'create'] as const, * 'action' * ); * ``` */ validateEnum(value, enumValues, paramName) { if (!enumValues.includes(value)) { throw new Error(`Invalid value for ${paramName}: ${value}. Must be one of: ${enumValues.join(', ')}`); } } /** * Creates an error response for an unknown action. * * Called when an action is not recognized by the tool. * * @param action - The unknown action name * @param toolName - The name of the tool * @returns Formatted error response * * @example * ```typescript * return this.createUnknownActionError('invalid_action', 'manage_issues'); * // Returns error response with message about unknown action * ``` */ createUnknownActionError(action, toolName) { return this.createErrorResponse(new Error(`Unknown action '${action}' for tool '${toolName}'`), toolName); } /** * Logs the invocation of an action for monitoring and debugging. * * Called at the start of handleAction to record which action was invoked. * Includes relevant context from args like workspace and repository. * * @param toolName - The name of the tool * @param action - The action being performed * @param args - The arguments passed to the tool */ logActionInvocation(toolName, action, args) { this.logger.debug(`Executing ${toolName}.${action}`, { tool: toolName, action, workspace: args.workspaceSlug, repo: args.repoSlug, }); } } //# sourceMappingURL=consolidated-base-tool.js.map