Skip to main content
Glama

RateSpot MCP Server

by zad0xlik
FileServerManager.ts•6.12 kB
import * as http from 'node:http'; import * as net from 'node:net'; import * as path from 'node:path'; import * as fs from 'node:fs'; export class FileServerManager { private static instance: FileServerManager | null = null; private server: http.Server | null = null; private currentPort: number | null = null; private isStarting: boolean = false; private startPromise: Promise<void> | null = null; private constructor() {} static getInstance(): FileServerManager { if (!FileServerManager.instance) { FileServerManager.instance = new FileServerManager(); } return FileServerManager.instance; } async findAvailablePort(strategy: 'random' | 'increment' = 'random', startPort: number = 3001): Promise<number> { console.error(`Finding available port using ${strategy} strategy`); if (strategy === 'random') { const MIN_PORT = 3000; const MAX_PORT = 9000; const MAX_ATTEMPTS = 50; for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { const port = Math.floor(Math.random() * (MAX_PORT - MIN_PORT)) + MIN_PORT; console.error(`Trying random port ${port} (attempt ${attempt + 1}/${MAX_ATTEMPTS})`); if (await this.isPortAvailable(port)) { console.error(`Found available random port: ${port}`); return port; } } throw new Error(`Could not find available port after ${MAX_ATTEMPTS} attempts`); } else { let port = startPort; const MAX_INCREMENT = 100; for (let i = 0; i < MAX_INCREMENT; i++) { console.error(`Trying port ${port}`); if (await this.isPortAvailable(port)) { console.error(`Found available port: ${port}`); return port; } port++; } throw new Error(`Could not find available port after trying ${MAX_INCREMENT} ports starting from ${startPort}`); } } private async isPortAvailable(port: number): Promise<boolean> { return new Promise(resolve => { const tester = net.createServer() .once('error', (err: any) => { console.error(`Port ${port} test error:`, err.code); resolve(false); }) .once('listening', () => { tester.close(); resolve(true); }) .listen(port); }); } async ensureServerRunning(dataDir: string): Promise<void> { if (this.server) { console.error('File server already running'); return; } if (this.isStarting && this.startPromise) { console.error('File server is already starting, waiting...'); return this.startPromise; } this.isStarting = true; this.startPromise = this.startServer(dataDir); try { await this.startPromise; } finally { this.isStarting = false; this.startPromise = null; } } private async startServer(dataDir: string): Promise<void> { try { this.currentPort = await this.findAvailablePort('increment'); return new Promise((resolve, reject) => { this.server = http.createServer((req, res) => { const url = new URL(req.url!, `http://localhost:${this.currentPort}`); if (url.pathname.startsWith('/download/')) { const fileName = url.pathname.replace('/download/', ''); const filePath = path.join(dataDir, fileName); if (fs.existsSync(filePath)) { console.error(`Serving file: ${filePath}`); res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`); res.setHeader('Access-Control-Allow-Origin', '*'); const fileStream = fs.createReadStream(filePath); fileStream.pipe(res); } else { console.error(`File not found: ${filePath}`); res.statusCode = 404; res.end('File not found'); } } else if (url.pathname === '/list') { try { const files = fs.readdirSync(dataDir) .filter(file => file.endsWith('.csv')) .map(file => { const filePath = path.join(dataDir, file); const stats = fs.statSync(filePath); return { name: file, size: stats.size, created: stats.birthtime, downloadUrl: `http://localhost:${this.currentPort}/download/${file}` }; }); res.setHeader('Content-Type', 'application/json'); res.setHeader('Access-Control-Allow-Origin', '*'); res.end(JSON.stringify(files, null, 2)); } catch (error) { console.error('Error listing files:', error); res.statusCode = 500; res.end('Error listing files'); } } else { res.statusCode = 404; res.end('Not found'); } }); this.server.on('error', (err) => { console.error('File server error:', err); reject(err); }); this.server.listen(this.currentPort, () => { console.error(`File server running on http://localhost:${this.currentPort}`); resolve(); }); }); } catch (error) { console.error('Failed to start file server:', error); throw error; } } async getDownloadUrl(fileName: string): Promise<string> { if (!this.server || !this.currentPort) { throw new Error('File server not running'); } return `http://localhost:${this.currentPort}/download/${fileName}`; } async shutdown(): Promise<void> { return new Promise((resolve) => { if (this.server) { console.error('Shutting down file server'); this.server.close(() => { this.server = null; this.currentPort = null; resolve(); }); } else { resolve(); } }); } }

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/zad0xlik/ratespot-mcp'

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