Skip to main content
Glama
server-dual-transport.ts5.01 kB
import { UmbrellaMcpServer } from './server.js'; import { ConfigLoader } from './config/config-loader.js'; import { StdioTransport } from './transports/stdio-transport.js'; import { HttpsTransport } from './transports/https-transport.js'; import { CloudflareTunnelManager } from './tunnel/cloudflare-manager.js'; /** * Enhanced MCP Server with dual transport support * Supports both stdio (for backward compatibility) and HTTPS with OAuth */ export class DualTransportMcpServer { private config = ConfigLoader.getInstance(); private mcpServer: UmbrellaMcpServer; private tunnelManager?: CloudflareTunnelManager; constructor() { const umbrellaUrl = this.config.getConfig().umbrella.baseUrl; const frontendUrl = this.config.getConfig().umbrella.frontendBaseUrl; const transportMode = this.config.getTransportMode(); // Pass transport mode to server so it knows whether to expose auth tools this.mcpServer = new UmbrellaMcpServer(umbrellaUrl, frontendUrl); // Log transport mode for debugging if (transportMode === 'https') { console.log('[MAIN] HTTPS mode - authentication via OAuth Bearer tokens only'); } } async start(): Promise<void> { const transportMode = this.config.getTransportMode(); const serverConfig = this.config.getConfig().server; console.log(`[MAIN] Starting MCP server in ${transportMode.toUpperCase()} mode`); console.log(`[MAIN] Debug mode: ${serverConfig.debug ? 'ENABLED' : 'DISABLED'}`); console.log(`[MAIN] Production mode: ${serverConfig.production ? 'YES' : 'NO'}`); if (transportMode === 'stdio') { // Traditional stdio mode const stdioTransport = new StdioTransport(this.mcpServer); await stdioTransport.start(); } else if (transportMode === 'https') { // HTTPS mode with OAuth // Start Cloudflare tunnel if enabled if (this.config.getConfig().cloudflare.enabled) { this.tunnelManager = new CloudflareTunnelManager(); try { console.log('[MAIN] Starting Cloudflare tunnel...'); const tunnelUrl = await this.tunnelManager.startTunnel(); console.log(`[MAIN] Tunnel started successfully: ${tunnelUrl}`); // Give the tunnel a moment to stabilize await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error: any) { console.error(`[MAIN] Failed to start tunnel: ${error.message}`); console.error('[MAIN] Please ensure cloudflared is installed and accessible'); process.exit(1); } } // Start HTTPS server const httpsTransport = new HttpsTransport(this.mcpServer); await httpsTransport.start(); // Print connection instructions this.printConnectionInstructions(); } else { throw new Error(`Unknown transport mode: ${transportMode}`); } // Handle graceful shutdown this.setupShutdownHandlers(); } private printConnectionInstructions(): void { const config = this.config.getConfig(); console.log('\n' + '='.repeat(60)); console.log('MCP SERVER READY'); console.log('='.repeat(60)); if (config.cloudflare.currentTunnelUrl) { console.log('\n📡 TUNNEL URL:', config.cloudflare.currentTunnelUrl); console.log('🔐 MCP ENDPOINT:', `${config.cloudflare.currentTunnelUrl}/mcp`); } else if (config.server.production) { console.log('\n🔐 MCP ENDPOINT:', `${config.auth.issuer}mcp`); } console.log('\n📋 CONFIGURATION:'); console.log(` - Transport: ${config.server.transport}`); console.log(` - Token Expiration: ${config.auth.tokenExpiration}s (${config.auth.tokenExpiration / 3600} hours)`); console.log(` - Debug Mode: ${config.server.debug ? 'ENABLED' : 'DISABLED'}`); console.log('\n🔧 TO CONNECT FROM CLAUDE DESKTOP:'); console.log(' 1. Open Claude Desktop settings'); console.log(' 2. Add a custom MCP server'); console.log(' 3. Use the MCP endpoint URL shown above'); console.log(' 4. Authenticate when prompted'); console.log('\n' + '='.repeat(60) + '\n'); } private setupShutdownHandlers(): void { const cleanup = async () => { console.log('\n[MAIN] Shutting down...'); // Stop tunnel if running if (this.tunnelManager?.isRunning()) { console.log('[MAIN] Stopping Cloudflare tunnel...'); this.tunnelManager.stopTunnel(); } console.log('[MAIN] Shutdown complete'); process.exit(0); }; process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); } /** * Get the MCP server instance (for testing) */ getMcpServer(): UmbrellaMcpServer { return this.mcpServer; } } // Auto-start if run directly if (import.meta.url === `file://${process.argv[1]}`) { const server = new DualTransportMcpServer(); server.start().catch((error) => { console.error('[MAIN] Fatal error:', error); process.exit(1); }); }

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/daviddraiumbrella/invoice-monitoring'

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