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
307 lines (305 loc) • 9.67 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/client/sse.ts
var SSEConnectionStatus = /* @__PURE__ */ ((SSEConnectionStatus2) => {
SSEConnectionStatus2["DISCONNECTED"] = "disconnected";
SSEConnectionStatus2["CONNECTING"] = "connecting";
SSEConnectionStatus2["CONNECTED"] = "connected";
SSEConnectionStatus2["RECONNECTING"] = "reconnecting";
SSEConnectionStatus2["ERROR"] = "error";
return SSEConnectionStatus2;
})(SSEConnectionStatus || {});
var _MCPSSEClient = class _MCPSSEClient {
/**
* Create a new SSE client
*/
constructor(options) {
this.eventSource = null;
this.eventHandlers = /* @__PURE__ */ new Map();
this.reconnectAttempts = 0;
this.status = "disconnected" /* DISCONNECTED */;
this.lastEventId = null;
this.heartbeatTimer = null;
this.connectionTimer = null;
this.statusHandlers = /* @__PURE__ */ new Set();
this.baseUrl = options.baseUrl || "https://api.mcp.ai";
this.apiKey = options.apiKey;
this.token = options.token;
this.tenantId = options.tenantId;
this.reconnectTimeout = options.reconnectTimeout || 3e3;
this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
this.eventsEndpoint = options.eventsEndpoint || "/events";
this.sseOptions = options.sseOptions;
this.eventTypes = options.eventTypes;
this.heartbeatInterval = options.heartbeatInterval || 3e4;
this.connectionTimeout = options.connectionTimeout || 1e4;
this.autoReconnect = options.autoReconnect !== false;
}
/**
* Get the current connection status
*/
getStatus() {
return this.status;
}
/**
* Register a handler for connection status changes
*/
onStatusChange(handler) {
this.statusHandlers.add(handler);
handler(this.status);
return () => {
this.statusHandlers.delete(handler);
};
}
/**
* Update connection status and notify handlers
*/
setStatus(status) {
if (this.status !== status) {
this.status = status;
for (const handler of this.statusHandlers) {
try {
handler(status);
} catch (error) {
console.error("Error in SSE status handler:", error);
}
}
}
}
/**
* Connect to the SSE endpoint
*/
connect() {
if (this.eventSource) {
this.disconnect();
}
this.setStatus("connecting" /* CONNECTING */);
this.reconnectAttempts = 0;
const url = new URL(`${this.baseUrl}${this.eventsEndpoint}`);
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);
}
if (this.eventTypes && this.eventTypes.length > 0) {
url.searchParams.append("events", this.eventTypes.join(","));
}
if (this.lastEventId) {
url.searchParams.append("lastEventId", this.lastEventId);
}
try {
this.connectionTimer = setTimeout(() => {
if (this.status === "connecting" /* CONNECTING */) {
console.warn("SSE connection timeout");
this.setStatus("error" /* ERROR */);
this.disconnect();
if (this.autoReconnect) {
this.attemptReconnect();
}
}
}, this.connectionTimeout);
this.eventSource = new EventSource(url.toString(), this.sseOptions);
this.eventSource.onopen = this.handleOpen.bind(this);
this.eventSource.onerror = this.handleError.bind(this);
this.eventSource.onmessage = this.handleMessage.bind(this);
if (this.eventTypes) {
for (const eventType of this.eventTypes) {
this.eventSource.addEventListener(eventType, this.handleEvent.bind(this));
}
}
for (const eventType of this.eventHandlers.keys()) {
this.eventSource.addEventListener(eventType, this.handleEvent.bind(this));
}
} catch (error) {
console.error("Error connecting to SSE endpoint:", error);
this.handleConnectionError(error);
}
}
/**
* Disconnect from the SSE endpoint
*/
disconnect() {
if (this.connectionTimer) {
clearTimeout(this.connectionTimer);
this.connectionTimer = null;
}
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
if (this.status !== "error" /* ERROR */) {
this.setStatus("disconnected" /* DISCONNECTED */);
}
}
}
/**
* Register an event handler for a specific event type
*/
on(eventType, callback) {
if (!this.eventHandlers.has(eventType)) {
this.eventHandlers.set(eventType, /* @__PURE__ */ new Set());
if (this.eventSource && this.eventSource.readyState === EventSource.OPEN) {
this.eventSource.addEventListener(eventType, this.handleEvent.bind(this));
}
}
const handlers = this.eventHandlers.get(eventType);
handlers.add(callback);
return () => {
if (this.eventHandlers.has(eventType)) {
const handlers2 = this.eventHandlers.get(eventType);
handlers2.delete(callback);
if (handlers2.size === 0) {
this.eventHandlers.delete(eventType);
}
}
};
}
/**
* Remove all event handlers for a specific event type
*/
off(eventType) {
this.eventHandlers.delete(eventType);
}
/**
* Attempt to reconnect with exponential backoff
*/
attemptReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error(`SSE maximum reconnection attempts (${this.maxReconnectAttempts}) reached`);
this.setStatus("error" /* ERROR */);
return;
}
this.reconnectAttempts++;
this.setStatus("reconnecting" /* RECONNECTING */);
const baseDelay = this.reconnectTimeout;
const exponentialDelay = baseDelay * Math.pow(1.5, this.reconnectAttempts - 1);
const jitter = 0.2 * exponentialDelay;
const delay = exponentialDelay + Math.random() * jitter;
console.log(`SSE reconnecting in ${Math.round(delay)}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => {
this.connect();
}, delay);
}
/**
* Start heartbeat to detect connection issues
*/
startHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
this.heartbeatTimer = setInterval(() => {
if (this.eventSource && this.eventSource.readyState === EventSource.OPEN) {
this.dispatchEvent("heartbeat", new MessageEvent("heartbeat", {
data: JSON.stringify({
timestamp: Date.now()
})
}));
} else if (this.eventSource && this.eventSource.readyState === EventSource.CLOSED) {
console.warn("SSE connection unexpectedly closed during heartbeat check");
this.disconnect();
if (this.autoReconnect) {
this.attemptReconnect();
}
}
}, this.heartbeatInterval);
}
/**
* Handle SSE connection open
*/
handleOpen(event) {
if (this.connectionTimer) {
clearTimeout(this.connectionTimer);
this.connectionTimer = null;
}
console.log("SSE connection opened");
this.setStatus("connected" /* CONNECTED */);
this.reconnectAttempts = 0;
this.startHeartbeat();
this.dispatchEvent("open", new MessageEvent("open"));
}
/**
* Handle SSE connection error
*/
handleError(event) {
console.error("SSE connection error:", event);
this.dispatchEvent("error", new MessageEvent("error"));
this.setStatus("error" /* ERROR */);
if (this.autoReconnect) {
this.disconnect();
this.attemptReconnect();
}
}
/**
* Handle connection initialization error
*/
handleConnectionError(error) {
if (this.connectionTimer) {
clearTimeout(this.connectionTimer);
this.connectionTimer = null;
}
this.setStatus("error" /* ERROR */);
this.dispatchEvent("error", new MessageEvent("error", {
data: error instanceof Error ? error.message : String(error)
}));
if (this.autoReconnect) {
this.attemptReconnect();
}
}
/**
* Handle generic message event
*/
handleMessage(event) {
if (event.lastEventId) {
this.lastEventId = event.lastEventId;
}
try {
const data = JSON.parse(event.data);
if (data.type && typeof data.type === "string") {
this.dispatchEvent(data.type, new MessageEvent(data.type, {
data: data.payload || data,
lastEventId: event.lastEventId
}));
}
} catch (e) {
}
this.reconnectAttempts = 0;
this.dispatchEvent("message", event);
}
/**
* Handle specific event type
*/
handleEvent(event) {
this.dispatchEvent(event.type, event);
}
/**
* Dispatch an event to all registered handlers
*/
dispatchEvent(eventType, event) {
if (this.eventHandlers.has(eventType)) {
const handlers = this.eventHandlers.get(eventType);
for (const handler of handlers) {
try {
handler(event);
} catch (error) {
console.error(`Error in SSE event handler for ${eventType}:`, error);
}
}
}
}
};
__name(_MCPSSEClient, "MCPSSEClient");
var MCPSSEClient = _MCPSSEClient;
function createSSEClient(options) {
return new MCPSSEClient(options);
}
__name(createSSEClient, "createSSEClient");
export { MCPSSEClient, SSEConnectionStatus, createSSEClient };
//# sourceMappingURL=sse.mjs.map
//# sourceMappingURL=sse.mjs.map