UNPKG

@net3/queuer

Version:

200 lines (199 loc) 8.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MCPManager = void 0; const pieces_common_1 = require("@activepieces/pieces-common"); /** * Helper class for interacting with MCP servers using the standard MCP protocol. */ class MCPManager { /** * Parse SSE response format to extract JSON data */ static parseSSEResponse(sseData) { try { if (typeof sseData === 'string') { // Handle different response formats if (sseData.startsWith('{') || sseData.startsWith('[')) { // Direct JSON response return JSON.parse(sseData); } // Extract JSON from SSE format: "event: message\ndata: {...}\n\n" const lines = sseData.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const jsonStr = line.substring(6); // Remove "data: " prefix if (jsonStr.trim()) { return JSON.parse(jsonStr); } } } // If no SSE data found, try to parse the entire string as JSON if (sseData.trim()) { return JSON.parse(sseData); } } return sseData; // If it's already parsed or not a string } catch (error) { console.error('Failed to parse SSE response:', error); console.error('Raw response data:', sseData); throw new Error(`Failed to parse MCP response: ${error.message}`); } } /** * Initialize MCP session if not already initialized */ static async initializeMCPSession(config) { const { mcpServerUrl } = config; if (this.initialized.get(mcpServerUrl)) { return true; // Already initialized } try { console.log('Initializing MCP session with server:', mcpServerUrl); // Step 1: Initialize const initRequest = { jsonrpc: '2.0', method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: { tools: {} }, clientInfo: { name: 'activepieces-task-queuer', version: '1.0.0' } }, id: `init-${Date.now()}` }; const initResponse = await pieces_common_1.httpClient.sendRequest({ method: pieces_common_1.HttpMethod.POST, url: mcpServerUrl, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream' }, body: initRequest, }); if (!initResponse.body) { throw new Error('Empty response from MCP server during initialization'); } const initData = this.parseSSEResponse(initResponse.body); if (initData.error) { throw new Error(`MCP initialization failed: ${JSON.stringify(initData.error)}`); } console.log('MCP initialization successful'); // Step 2: Send initialized notification const initializedNotification = { jsonrpc: '2.0', method: 'initialized', params: {} }; try { await pieces_common_1.httpClient.sendRequest({ method: pieces_common_1.HttpMethod.POST, url: mcpServerUrl, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream' }, body: initializedNotification, }); console.log('MCP initialized notification sent successfully'); } catch (notifError) { // Notification might not return a response, which is fine console.log('Initialized notification sent (no response expected)'); } this.initialized.set(mcpServerUrl, true); return true; } catch (error) { console.error('MCP initialization error:', error); throw new Error(`Failed to initialize MCP session: ${error.message}`); } } /** * List available MCP tools with their schemas */ static async listMCPTools(config) { try { if (!config?.mcpServerUrl) { throw new Error('MCP Server URL is required'); } // Initialize session first const initialized = await this.initializeMCPSession(config); if (!initialized) { throw new Error('Failed to initialize MCP session'); } const toolsRequest = { jsonrpc: '2.0', method: 'tools/list', params: {}, id: `tools-list-${Date.now()}` }; console.log('Sending MCP tools/list request to:', config.mcpServerUrl); const toolsResponse = await pieces_common_1.httpClient.sendRequest({ method: pieces_common_1.HttpMethod.POST, url: config.mcpServerUrl, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream' }, body: toolsRequest, }); if (!toolsResponse.body) { throw new Error('Empty response from MCP server'); } const toolsData = this.parseSSEResponse(toolsResponse.body); if (toolsData.error) { throw new Error(`MCP server error: ${JSON.stringify(toolsData.error)}`); } const tools = toolsData.result?.tools || []; console.log(`Retrieved ${tools.length} MCP tools from server`); return tools; } catch (error) { console.error('Error listing MCP tools:', error); throw new Error(`Failed to list MCP tools: ${error.message}`); } } /** * Execute an MCP tool using JSON-RPC. Returns `{ success, result?, error? }`. */ static async executeMCPAction(toolName, params, config) { try { // Initialize session first const initialized = await this.initializeMCPSession(config); if (!initialized) { return { success: false, error: 'Failed to initialize MCP session' }; } const requestId = `tool-call-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const rpcRequest = { jsonrpc: '2.0', method: 'tools/call', params: { name: toolName, arguments: params }, id: requestId, }; const res = await pieces_common_1.httpClient.sendRequest({ method: pieces_common_1.HttpMethod.POST, url: config.mcpServerUrl, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream', }, body: rpcRequest, }); const responseData = this.parseSSEResponse(res.body); if (responseData.error) { return { success: false, error: responseData.error.message || 'MCP error' }; } return { success: true, result: responseData.result }; } catch (err) { return { success: false, error: `MCP execution failed: ${err.message}` }; } } } exports.MCPManager = MCPManager; MCPManager.initialized = new Map();