import { WorkflowExecutionPayload, WorkflowExecutionResult, WorkflowListResponse, McpConnectionStatus, } from '../../types/src/mcp'; import { HealthStatus, RequestOptions, SymbiontConfig, } from 'symbi-types'; /** * Simple interface to avoid circular dependency with SymbiontClient */ interface ClientDependency { getAuthHeaders(endpoint: string): Promise>; configuration: Readonly; } /** * Client for managing workflows and MCP operations via the Symbiont Runtime API */ export class McpClient { private client: ClientDependency; constructor(client: ClientDependency) { this.client = client; } /** * Execute a workflow with parameters * POST /workflows/execute */ async executeWorkflow( payload: WorkflowExecutionPayload ): Promise> { if (!payload) { throw new Error('Workflow execution payload is required'); } if (!payload.workflowId) { throw new Error('Workflow ID is required'); } if (!payload.parameters) { throw new Error('Workflow parameters are required'); } return this.makeRequest>('/workflows/execute', { method: 'POST', body: payload, }); } /** * Check server health status * GET /health */ async checkServerHealth(): Promise { return this.makeRequest('/health', { method: 'GET', }); } /** * List available workflows * GET /workflows */ async listWorkflows(): Promise { return this.makeRequest('/workflows', { method: 'GET', }); } /** * Get workflow execution status * GET /workflows/executions/{executionId} */ async getExecutionStatus( executionId: string ): Promise> { if (!executionId) { throw new Error('Execution ID is required'); } return this.makeRequest>( `/workflows/executions/${executionId}`, { method: 'GET', } ); } /** * Cancel a workflow execution * DELETE /workflows/executions/{executionId} */ async cancelExecution(executionId: string): Promise { if (!executionId) { throw new Error('Execution ID is required'); } await this.makeRequest(`/workflows/executions/${executionId}`, { method: 'DELETE', }); } /** * Get MCP connection status * GET /mcp/status */ async getConnectionStatus(): Promise { return this.makeRequest('/mcp/status', { method: 'GET', }); } /** * Make an HTTP request using the underlying client */ private async makeRequest( endpoint: string, options: RequestOptions ): Promise { try { // Get authentication headers from the parent client const authHeaders = await this.client.getAuthHeaders(endpoint); // Build the full URL const config = this.client.configuration; const baseUrl = config.runtimeApiUrl; const fullUrl = `${baseUrl}${endpoint}`; // Prepare headers const headers: Record = { 'Content-Type': 'application/json', ...authHeaders, ...(options.headers || {}), }; // Prepare fetch options const fetchOptions: RequestInit = { method: options.method || 'GET', headers, ...(options.timeout && { signal: AbortSignal.timeout(options.timeout) }), }; // Add body for non-GET requests if (options.body && options.method !== 'GET') { fetchOptions.body = JSON.stringify(options.body); } // Make the request const response = await fetch(fullUrl, fetchOptions); // Handle response if (!response.ok) { const errorText = await response.text(); throw new Error( `MCP API request failed: ${response.status} ${response.statusText}. ${errorText}` ); } // Return empty for DELETE requests if (options.method === 'DELETE') { return undefined as T; } // Parse JSON response const data = await response.json(); return data as T; } catch (error) { if (error instanceof Error) { throw new Error(`McpClient request failed: ${error.message}`); } throw new Error('McpClient request failed: Unknown error'); } } }