capacitor-cors-bypass-enhanced
Version:
Enhanced Capacitor plugin for CORS bypass with HTTP/2, HTTP/3, gRPC, GraphQL, file operations, and advanced networking features. Modular TypeScript definitions for better maintainability.
193 lines (192 loc) • 6.29 kB
JavaScript
/**
* MCP Client Manager (Web Implementation)
* Manages MCP connections with StreamableHTTP transport
*/
import { StreamableHTTPTransport } from './streamable-http';
export class MCPClientManager {
constructor() {
this.connections = new Map();
this.connectionCounter = 0;
}
/**
* Create a new MCP client connection
*/
async createClient(options) {
this.connectionCounter++;
const connectionId = `mcp_web_${this.connectionCounter}`;
// Determine transport type
const transport = options.transport || 'streamablehttp';
// Get endpoint URL
let url = options.url;
if (!url && options.sseUrl) {
// Backward compatibility
url = options.sseUrl;
}
if (!url) {
throw new Error('URL is required for MCP client');
}
if (transport === 'streamablehttp') {
return this.createStreamableHTTPClient(connectionId, url, options);
}
else if (transport === 'sse') {
throw new Error('Legacy SSE transport not yet implemented. Use StreamableHTTP instead.');
}
else {
throw new Error(`Unsupported transport: ${transport}`);
}
}
/**
* Create a StreamableHTTP MCP client
*/
async createStreamableHTTPClient(connectionId, url, options) {
const listeners = new Map();
const streamableTransport = new StreamableHTTPTransport(url, {
onMessage: (message) => {
this.notifyListeners(connectionId, 'message', { connectionId, message });
},
onError: (error) => {
this.notifyListeners(connectionId, 'error', { connectionId, error });
},
onConnectionStateChange: (state) => {
this.notifyListeners(connectionId, 'stateChange', { connectionId, state });
},
}, options.resumable || false, options.sessionId, options.lastSequence);
const clientInfo = {
connectionId,
status: 'connecting',
};
this.connections.set(connectionId, {
transport: streamableTransport,
info: clientInfo,
listeners,
});
// Send initialize request
try {
const initRequest = {
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: options.protocolVersion || '2025-03-26',
clientInfo: options.clientInfo,
capabilities: options.capabilities,
},
};
await streamableTransport.sendMessage(initRequest, true);
clientInfo.status = 'connected';
}
catch (error) {
clientInfo.status = 'error';
throw error;
}
return clientInfo;
}
/**
* Send a message to an MCP server
*/
async sendMessage(connectionId, message, expectStream = false) {
const connection = this.connections.get(connectionId);
if (!connection) {
throw new Error(`MCP connection not found: ${connectionId}`);
}
await connection.transport.sendMessage(message, expectStream);
}
/**
* Open a listen stream for server-initiated messages
*/
async openListenStream(connectionId) {
const connection = this.connections.get(connectionId);
if (!connection) {
throw new Error(`MCP connection not found: ${connectionId}`);
}
await connection.transport.openListenStream();
}
/**
* Close an MCP client connection
*/
closeClient(connectionId) {
const connection = this.connections.get(connectionId);
if (connection) {
connection.transport.close();
connection.info.status = 'disconnected';
this.connections.delete(connectionId);
}
}
/**
* Get session information for a connection
*/
getSessionInfo(connectionId) {
const connection = this.connections.get(connectionId);
if (!connection) {
return null;
}
return {
sessionId: connection.transport.getSessionId(),
lastSequence: connection.transport.getLastSequence(),
resumable: connection.transport.isResumable(),
};
}
/**
* Get client info
*/
getClientInfo(connectionId) {
const connection = this.connections.get(connectionId);
return connection ? connection.info : null;
}
/**
* Add event listener
*/
addEventListener(connectionId, event, callback) {
const connection = this.connections.get(connectionId);
if (!connection) {
throw new Error(`MCP connection not found: ${connectionId}`);
}
if (!connection.listeners.has(event)) {
connection.listeners.set(event, new Set());
}
connection.listeners.get(event).add(callback);
}
/**
* Remove event listener
*/
removeEventListener(connectionId, event, callback) {
const connection = this.connections.get(connectionId);
if (!connection) {
return;
}
const listeners = connection.listeners.get(event);
if (listeners) {
listeners.delete(callback);
}
}
/**
* Notify listeners
*/
notifyListeners(connectionId, event, data) {
const connection = this.connections.get(connectionId);
if (!connection) {
return;
}
const listeners = connection.listeners.get(event);
if (listeners) {
listeners.forEach((callback) => {
try {
callback(data);
}
catch (error) {
console.error('Error in MCP event listener:', error);
}
});
}
}
/**
* Close all connections
*/
closeAll() {
this.connections.forEach((connection, connectionId) => {
this.closeClient(connectionId);
});
}
}
// Export singleton instance
export const mcpClientManager = new MCPClientManager();