Skip to main content
Glama
connect.ts6.84 kB
import * as WebSocket from "ws" import { generateSocketId, generateMessageId, addSocket, updateSocketStatus, addMessage, getSocket, type SocketInstance, type Message } from "../socket-instances" export interface ConnectParams { url: string protocols?: string[] headers?: Record<string, string> autoReconnect?: boolean maxReconnectAttempts?: number reconnectInterval?: number messageHistoryLimit?: number } export async function connectSocket(params: ConnectParams): Promise<string> { const { url, protocols, headers, autoReconnect = false, maxReconnectAttempts = 5, reconnectInterval = 1000, messageHistoryLimit = 100 } = params // Validate URL if (!url.startsWith('ws://') && !url.startsWith('wss://')) { return JSON.stringify({ success: false, error: "Invalid WebSocket URL. Must start with ws:// or wss://", }, null, 2) } // Generate unique socket ID const socketId = generateSocketId() // Create WebSocket options const options: WebSocket.ClientOptions = { ...(headers && { headers }), } // Create WebSocket instance const socket = new WebSocket.WebSocket(url, protocols, options) // Create socket instance const socketInstance: SocketInstance = { id: socketId, socket, url, createdAt: new Date(), status: 'connecting', messageQueue: [], config: { autoReconnect, maxReconnectAttempts, reconnectInterval, messageHistoryLimit, protocols, headers, }, reconnectAttempts: 0, lastActivity: new Date(), } // Add to global store addSocket(socketInstance) // Set up event handlers socket.on('open', () => { updateSocketStatus(socketId, 'open') socketInstance.reconnectAttempts = 0 // Add connection message const message: Message = { id: generateMessageId(), type: 'received', data: JSON.stringify({ event: 'connected', timestamp: new Date() }), timestamp: new Date(), } addMessage(socketId, message) }) socket.on('message', (data: WebSocket.Data) => { const message: Message = { id: generateMessageId(), type: 'received', data: data instanceof Buffer ? data : data.toString(), timestamp: new Date(), } addMessage(socketId, message) }) socket.on('error', (error: Error) => { const message: Message = { id: generateMessageId(), type: 'received', data: JSON.stringify({ event: 'error', error: error.message, timestamp: new Date() }), timestamp: new Date(), } addMessage(socketId, message) }) socket.on('close', (code: number, reason: Buffer) => { updateSocketStatus(socketId, 'closed') const message: Message = { id: generateMessageId(), type: 'received', data: JSON.stringify({ event: 'disconnected', code, reason: reason.toString(), timestamp: new Date() }), timestamp: new Date(), } addMessage(socketId, message) // Handle auto-reconnection if (autoReconnect && socketInstance.reconnectAttempts! < maxReconnectAttempts) { socketInstance.reconnectAttempts!++ const delay = reconnectInterval * Math.pow(2, socketInstance.reconnectAttempts! - 1) setTimeout(() => { const existingSocket = getSocket(socketId) if (existingSocket && existingSocket.status === 'closed') { reconnectSocketInstance(existingSocket) } }, delay) } }) // Wait for connection or error return new Promise((resolve) => { const timeout = setTimeout(() => { resolve(JSON.stringify({ success: false, error: "Connection timeout", socketId, status: socketInstance.status, }, null, 2)) }, 10000) // 10 second timeout socket.once('open', () => { clearTimeout(timeout) resolve(JSON.stringify({ success: true, socketId, url, status: 'open', message: `WebSocket connected to ${url}`, config: { autoReconnect, messageHistoryLimit, protocols, }, }, null, 2)) }) socket.once('error', (error) => { clearTimeout(timeout) resolve(JSON.stringify({ success: false, error: error.message, socketId, status: 'error', }, null, 2)) }) }) } // Reconnect socket function export function reconnectSocketInstance(instance: SocketInstance) { const { url, config } = instance const options: WebSocket.ClientOptions = { ...(config.headers && { headers: config.headers }), } const newSocket = new WebSocket.WebSocket(url, config.protocols, options) instance.socket = newSocket updateSocketStatus(instance.id, 'connecting') // Re-attach event handlers newSocket.on('open', () => { updateSocketStatus(instance.id, 'open') instance.reconnectAttempts = 0 const message: Message = { id: generateMessageId(), type: 'received', data: JSON.stringify({ event: 'reconnected', attempt: instance.reconnectAttempts, timestamp: new Date() }), timestamp: new Date(), } addMessage(instance.id, message) }) newSocket.on('message', (data: WebSocket.Data) => { const message: Message = { id: generateMessageId(), type: 'received', data: data instanceof Buffer ? data : data.toString(), timestamp: new Date(), } addMessage(instance.id, message) }) newSocket.on('error', (error: Error) => { const message: Message = { id: generateMessageId(), type: 'received', data: JSON.stringify({ event: 'error', error: error.message, timestamp: new Date() }), timestamp: new Date(), } addMessage(instance.id, message) }) newSocket.on('close', (code: number, reason: Buffer) => { updateSocketStatus(instance.id, 'closed') const message: Message = { id: generateMessageId(), type: 'received', data: JSON.stringify({ event: 'disconnected', code, reason: reason.toString(), timestamp: new Date() }), timestamp: new Date(), } addMessage(instance.id, message) // Continue reconnection attempts if (instance.config.autoReconnect && instance.reconnectAttempts! < instance.config.maxReconnectAttempts!) { instance.reconnectAttempts!++ const delay = instance.config.reconnectInterval! * Math.pow(2, instance.reconnectAttempts! - 1) setTimeout(() => { const existingSocket = getSocket(instance.id) if (existingSocket && existingSocket.status === 'closed') { reconnectSocketInstance(instance) } }, delay) } }) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/matiasngf/mcp-fetch'

If you have feedback or need assistance with the MCP directory API, please join our Discord server