UNPKG

ragatanga-mcp-sdk

Version:

SDK for integrating with the Ragatanga Management Control Plane (MCP) with Next.js 15, React 19, WebSocket and Arrow IPC support

1,412 lines (1,401 loc) 121 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var zod = require('zod'); var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/errors/ontology-errors.ts function parseOntologyError(error) { if (error instanceof OntologyError) { return error; } if (error instanceof exports.HTTPError) { const errorData = error.data; const status = error.status; const message = errorData?.message || error.message; if (status === 404) { const entityUriMatch = message.match(/Entity not found: (.+)/); if (entityUriMatch) { return new OntologyEntityNotFoundError(entityUriMatch[1], message, { cause: error }); } } else if (status === 400) { if (message.includes("SPARQL") || message.includes("query")) { const queryMatch = message.match(/query: (.+)/); const query = queryMatch ? queryMatch[1] : "unknown query"; return new OntologySparqlError(query, message, { cause: error }); } if (message.includes("cursor") || message.includes("pagination")) { const cursorMatch = message.match(/cursor: (.+)/); const cursor = cursorMatch ? cursorMatch[1] : void 0; return new OntologyPaginationError(cursor, message, { cause: error }); } } if (message.includes("Failed to")) { const operationMatch = message.match(/Failed to (\w+)/); const entityUriMatch = message.match(/entity: (.+)/); if (operationMatch) { return new OntologyModificationError( operationMatch[1], entityUriMatch ? entityUriMatch[1] : void 0, message, { cause: error } ); } } } return new OntologyError(error.message, { cause: error }); } var _OntologyError, OntologyError, _OntologyEntityNotFoundError, OntologyEntityNotFoundError, _OntologySparqlError, OntologySparqlError, _OntologyModificationError, OntologyModificationError, _OntologyPaginationError, OntologyPaginationError; var init_ontology_errors = __esm({ "src/errors/ontology-errors.ts"() { init_errors(); _OntologyError = class _OntologyError extends exports.MCPError { constructor(message, options) { super(message, options); this.name = "OntologyError"; } }; __name(_OntologyError, "OntologyError"); OntologyError = _OntologyError; _OntologyEntityNotFoundError = class _OntologyEntityNotFoundError extends OntologyError { constructor(entityUri, message, options) { super(message || `Entity not found: ${entityUri}`, options); this.name = "OntologyEntityNotFoundError"; this.entityUri = entityUri; } }; __name(_OntologyEntityNotFoundError, "OntologyEntityNotFoundError"); OntologyEntityNotFoundError = _OntologyEntityNotFoundError; _OntologySparqlError = class _OntologySparqlError extends OntologyError { constructor(query, message, options) { super(message || `Invalid SPARQL query: ${query}`, options); this.name = "OntologySparqlError"; this.query = query; } }; __name(_OntologySparqlError, "OntologySparqlError"); OntologySparqlError = _OntologySparqlError; _OntologyModificationError = class _OntologyModificationError extends OntologyError { constructor(operation, entityUri, message, options) { super( message || `Failed to ${operation}${entityUri ? ` entity: ${entityUri}` : ""}`, options ); this.name = "OntologyModificationError"; this.operation = operation; this.entityUri = entityUri; } }; __name(_OntologyModificationError, "OntologyModificationError"); OntologyModificationError = _OntologyModificationError; _OntologyPaginationError = class _OntologyPaginationError extends OntologyError { constructor(cursor, message, options) { super( message || `Invalid pagination${cursor ? ` cursor: ${cursor}` : ""}`, options ); this.name = "OntologyPaginationError"; this.cursor = cursor; } }; __name(_OntologyPaginationError, "OntologyPaginationError"); OntologyPaginationError = _OntologyPaginationError; __name(parseOntologyError, "parseOntologyError"); } }); // src/errors/index.ts function createHttpError(response) { const status = response.status; let message = `HTTP Error ${status}`; let code = `HTTP_${status}`; if (status === 400) { message = "Bad Request: The request was invalid"; return new exports.HTTPError(message, response); } else if (status === 401) { message = "Unauthorized: Authentication is required"; return new exports.AuthError(message, { status, code, response }); } else if (status === 403) { message = "Forbidden: You don't have permission to access this resource"; return new exports.AuthError(message, { status, code, response }); } else if (status === 404) { message = "Not Found: The requested resource does not exist"; return new exports.HTTPError(message, response); } else if (status === 429) { message = "Too Many Requests: Rate limit exceeded"; return new exports.HTTPError(message, response); } else if (status >= 500) { message = "Server Error: Something went wrong on the server"; return new exports.HTTPError(message, response); } return new exports.HTTPError(message, response); } var _MCPError; exports.MCPError = void 0; var _NetworkError; exports.NetworkError = void 0; var _TimeoutError; exports.TimeoutError = void 0; var _HTTPError; exports.HTTPError = void 0; var _AuthError; exports.AuthError = void 0; var _UnauthorizedError; exports.UnauthorizedError = void 0; var _TenantNotFoundError; exports.TenantNotFoundError = void 0; var _WebSocketError; exports.WebSocketError = void 0; var _ParseError; exports.ParseError = void 0; var _ValidationError; exports.ValidationError = void 0; var init_errors = __esm({ "src/errors/index.ts"() { init_ontology_errors(); _MCPError = class _MCPError extends Error { constructor(message, options) { super(message); this.name = "MCPError"; this.status = options?.status; this.code = options?.code; this.cause = options?.cause; Object.setPrototypeOf(this, _MCPError.prototype); } /** * Creates a formatted error message with all available details */ toFormattedString() { const parts = [ `[${this.name}] ${this.message}`, this.code ? `Code: ${this.code}` : null, this.status ? `Status: ${this.status}` : null, this.cause ? `Cause: ${this.cause instanceof Error ? this.cause.message : String(this.cause)}` : null ].filter(Boolean); return parts.join(" | "); } }; __name(_MCPError, "MCPError"); exports.MCPError = _MCPError; _NetworkError = class _NetworkError extends exports.MCPError { constructor(message, options) { super(message, { status: 0, code: options?.code || "NETWORK_ERROR", cause: options?.cause }); this.name = "NetworkError"; Object.setPrototypeOf(this, _NetworkError.prototype); } }; __name(_NetworkError, "NetworkError"); exports.NetworkError = _NetworkError; _TimeoutError = class _TimeoutError extends exports.MCPError { constructor(message = "Request timed out", options) { super(message, { status: 0, code: options?.code || "TIMEOUT", cause: options?.cause }); this.name = "TimeoutError"; Object.setPrototypeOf(this, _TimeoutError.prototype); } }; __name(_TimeoutError, "TimeoutError"); exports.TimeoutError = _TimeoutError; _HTTPError = class _HTTPError extends exports.MCPError { constructor(message, response, options) { super(message, { status: response.status, code: options?.code || `HTTP_${response.status}`, cause: options?.cause }); this.name = "HTTPError"; this.response = response; Object.setPrototypeOf(this, _HTTPError.prototype); } }; __name(_HTTPError, "HTTPError"); exports.HTTPError = _HTTPError; _AuthError = class _AuthError extends exports.MCPError { constructor(message, options) { super(message, { status: options?.status || 401, code: options?.code || "AUTH_ERROR", cause: options?.cause }); this.name = "AuthError"; this.response = options?.response; Object.setPrototypeOf(this, _AuthError.prototype); } }; __name(_AuthError, "AuthError"); exports.AuthError = _AuthError; _UnauthorizedError = class _UnauthorizedError extends exports.AuthError { constructor(message, options) { super(message, { status: 401, code: options?.code || "UNAUTHORIZED", cause: options?.cause, response: options?.response }); this.name = "UnauthorizedError"; Object.setPrototypeOf(this, _UnauthorizedError.prototype); } }; __name(_UnauthorizedError, "UnauthorizedError"); exports.UnauthorizedError = _UnauthorizedError; _TenantNotFoundError = class _TenantNotFoundError extends exports.HTTPError { constructor(message, options) { const mockResponse = new Response(null, { status: 404 }); super(message, mockResponse, { code: options?.code || "TENANT_NOT_FOUND", cause: options?.cause }); this.name = "TenantNotFoundError"; Object.setPrototypeOf(this, _TenantNotFoundError.prototype); } }; __name(_TenantNotFoundError, "TenantNotFoundError"); exports.TenantNotFoundError = _TenantNotFoundError; _WebSocketError = class _WebSocketError extends exports.MCPError { constructor(message, options) { super(message, { code: options?.code || "WEBSOCKET_ERROR", cause: options?.cause }); this.name = "WebSocketError"; this.event = options?.event; Object.setPrototypeOf(this, _WebSocketError.prototype); } }; __name(_WebSocketError, "WebSocketError"); exports.WebSocketError = _WebSocketError; _ParseError = class _ParseError extends exports.MCPError { constructor(message, options) { super(message, { code: options?.code || "PARSE_ERROR", cause: options?.cause }); this.name = "ParseError"; this.data = options?.data; Object.setPrototypeOf(this, _ParseError.prototype); } }; __name(_ParseError, "ParseError"); exports.ParseError = _ParseError; _ValidationError = class _ValidationError extends exports.MCPError { constructor(message, options) { super(message, { code: options?.code || "VALIDATION_ERROR", cause: options?.cause }); this.name = "ValidationError"; this.errors = options?.errors; Object.setPrototypeOf(this, _ValidationError.prototype); } }; __name(_ValidationError, "ValidationError"); exports.ValidationError = _ValidationError; __name(createHttpError, "createHttpError"); } }); // src/client/paths.ts function buildPath(pathTemplate, params) { if (!params) return pathTemplate; let result = pathTemplate; for (const [key, value] of Object.entries(params)) { result = result.replace(`:${key}`, encodeURIComponent(String(value))); } return result; } function createFullUrl(baseUrl, path) { const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; const normalizedPath = path.startsWith("/") ? path : `/${path}`; return `${normalizedBaseUrl}${normalizedPath}`; } var ApiEndpoints; var init_paths = __esm({ "src/client/paths.ts"() { ApiEndpoints = { // Ontology endpoints Ontologies: "/ontologies", OntologyById: "/ontologies/:id", // User endpoints CurrentUser: "/users/me", UserById: "/users/:id", // Authentication endpoints Login: "/auth/login", Logout: "/auth/logout", RefreshToken: "/auth/refresh", // Tenant endpoints CurrentTenant: "/tenants/current", TenantById: "/tenants/:id", // MCP context endpoints ResolveContext: "/resolve", WebSocketResolve: "/resolve_ws", // Tools endpoints ToolsList: "/tools/list", ToolsExecute: "/tools/execute", ToolsStream: "/tools/stream", // JSON-RPC endpoint JsonRPC: "/mcp" }; __name(buildPath, "buildPath"); __name(createFullUrl, "createFullUrl"); } }); // src/client/arrow-ipc.ts var arrow_ipc_exports = {}; __export(arrow_ipc_exports, { ArrowConnectionStatus: () => ArrowConnectionStatus, MCPArrowClient: () => exports.MCPArrowClient, createArrowClient: () => createArrowClient, default: () => arrow_ipc_default }); function createArrowClient(options) { return new exports.MCPArrowClient(options); } var ArrowConnectionStatus, _MCPArrowClient; exports.MCPArrowClient = void 0; var arrow_ipc_default; var init_arrow_ipc = __esm({ "src/client/arrow-ipc.ts"() { init_errors(); init_paths(); ArrowConnectionStatus = /* @__PURE__ */ ((ArrowConnectionStatus2) => { ArrowConnectionStatus2[ArrowConnectionStatus2["DISCONNECTED"] = 0] = "DISCONNECTED"; ArrowConnectionStatus2[ArrowConnectionStatus2["CONNECTING"] = 1] = "CONNECTING"; ArrowConnectionStatus2[ArrowConnectionStatus2["CONNECTED"] = 2] = "CONNECTED"; ArrowConnectionStatus2[ArrowConnectionStatus2["CLOSING"] = 3] = "CLOSING"; ArrowConnectionStatus2[ArrowConnectionStatus2["ERROR"] = 4] = "ERROR"; ArrowConnectionStatus2[ArrowConnectionStatus2["RECONNECTING"] = 5] = "RECONNECTING"; return ArrowConnectionStatus2; })(ArrowConnectionStatus || {}); _MCPArrowClient = class _MCPArrowClient { /** * Creates a new Arrow IPC client */ constructor(options) { this.ws = null; this.status = 0 /* DISCONNECTED */; this.handlers = {}; this.messageQueue = []; this.pendingRequests = /* @__PURE__ */ new Map(); this.defaultTimeout = 3e4; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectDelay = 1e3; this.autoReconnect = true; this.connectionPath = "/arrow"; this.connectionRetryTimer = null; this.manualClose = false; this.lastConnectionTime = 0; this.nextMessageId = 1; // Heartbeat properties this.heartbeatInterval = 15e3; // 15 seconds this.heartbeatTimer = null; this.missedHeartbeats = 0; this.maxMissedHeartbeats = 3; this.lastHeartbeatResponse = 0; /** * Event listener map for custom events */ this.eventListeners = /* @__PURE__ */ new Map(); let wsBaseUrl = options.baseUrl || "https://api.mcp.ai"; wsBaseUrl = wsBaseUrl.replace(/^http/, "ws"); this.baseUrl = wsBaseUrl; this.apiKey = options.apiKey; this.token = options.token; this.tenantId = options.tenantId; this.autoReconnect = options.autoReconnect !== false; if (options.maxReconnectAttempts !== void 0) { this.maxReconnectAttempts = options.maxReconnectAttempts; } if (options.defaultTimeout) { this.defaultTimeout = options.defaultTimeout; } if (options.streamTimeout) { this.streamTimeout = options.streamTimeout; } } /** * Add an event listener for a specific event type */ addEventListener(event, handler) { if (!this.eventListeners.has(event)) { this.eventListeners.set(event, /* @__PURE__ */ new Set()); } this.eventListeners.get(event)?.add(handler); } /** * Remove an event listener */ removeEventListener(event, handler) { if (this.eventListeners.has(event)) { this.eventListeners.get(event)?.delete(handler); } } /** * Trigger event handlers for a specific event */ triggerEvent(event, message) { if (this.eventListeners.has(event)) { for (const handler of this.eventListeners.get(event) || []) { try { handler(message); } catch (error) { console.error(`Error in ${event} event handler:`, error); } } } } /** * Connect to the Arrow IPC WebSocket endpoint */ connect(path = "/arrow", handlers = {}) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { console.warn("Arrow WebSocket is already connected. Disconnect first to reconnect."); return; } this.connectionPath = path; this.manualClose = false; this.setStatus(1 /* CONNECTING */); this.handlers = handlers; this.lastConnectionTime = Date.now(); const url = new URL(createFullUrl(this.baseUrl, path)); if (this.apiKey) { url.searchParams.append("apiKey", this.apiKey); } if (this.token) { url.searchParams.append("token", this.token); } if (this.tenantId) { url.searchParams.append("tenantId", this.tenantId); } try { this.ws = new WebSocket(url.toString()); this.ws.binaryType = "arraybuffer"; this.ws.onopen = this.handleOpen.bind(this); this.ws.onmessage = this.handleMessage.bind(this); this.ws.onerror = this.handleError.bind(this); this.ws.onclose = this.handleClose.bind(this); } catch (error) { this.handleConnectionError(error); } } /** * Update and notify about status changes */ setStatus(newStatus) { if (this.status !== newStatus) { this.status = newStatus; if (this.handlers.onStatusChange) { try { this.handlers.onStatusChange(newStatus); } catch (error) { console.error("Error in Arrow status change handler:", error); } } } } /** * Handle WebSocket open event */ handleOpen(event) { console.log("Arrow WebSocket connection opened"); this.setStatus(2 /* CONNECTED */); this.reconnectAttempts = 0; if (this.handlers.onOpen) { try { this.handlers.onOpen(event); } catch (error) { console.error("Error in Arrow open handler:", error); } } this.processQueue(); this.startHeartbeat(); } /** * Start heartbeat mechanism to keep connection alive */ startHeartbeat() { if (this.heartbeatTimer) { clearTimeout(this.heartbeatTimer); this.heartbeatTimer = null; } this.missedHeartbeats = 0; this.heartbeatTimer = setInterval(() => { this.sendHeartbeat(); }, this.heartbeatInterval); this.sendHeartbeat(); } /** * Stop heartbeat mechanism */ stopHeartbeat() { if (this.heartbeatTimer) { clearTimeout(this.heartbeatTimer); this.heartbeatTimer = null; } } /** * Send a heartbeat ping */ sendHeartbeat() { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { return; } if (this.missedHeartbeats >= this.maxMissedHeartbeats) { console.warn(`Missed ${this.missedHeartbeats} heartbeats, reconnecting...`); try { this.ws.close(); } catch (e) { console.error("Error closing WebSocket during heartbeat failure:", e); } if (this.autoReconnect && !this.manualClose) { this.attemptReconnect(); } return; } this.missedHeartbeats++; try { console.debug("Ping sent"); const pingMessage = { jsonrpc: "2.0", method: "ping", id: `ping-${Date.now()}` }; this.ws.send(JSON.stringify(pingMessage)); } catch (error) { console.error("Error sending heartbeat:", error); } } /** * Handle a WebSocket message event */ handleMessage(event) { try { this.missedHeartbeats = 0; this.lastHeartbeatResponse = Date.now(); if (event.data instanceof ArrayBuffer) { const dataSize = event.data.byteLength; console.log(`Received binary data (${dataSize} bytes)`); if (this.handlers.onBinaryMessage) { try { this.handlers.onBinaryMessage(event.data); } catch (error) { console.error("Error in binary message handler:", error); } } try { const message = this.decodeMessage(event.data); if (message.method === "ping") { if (this.ws && this.ws.readyState === WebSocket.OPEN) { const pongMessage = { jsonrpc: "2.0", method: "pong", id: message.id }; try { this.ws.send(JSON.stringify(pongMessage)); } catch (e) { console.warn("Failed to send pong response:", e); } } return; } if (message.error) { console.warn("Received error message:", message.error); } this.triggerEvent("message", message); this.processMessage(message); } catch (decodeError) { console.error("Error decoding binary message:", decodeError); } } else if (typeof event.data === "string") { try { const message = JSON.parse(event.data); if (message.method === "pong" || message.id && String(message.id).startsWith("ping-")) { console.debug("Pong received"); return; } if (this.handlers.onMessage) { try { this.handlers.onMessage(message); } catch (error) { console.error("Error in message handler:", error); } } this.triggerEvent("message", message); this.processMessage(message); } catch (error) { console.error("Error parsing JSON message:", error, "Raw data:", event.data); } } else { console.warn("Received unsupported message type:", typeof event.data); } } catch (error) { console.error("Error handling WebSocket message:", error); } } /** * Handle WebSocket error event */ handleError(event) { console.error("Arrow WebSocket error:", event); this.setStatus(4 /* ERROR */); if (this.handlers.onError) { try { this.handlers.onError(event); } catch (error) { console.error("Error in Arrow error handler:", error); } } if (this.autoReconnect && !this.manualClose) { this.attemptReconnect(); } } /** * Handle WebSocket close event */ handleClose(event) { console.log("Arrow WebSocket connection closed:", event); this.setStatus(0 /* DISCONNECTED */); this.stopHeartbeat(); if (this.handlers.onClose) { try { this.handlers.onClose(event); } catch (error) { console.error("Error in Arrow close handler:", error); } } for (const [id, request] of this.pendingRequests.entries()) { this.pendingRequests.delete(id); if (request.timer) { clearTimeout(request.timer); } request.reject(new exports.WebSocketError("Connection closed", { code: `CLOSE_${event.code}`, cause: event })); } if (this.autoReconnect && !this.manualClose && event.code !== 1e3) { this.attemptReconnect(); } } /** * Handle connection error */ handleConnectionError(error) { console.error("Arrow WebSocket connection error:", error); this.setStatus(4 /* ERROR */); if (this.autoReconnect && !this.manualClose) { this.attemptReconnect(); } } /** * Send a message over the WebSocket */ async sendArrowMessage(message) { if (message.id === void 0) { message.id = this.nextMessageId++; } const id = String(message.id); return new Promise((resolve, reject) => { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.queueMessage({ ...message }); const timer = setTimeout(() => { this.pendingRequests.delete(id); reject(new exports.WebSocketError("WebSocket not connected", { code: "CONNECTION_ERROR" })); }, this.defaultTimeout); this.pendingRequests.set(id, { resolve, reject, timer }); if (this.status !== 1 /* CONNECTING */ && this.autoReconnect && !this.manualClose) { this.attemptReconnect(); } return; } try { const data = this.encodeMessage(message); const timer = setTimeout(() => { this.pendingRequests.delete(id); reject(new exports.MCPError("Request timed out", { code: "TIMEOUT" })); }, this.defaultTimeout); this.pendingRequests.set(id, { resolve, reject, timer }); this.ws.send(data); } catch (error) { reject(new exports.MCPError("Failed to send message", { cause: error })); } }); } /** * Encode a message to binary format */ encodeMessage(message) { try { if ("binaryData" in message && message.binaryData instanceof Uint8Array && message.binaryData) { const { binaryData, ...jsonPart } = message; const jsonString = JSON.stringify(jsonPart); const headerDelimiter = new Uint8Array([0, 65, 82, 82, 79, 87, 58]); const encoder2 = new TextEncoder(); const jsonBytes = encoder2.encode(jsonString); const totalLength = jsonBytes.length + headerDelimiter.length + binaryData.length; const result = new Uint8Array(totalLength); result.set(jsonBytes, 0); result.set(headerDelimiter, jsonBytes.length); result.set(binaryData, jsonBytes.length + headerDelimiter.length); return result.buffer; } const encoder = new TextEncoder(); const encodedData = encoder.encode(JSON.stringify(message)); return encodedData.buffer; } catch (error) { console.error("Error encoding message:", error); throw new exports.MCPError("Failed to encode message", { code: "ENCODING_ERROR", cause: error }); } } /** * Decode an Arrow IPC message from binary data */ decodeMessage(buffer) { try { const decoder = new TextDecoder(); const text = decoder.decode(new Uint8Array(buffer)); try { const jsonMessage = JSON.parse(text); if (jsonMessage && typeof jsonMessage === "object") { return jsonMessage; } } catch (jsonError) { console.debug("Not valid JSON, trying binary parse"); } const headerDelimiter = new Uint8Array([0, 65, 82, 82, 79, 87, 58]); const data = new Uint8Array(buffer); console.debug(`Received binary data (${data.length} bytes)`); let delimiterIndex = -1; for (let i = 0; i <= data.length - headerDelimiter.length; i++) { let match = true; for (let j = 0; j < headerDelimiter.length; j++) { if (data[i + j] !== headerDelimiter[j]) { match = false; break; } } if (match) { delimiterIndex = i; break; } } let message; if (delimiterIndex > 0) { const headerPart = new Uint8Array(buffer, 0, delimiterIndex); const headerText = decoder.decode(headerPart); try { message = JSON.parse(headerText); } catch (e) { console.error("Failed to parse Arrow IPC header:", e); message = { jsonrpc: "2.0" }; } const binaryDataStart = delimiterIndex + headerDelimiter.length; const binaryData = new Uint8Array(buffer, binaryDataStart); message.binaryData = binaryData; } else { if (data.length < 10) { console.debug("Received likely heartbeat/ping message"); message = { jsonrpc: "2.0", method: "ping", id: String(Date.now()) }; } else { console.warn(`Received unrecognized binary format (${data.length} bytes): ${data.slice(0, 20)}`); message = { jsonrpc: "2.0", id: String(Date.now()), binaryData: data }; } } return message; } catch (error) { console.error("Error decoding message:", error); return { jsonrpc: "2.0", error: { code: -32700, message: "Failed to decode binary message" } }; } } /** * Queue a message for later */ queueMessage(message) { console.log("Queueing message for later:", message); this.messageQueue.push(message); if (this.status !== 1 /* CONNECTING */ && this.autoReconnect && !this.manualClose) { this.attemptReconnect(); } } /** * Process the message queue */ processQueue() { if (this.messageQueue.length === 0) { return; } console.log(`Processing ${this.messageQueue.length} queued messages`); const queue = [...this.messageQueue]; this.messageQueue = []; for (const message of queue) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.sendArrowMessage(message).catch(console.error); } else { this.messageQueue.push(message); } } } /** * Attempt to reconnect to the WebSocket */ attemptReconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.error(`Maximum reconnect attempts (${this.maxReconnectAttempts}) reached. Giving up.`); return; } if (this.status === 1 /* CONNECTING */ || this.status === 5 /* RECONNECTING */) { return; } this.setStatus(5 /* RECONNECTING */); const delay = Math.min(this.reconnectDelay * Math.pow(1.5, this.reconnectAttempts), 3e4); this.reconnectAttempts++; console.log(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`); this.connectionRetryTimer = setTimeout(() => { this.connectionRetryTimer = null; this.connect(this.connectionPath, this.handlers); }, delay); } /** * Generate a unique message ID */ generateMessageId() { return this.nextMessageId++; } /** * Get server capabilities */ async getCapabilities() { return this.sendArrowMessage({ jsonrpc: "2.0", method: "capabilities", params: {}, id: this.generateMessageId() }); } /** * List available resources */ async listResources() { return this.sendArrowMessage({ jsonrpc: "2.0", method: "listResources", params: {}, id: this.generateMessageId() }); } /** * Get a specific resource */ async getResource(uri) { return this.sendArrowMessage({ jsonrpc: "2.0", method: "getResource", params: { uri }, id: this.generateMessageId() }); } /** * List available tools */ async listTools() { return this.sendArrowMessage({ jsonrpc: "2.0", method: "listTools", params: {}, id: this.generateMessageId() }); } /** * Get the current connection status */ getStatus() { return this.status; } /** * Disconnect from the WebSocket */ disconnect() { if (!this.ws) { return; } console.log("Disconnecting Arrow WebSocket"); this.manualClose = true; this.stopHeartbeat(); this.setStatus(3 /* CLOSING */); try { this.ws.close(); } catch (error) { console.error("Error closing WebSocket:", error); } finally { this.ws = null; this.setStatus(0 /* DISCONNECTED */); } if (this.connectionRetryTimer) { clearTimeout(this.connectionRetryTimer); this.connectionRetryTimer = null; } } /** * Send a request to a model (like Claude) through the MCP server */ async sendRequest(options) { const startTime = performance.now(); let serializationTime = 0; let networkStart = 0; let networkEnd = 0; let deserializationTime = 0; try { const serializationStart = performance.now(); const method = options.method || "POST"; const responseType = options.responseType || "json"; const isStreaming = options.onChunk && options.body?.stream; const params = { path: options.path, method, body: options.body || {}, responseType, stream: isStreaming }; serializationTime = performance.now() - serializationStart; networkStart = performance.now(); if (isStreaming) { const messageId = this.generateMessageId(); const streamingPromise = new Promise((resolve, reject) => { const streamHandler = /* @__PURE__ */ __name((message) => { if (message.id === messageId && message.method === "streamChunk") { options.onChunk?.(message.params); } else if (message.id === messageId && message.method === "streamEnd") { this.removeEventListener("message", streamHandler); networkEnd = performance.now(); resolve(message.params?.result || {}); } else if (message.id === messageId && message.error) { this.removeEventListener("message", streamHandler); reject(new exports.MCPError("Streaming error", { code: "STREAM_ERROR", cause: message.error })); } }, "streamHandler"); this.addEventListener("message", streamHandler); const timer = setTimeout(() => { this.removeEventListener("message", streamHandler); reject(new exports.MCPError("Stream request timed out", { code: "TIMEOUT" })); }, this.streamTimeout || 6e4); this.sendArrowMessage({ jsonrpc: "2.0", method: "startStream", params, id: messageId }).catch((error) => { this.removeEventListener("message", streamHandler); clearTimeout(timer); reject(error); }); }); const response = await streamingPromise; deserializationTime = 0; const result = { data: response.data || {}, status: response.status || 200, headers: response.headers || {} }; if (options.metrics) { result.metrics = { totalTime: performance.now() - startTime, serializationTime, networkTime: networkEnd - networkStart, deserializationTime }; } return result; } else { const response = await this.sendArrowMessage({ jsonrpc: "2.0", method: "sendRequest", params, id: this.generateMessageId() }); networkEnd = performance.now(); const deserializationStart = performance.now(); const result = { data: response.data || {}, status: response.status || 200, headers: response.headers || {} }; deserializationTime = performance.now() - deserializationStart; if (options.metrics) { result.metrics = { totalTime: performance.now() - startTime, serializationTime, networkTime: networkEnd - networkStart, deserializationTime }; } return result; } } catch (error) { console.error("Error in sendRequest:", error); throw error; } } /** * Check server health */ async checkHealth() { try { const healthUrl = new URL(this.baseUrl.replace("ws://", "http://").replace("wss://", "https://")); healthUrl.pathname = "/health"; const response = await fetch(healthUrl.toString(), { method: "GET", headers: { "Accept": "application/json" } }); if (response.ok) { const data = await response.json(); return data.status === "ok"; } if (this.status !== 2 /* CONNECTED */) { return false; } await this.getCapabilities(); return true; } catch (error) { console.error("Health check failed:", error); return false; } } /** * Get server connection status as string */ getConnectionStatusText() { switch (this.status) { case 2 /* CONNECTED */: return "Connected"; case 1 /* CONNECTING */: return "Connecting"; case 0 /* DISCONNECTED */: return "Disconnected"; case 4 /* ERROR */: return "Error"; case 5 /* RECONNECTING */: return "Reconnecting"; case 3 /* CLOSING */: return "Closing"; default: return "Unknown"; } } /** * Process an incoming message */ processMessage(message) { const id = String(message.id || ""); const pendingRequest = this.pendingRequests.get(id); if (pendingRequest) { if (pendingRequest.timer) { clearTimeout(pendingRequest.timer); } this.pendingRequests.delete(id); if (message.error) { pendingRequest.reject(new exports.MCPError(message.error.message || "Unknown error", { code: String(message.error.code), cause: message.error.data })); } else { let result = message.result; if (message.binaryData && message.binaryData.length > 0) { try { if (!result) { result = {}; } if (typeof result === "object") { result.arrowData = message.binaryData.buffer; if (message.params?.schema) { result.schema = message.params.schema; } } } catch (error) { console.warn("Error processing Arrow binary data:", error); } } pendingRequest.resolve(result); } } if (message.method === "streamChunk" || message.method === "streamEnd") { return; } if (this.handlers.onMessage) { try { this.handlers.onMessage(message); } catch (error) { console.error("Error in message handler:", error); } } } }; __name(_MCPArrowClient, "MCPArrowClient"); exports.MCPArrowClient = _MCPArrowClient; __name(createArrowClient, "createArrowClient"); arrow_ipc_default = exports.MCPArrowClient; } }); // src/auth/index.ts function createAuthHeaders(options) { const headers = {}; if (options.apiKey) { headers["X-API-Key"] = options.apiKey; } if (options.token) { headers["Authorization"] = `Bearer ${options.token}`; } if (options.tenantId) { headers["X-Tenant-ID"] = options.tenantId; } return headers; } __name(createAuthHeaders, "createAuthHeaders"); // src/utils/fetcher.ts init_errors(); var sleep = /* @__PURE__ */ __name((ms) => new Promise((resolve) => setTimeout(resolve, ms)), "sleep"); var DEFAULT_RETRY_STATUS_CODES = [408, 429, 500, 502, 503, 504]; function createFetchParams(url, options = {}) { const { cache, revalidate, tags, ...fetchOptions } = options; const nextFetchOptions = { ...fetchOptions }; if (typeof cache === "boolean") { nextFetchOptions.cache = cache ? "force-cache" : "no-store"; } else if (typeof cache === "string") { nextFetchOptions.cache = cache; } else if (cache && typeof cache === "object") { nextFetchOptions.next = nextFetchOptions.next || {}; const cacheConfig = cache; if (cacheConfig.maxAge !== void 0) { nextFetchOptions.next.revalidate = cacheConfig.maxAge; } if (cacheConfig.tag) { nextFetchOptions.next.tags = Array.isArray(cacheConfig.tag) ? cacheConfig.tag : [cacheConfig.tag]; } } if (revalidate !== void 0) { nextFetchOptions.next = nextFetchOptions.next || {}; nextFetchOptions.next.revalidate = revalidate; } if (tags && tags.length > 0) { nextFetchOptions.next = nextFetchOptions.next || {}; nextFetchOptions.next.tags = tags; } return { url, options: nextFetchOptions }; } __name(createFetchParams, "createFetchParams"); async function fetcher(url, options = {}) { const { timeout = 1e4, retry = false, ...fetchOptions } = options; const retryOptions = retry === true ? {} : retry || {}; const { retries = 0, retryBackoffFactor = 2, retryInitialDelay = 300, retryMaxDelay = 1e4, retryStatusCodes = DEFAULT_RETRY_STATUS_CODES, shouldRetry } = retryOptions; const controller = new AbortController(); const { signal } = controller; let timeoutId = null; if (timeout > 0) { timeoutId = setTimeout(() => { controller.abort(); }, timeout); } const originalSignal = fetchOptions.signal; if (originalSignal) { originalSignal.addEventListener("abort", () => { controller.abort(originalSignal.reason); if (timeoutId) clearTimeout(timeoutId); }); } const mergedOptions = { ...fetchOptions, signal }; const { url: parsedUrl, options: nextOptions } = createFetchParams(url, { ...mergedOptions, cache: options.cache, revalidate: options.revalidate, tags: options.tags }); const clearTimeoutIfExists = /* @__PURE__ */ __name(() => { if (timeoutId !== null) { clearTimeout(timeoutId); timeoutId = null; } }, "clearTimeoutIfExists"); const customShouldRetry = /* @__PURE__ */ __name((error, attempt2) => { if (attempt2 >= retries) return false; if (shouldRetry) return shouldRetry(error, attempt2); if (error instanceof exports.TimeoutError) return true; if (error instanceof exports.NetworkError) return true; if (error instanceof exports.HTTPError && retryStatusCodes.includes(error.status || 0)) return true; return false; }, "customShouldRetry"); let attempt = 0; let lastError = null; while (attempt <= retries) { try { const response = await fetch(parsedUrl, nextOptions); clearTimeoutIfExists(); if (!response.ok) { throw createHttpError(response); } let data; const contentType = response.headers.get("content-type"); try { if (contentType && contentType.includes("application/json")) { data = await response.json(); } else { try { data = await response.json(); } catch { const text = await response.text(); data = text.trim() ? text : {}; } } } catch (error) { throw new exports.ParseError("Failed to parse response", { cause: error, data: await response.text().catch(() => void 0) }); } return { data, status: response.status, headers: response.headers, response }; } catch (error) { clearTimeoutIfExists(); let mcpError; if (error instanceof exports.MCPError) { mcpError = error; } else if (error instanceof DOMException && error.name === "AbortError") { mcpError = new exports.TimeoutError("Request timed out", { cause: error }); } else if (error instanceof TypeError && error.message.includes("fetch")) { mcpError = new exports.NetworkError("Network error occurred", { cause: error }); } else if (error instanceof Error) { mcpError = new exports.MCPError(error.message, { cause: error }); } else { mcpError = new exports.MCPError(String(error)); } lastError = mcpError; if (!customShouldRetry(mcpError, attempt)) { throw mcpError; } const delay = Math.min( retryInitialDelay * Math.pow(retryBackoffFactor, attempt), retryMaxDelay ); if (process.env.NODE_ENV !== "production") { console.warn(`Retry attempt ${attempt + 1}/${retries} for ${parsedUrl} after ${delay}ms delay`); } await sleep(delay); attempt++; } } if (lastError) { throw lastError; } throw new exports.MCPError("Unknown error occurred"); } __name(fetcher, "fetcher"); // src/client/websocket.ts init_paths(); var _MCPWebSocket = class _MCPWebSocket { /** * Creates a new WebSocket client */ constructor(options) { this.ws = null; this.reconnectAttempts = 0; this.maxReconnectAttempts = 10; this.reconnectDelay = 1e3; this.handlers = {}; this.status = 3 /* CLOSED */; this.messageQueue = []; this.subscriptions = /* @__PURE__ */ new Map(); this.heartbeatDelay = 15e3; // 15 seconds to match server's interval this.connectionId = ""; this.binarySupport = false; this.connectionPath = "/resolve_ws"; this.autoReconnect = true; this.lastConnectionTime = 0; this.connectionRetryTimer = null; this.manualClose = false; let wsBaseUrl = options.baseUrl || "https://api.mcp.ai"; wsBaseUrl = wsBaseUrl.replace(/^http/, "ws"); this.baseUrl = wsBaseUrl; this.apiKey = options.apiKey; this.token = options.token; this.tenantId = options.tenantId; this.autoReconnect = options.autoReconnect !== false; if (options.maxReconnectAttempts !== void 0) { this.maxReconnectAttempts = options.maxReconnectAttempts; } } /** * Connects to the MCP WebSocket endpoint */ connect(path = "/resolve_ws", handlers = {}) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { console.warn("WebSocket is already connected. Disconnect first to reconnect."); return; } this.connectionPath = path; this.manualClose = false; this.setStatus(0 /* CONNECTING */); this.handlers = handlers; this.lastConnectionTime = Date.now(); const url = new URL(createFullUrl(this.baseUrl, path)); this.connectionId = this.generateConnectionId(); if (this.apiKey) { url.searchParams.append("apiKey", this.apiKey); } if (this.token) { url.searchParams.append("token", this.token);