UNPKG

openai-assistants-mcp

Version:

OpenAI Assistants MCP Server - A Model Context Protocol server providing OpenAI Assistants API tools and resources. Features comprehensive assistant management, thread operations, message handling, and run execution with seamless stdio transport for Claud

307 lines 11.3 kB
/** * Run Tool Handlers - Handlers for all run-related operations * * This file contains handlers for: * - run-create: Start an assistant run on a thread * - run-list: List runs for a thread with pagination * - run-get: Get details of a specific run * - run-update: Update run metadata * - run-cancel: Cancel a running assistant * - run-submit-tool-outputs: Submit tool call results * * Each handler implements the BaseToolHandler interface and provides * specific validation and execution logic for run operations. */ import { BaseToolHandler } from './base-tool-handler.js'; import { MCPError, ErrorCodes } from '../../types/index.js'; import { validateOpenAIId, validateModel, validateMetadata, validatePaginationParams, validateArray } from '../../validation/index.js'; /** * Handler for creating new runs */ export class RunCreateHandler extends BaseToolHandler { getToolName() { return 'run-create'; } getCategory() { return 'run'; } validate(args) { // Validate required thread ID const threadIdValidation = validateOpenAIId(args?.thread_id, 'thread', 'thread_id'); if (!threadIdValidation.isValid) { return threadIdValidation; } // Validate required assistant ID const assistantIdValidation = validateOpenAIId(args?.assistant_id, 'assistant', 'assistant_id'); if (!assistantIdValidation.isValid) { return assistantIdValidation; } // Validate model if provided if (args?.model) { const modelValidation = validateModel(args.model); if (!modelValidation.isValid) { return modelValidation; } } // Validate metadata if provided if (args?.metadata !== undefined) { const metadataValidation = validateMetadata(args.metadata); if (!metadataValidation.isValid) { return metadataValidation; } } return { isValid: true }; } async execute(args) { try { const { thread_id, ...runData } = args; // Use generic request type const genericRequest = { assistantId: runData.assistant_id, model: runData.model, instructions: runData.instructions, additionalInstructions: runData.additional_instructions, tools: runData.tools, metadata: runData.metadata, providerOptions: runData.providerOptions }; return await this.context.provider.createRun(thread_id, genericRequest); } catch (error) { throw this.createExecutionError(`Failed to create run: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } } /** * Handler for listing runs */ export class RunListHandler extends BaseToolHandler { getToolName() { return 'run-list'; } getCategory() { return 'run'; } validate(args) { // Validate thread ID const threadIdValidation = validateOpenAIId(args?.thread_id, 'thread', 'thread_id'); if (!threadIdValidation.isValid) { return threadIdValidation; } const { thread_id, ...listData } = args; // Validate pagination parameters const paginationValidation = validatePaginationParams(listData); if (!paginationValidation.isValid) { return paginationValidation; } return { isValid: true }; } async execute(args) { try { const { thread_id, ...listData } = args; // Use generic request type const genericRequest = { limit: listData.limit, order: listData.order, after: listData.after, before: listData.before }; return await this.context.provider.listRuns(thread_id, genericRequest); } catch (error) { throw this.createExecutionError(`Failed to list runs: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } } /** * Handler for getting run details */ export class RunGetHandler extends BaseToolHandler { getToolName() { return 'run-get'; } getCategory() { return 'run'; } validate(args) { // Validate thread ID const threadIdValidation = validateOpenAIId(args?.thread_id, 'thread', 'thread_id'); if (!threadIdValidation.isValid) { return threadIdValidation; } // Validate run ID const runIdValidation = validateOpenAIId(args?.run_id, 'run', 'run_id'); if (!runIdValidation.isValid) { return runIdValidation; } return { isValid: true }; } async execute(args) { try { return await this.context.provider.getRun(args.thread_id, args.run_id); } catch (error) { throw this.createExecutionError(`Failed to get run: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } } /** * Handler for updating runs */ export class RunUpdateHandler extends BaseToolHandler { getToolName() { return 'run-update'; } getCategory() { return 'run'; } validate(args) { // Validate thread ID const threadIdValidation = validateOpenAIId(args?.thread_id, 'thread', 'thread_id'); if (!threadIdValidation.isValid) { return threadIdValidation; } // Validate run ID const runIdValidation = validateOpenAIId(args?.run_id, 'run', 'run_id'); if (!runIdValidation.isValid) { return runIdValidation; } const { thread_id, run_id, ...updateData } = args; // Validate metadata if provided if (updateData.metadata !== undefined) { const metadataValidation = validateMetadata(updateData.metadata); if (!metadataValidation.isValid) { return metadataValidation; } } return { isValid: true }; } async execute(args) { try { const { thread_id, run_id, ...updateData } = args; // Use generic request type const genericRequest = { metadata: updateData.metadata, providerOptions: updateData.providerOptions }; return await this.context.provider.updateRun(thread_id, run_id, genericRequest); } catch (error) { throw this.createExecutionError(`Failed to update run: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } } /** * Handler for canceling runs */ export class RunCancelHandler extends BaseToolHandler { getToolName() { return 'run-cancel'; } getCategory() { return 'run'; } validate(args) { // Validate thread ID const threadIdValidation = validateOpenAIId(args?.thread_id, 'thread', 'thread_id'); if (!threadIdValidation.isValid) { return threadIdValidation; } // Validate run ID const runIdValidation = validateOpenAIId(args?.run_id, 'run', 'run_id'); if (!runIdValidation.isValid) { return runIdValidation; } return { isValid: true }; } async execute(args) { try { return await this.context.provider.cancelRun(args.thread_id, args.run_id); } catch (error) { throw this.createExecutionError(`Failed to cancel run: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } } /** * Handler for submitting tool outputs */ export class RunSubmitToolOutputsHandler extends BaseToolHandler { getToolName() { return 'run-submit-tool-outputs'; } getCategory() { return 'run'; } validate(args) { // Validate required thread ID const threadIdValidation = validateOpenAIId(args?.thread_id, 'thread', 'thread_id'); if (!threadIdValidation.isValid) { return threadIdValidation; } // Validate required run ID const runIdValidation = validateOpenAIId(args?.run_id, 'run', 'run_id'); if (!runIdValidation.isValid) { return runIdValidation; } // Validate required tool_outputs array const toolOutputsValidation = validateArray(args?.tool_outputs, 'tool_outputs', true); if (!toolOutputsValidation.isValid) { return toolOutputsValidation; } // Validate each tool output if (args?.tool_outputs && Array.isArray(args.tool_outputs)) { for (let i = 0; i < args.tool_outputs.length; i++) { const output = args.tool_outputs[i]; if (!output.tool_call_id) { return { isValid: false, error: new MCPError(ErrorCodes.INVALID_PARAMS, `Tool output at index ${i} is missing 'tool_call_id'. Each tool output must include the tool_call_id from the run's required_action. Example: {"tool_call_id": "call_abc123def456ghi789jkl012", "output": "result"}.`) }; } const toolCallIdValidation = validateOpenAIId(output.tool_call_id, 'tool_call', `tool_outputs[${i}].tool_call_id`); if (!toolCallIdValidation.isValid) { return toolCallIdValidation; } if (!output.output || typeof output.output !== 'string') { return { isValid: false, error: new MCPError(ErrorCodes.INVALID_PARAMS, `Tool output at index ${i} is missing or has invalid 'output'. Provide the function result as a string. Example: {"tool_call_id": "call_abc123def456ghi789jkl012", "output": "The calculation result is 42"}.`) }; } } } return { isValid: true }; } async execute(args) { try { const { thread_id, run_id, ...submitData } = args; // Use generic request type const genericRequest = { toolOutputs: submitData.tool_outputs?.map((output) => ({ toolCallId: output.tool_call_id, output: output.output })) || [], providerOptions: submitData.providerOptions }; return await this.context.provider.submitToolOutputs(thread_id, run_id, genericRequest); } catch (error) { throw this.createExecutionError(`Failed to submit tool outputs: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } } /** * Factory function to create all run handlers */ export function createRunHandlers(context) { return { 'run-create': new RunCreateHandler(context), 'run-list': new RunListHandler(context), 'run-get': new RunGetHandler(context), 'run-update': new RunUpdateHandler(context), 'run-cancel': new RunCancelHandler(context), 'run-submit-tool-outputs': new RunSubmitToolOutputsHandler(context) }; } //# sourceMappingURL=run-handlers.js.map