Skip to main content
Glama
proxy.ts5.18 kB
#!/usr/bin/env node import type { ChildProcess } from 'child_process' import { spawn } from 'child_process' import * as readline from 'readline' import * as fs from 'fs' const DEBUG = process.env.DEBUG === 'true' const LOG_FILE = '/tmp/shortcut-mcp-proxy.log' function log(message: string) { if (DEBUG) { fs.appendFileSync(LOG_FILE, `${new Date().toISOString()} - ${message}\n`) } } class MCPProxy { private serverProcess: ChildProcess | null = null private inputBuffer: string[] = [] private outputBuffer: string[] = [] private isRestarting = false private rl: readline.Interface constructor() { // Set up readline interface for stdin this.rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false, }) // Handle input from Claude this.rl.on('line', (line) => { log(`Received from Claude: ${line}`) if (this.isRestarting) { // Buffer input during restart this.inputBuffer.push(line) } else if (this.serverProcess && !this.serverProcess.killed) { // Forward to server this.serverProcess.stdin?.write(line + '\n') } }) // Handle process termination process.on('SIGTERM', () => this.cleanup()) process.on('SIGINT', () => this.cleanup()) process.on('SIGHUP', () => this.reload()) } async start() { log('Starting MCP proxy...') await this.startServer() } private async startServer() { log('Starting server process...') // Spawn the actual MCP server this.serverProcess = spawn('node', ['dist/server.js'], { cwd: process.cwd(), env: process.env, stdio: ['pipe', 'pipe', 'pipe'], }) // Handle server output this.serverProcess.stdout?.on('data', (data) => { const output = data.toString() log(`Server output: ${output}`) if (!this.isRestarting) { process.stdout.write(data) } else { // Buffer output during restart this.outputBuffer.push(output) } }) // Handle server errors this.serverProcess.stderr?.on('data', (data) => { const error = data.toString() log(`Server error: ${error}`) fs.appendFileSync('/tmp/shortcut-mcp-debug.log', error) }) // Handle server exit this.serverProcess.on('exit', (code, signal) => { log(`Server exited with code ${code}, signal ${signal}`) if (!this.isRestarting) { // Unexpected exit - restart setTimeout(() => this.startServer(), 1000) } }) // Process any buffered input while (this.inputBuffer.length > 0) { const line = this.inputBuffer.shift() if (line && this.serverProcess.stdin) { this.serverProcess.stdin.write(line + '\n') } } // Process any buffered output while (this.outputBuffer.length > 0) { const output = this.outputBuffer.shift() if (output) { process.stdout.write(output) } } this.isRestarting = false } private async reload() { log('Reloading server...') this.isRestarting = true // Kill the current server if (this.serverProcess && !this.serverProcess.killed) { this.serverProcess.kill('SIGTERM') // Wait for process to exit await new Promise<void>((resolve) => { const checkInterval = setInterval(() => { if (!this.serverProcess || this.serverProcess.killed) { clearInterval(checkInterval) resolve() } }, 100) // Force kill after 5 seconds setTimeout(() => { if (this.serverProcess && !this.serverProcess.killed) { this.serverProcess.kill('SIGKILL') } }, 5000) }) } // Rebuild the project log('Rebuilding project...') const buildProcess = spawn('pnpm', ['run', 'build'], { cwd: process.cwd(), stdio: ['ignore', 'pipe', 'pipe'], }) await new Promise<void>((resolve) => { buildProcess.on('exit', () => { log('Build complete') resolve() }) }) // Start the new server await this.startServer() log('Reload complete') } private cleanup() { log('Cleaning up...') if (this.serverProcess && !this.serverProcess.killed) { this.serverProcess.kill('SIGTERM') } process.exit(0) } } // Start the proxy const proxy = new MCPProxy() proxy.start().catch((error) => { console.error('Failed to start proxy:', 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/currentspace/shortcut_mcp'

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