Skip to main content
Glama

MCPControl

index.ts5.97 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { setupTools } from './handlers/tools.js'; import { loadConfig } from './config.js'; import { createAutomationProvider } from './providers/factory.js'; import { AutomationProvider } from './interfaces/provider.js'; import { createHttpServer, DEFAULT_PORT } from './server.js'; class MCPControlServer { private useSse: boolean; private port?: number; private server: Server; private useHttps: boolean; private certPath?: string; private keyPath?: string; /** * Automation provider instance used for system interaction * The provider implements keyboard, mouse, screen, and clipboard functionality * through a consistent interface allowing for different backend implementations */ private provider: AutomationProvider; /** * HTTP server instance if SSE mode is enabled */ private httpServer?: ReturnType<typeof createHttpServer>; constructor( useSse: boolean, port?: number, useHttps = false, certPath?: string, keyPath?: string, ) { this.useSse = useSse; this.port = port; this.useHttps = useHttps; this.certPath = certPath; this.keyPath = keyPath; try { // Load configuration const config = loadConfig(); // Validate configuration if (!config || typeof config.provider !== 'string') { throw new Error('Invalid configuration: provider property is missing or invalid'); } // Validate that the provider is supported const supportedProviders = ['keysender']; // add others as they become available if (!supportedProviders.includes(config.provider.toLowerCase())) { throw new Error( `Unsupported provider: ${config.provider}. Supported providers: ${supportedProviders.join(', ')}`, ); } // Create automation provider based on configuration this.provider = createAutomationProvider(config); this.server = new Server( { name: 'mcp-control', version: '0.1.21-alpha.2', }, { capabilities: { tools: {}, }, }, ); this.setupHandlers(); this.setupErrorHandling(); } catch (error) { // Using process.stderr.write to avoid affecting the JSON-RPC stream process.stderr.write( `Failed to initialize MCP Control Server: ${error instanceof Error ? error.message : String(error)}\n`, ); // Log additional shutdown information process.stderr.write('Server initialization failed. Application will now exit.\n'); // Exit with non-zero status to indicate error process.exit(1); } } private setupHandlers(): void { // Set up tools with Zod validation setupTools(this.server, this.provider); } private setupErrorHandling(): void { this.server.onerror = (error) => { // Using process.stderr.write to avoid affecting the JSON-RPC stream process.stderr.write( `[MCP Error] ${error instanceof Error ? error.message : String(error)}\n`, ); }; process.on('SIGINT', () => { if (this.httpServer) { this.httpServer.httpServer.close(); } void this.server.close().then(() => process.exit(0)); }); } async run(): Promise<void> { // Create the StdioServerTransport for standard MCP communication const transport = new StdioServerTransport(); await this.server.connect(transport); // Using process.stderr.write to avoid affecting the JSON-RPC stream process.stderr.write( `MCP Control server running on stdio (using ${this.provider.constructor.name})\n`, ); // Start HTTP server with SSE support if requested if (this.useSse) { const port = this.port ?? DEFAULT_PORT; try { this.httpServer = createHttpServer( this.server, port, this.useHttps, this.certPath, this.keyPath, ); } catch (error) { process.stderr.write( `Failed to create ${this.useHttps ? 'HTTPS' : 'HTTP'} server: ${ error instanceof Error ? error.message : String(error) }\n`, ); void this.server.close().then(() => process.exit(1)); return; } // Set up error handler for HTTP server this.httpServer.httpServer.on('error', (err) => { process.stderr.write(`Failed to start HTTP server: ${err.message}\n`); }); const protocol = this.useHttps ? 'HTTPS' : 'HTTP'; process.stderr.write(`${protocol}/SSE server enabled on port ${port}\n`); } } } // Parse CLI flags for SSE mode and port const args = process.argv.slice(2); const useSse = args.includes('--sse'); const useHttps = args.includes('--https'); let port: number | undefined; let certPath: string | undefined; let keyPath: string | undefined; // Parse port const portIndex = args.indexOf('--port'); if (portIndex >= 0 && args[portIndex + 1]) { const parsed = parseInt(args[portIndex + 1], 10); if (!Number.isNaN(parsed)) { port = parsed; } } // Parse certificate and key paths for HTTPS const certIndex = args.indexOf('--cert'); if (certIndex >= 0 && args[certIndex + 1]) { certPath = args[certIndex + 1]; } const keyIndex = args.indexOf('--key'); if (keyIndex >= 0 && args[keyIndex + 1]) { keyPath = args[keyIndex + 1]; } // Validate HTTPS configuration if (useHttps && (!certPath || !keyPath)) { process.stderr.write('Error: --cert and --key are required when using --https\n'); process.exit(1); } const server = new MCPControlServer(useSse, port, useHttps, certPath, keyPath); server.run().catch((err) => { // Using process.stderr.write to avoid affecting the JSON-RPC stream process.stderr.write( `Error starting server: ${err instanceof Error ? err.message : String(err)}\n`, ); });

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/claude-did-this/MCPControl'

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