Skip to main content
Glama
server.js7.21 kB
#!/usr/bin/env node /** * QoutaMCP - HTTP/SSE Server for Remote Deployment * Run with: node server.js * * Endpoints: * - GET /sse - SSE connection for MCP protocol * - POST /message - Send messages to MCP server * - GET /health - Health check */ import express from 'express'; import cors from 'cors'; import { randomUUID } from 'crypto'; // Import tools import { inspectProject } from './src/tools/inspectProject.js'; import { detectStack } from './src/tools/detectStack.js'; import { listKeyFiles } from './src/tools/listKeyFiles.js'; const app = express(); const PORT = process.env.PORT || 3000; app.use(cors()); app.use(express.json()); // Store active SSE connections const clients = new Map(); // Tool definitions const tools = { inspect_project: { name: "inspect_project", description: "Analyze a project directory and return a complete snapshot including detected languages, frameworks, entry points, structure summary, and dependencies. This is the primary tool for understanding a project.", inputSchema: { type: "object", properties: { path: { type: "string", description: "Path to the project directory to inspect" }, maxDepth: { type: "number", description: "Maximum depth to scan (default: 3)" }, includeHidden: { type: "boolean", description: "Include hidden files and directories" } }, required: ["path"] }, handler: inspectProject }, detect_stack: { name: "detect_stack", description: "Quickly detect the technology stack of a project. Faster and lighter than inspect_project, returns runtime, framework, database hints, and frontend info.", inputSchema: { type: "object", properties: { path: { type: "string", description: "Path to the project directory" } }, required: ["path"] }, handler: detectStack }, list_key_files: { name: "list_key_files", description: "List key files in a project, categorized by purpose: entry points, config files, and documentation.", inputSchema: { type: "object", properties: { path: { type: "string", description: "Path to the project directory" } }, required: ["path"] }, handler: listKeyFiles } }; // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'ok', server: 'qoutamcp', version: '1.0.0', tools: Object.keys(tools) }); }); // SSE endpoint for MCP protocol app.get('/sse', (req, res) => { const clientId = randomUUID(); res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); res.setHeader('X-Accel-Buffering', 'no'); // Send endpoint info res.write(`event: endpoint\ndata: /message?clientId=${clientId}\n\n`); // Store client connection clients.set(clientId, res); console.log(`Client connected: ${clientId}`); // Handle disconnect req.on('close', () => { clients.delete(clientId); console.log(`Client disconnected: ${clientId}`); }); }); // Message endpoint for receiving requests app.post('/message', async (req, res) => { const clientId = req.query.clientId; const client = clients.get(clientId); if (!client) { return res.status(400).json({ error: 'Invalid or expired client session' }); } const { jsonrpc, id, method, params } = req.body; try { let result; switch (method) { case 'initialize': result = { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "qoutamcp", version: "1.0.0" } }; break; case 'tools/list': result = { tools: Object.values(tools).map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })) }; break; case 'tools/call': const tool = tools[params.name]; if (!tool) { throw new Error(`Unknown tool: ${params.name}`); } const toolResult = await tool.handler(params.arguments); result = { content: [ { type: "text", text: JSON.stringify(toolResult, null, 2) } ] }; break; case 'notifications/initialized': // No response needed for notifications return res.status(204).send(); default: throw new Error(`Unknown method: ${method}`); } // Send response via SSE const response = { jsonrpc: "2.0", id, result }; client.write(`event: message\ndata: ${JSON.stringify(response)}\n\n`); res.status(202).json({ status: 'accepted' }); } catch (error) { const errorResponse = { jsonrpc: "2.0", id, error: { code: -32603, message: error.message } }; client.write(`event: message\ndata: ${JSON.stringify(errorResponse)}\n\n`); res.status(202).json({ status: 'accepted' }); } }); // Start server app.listen(PORT, () => { console.log(` ╔══════════════════════════════════════════════════════════╗ ║ QoutaMCP Server ║ ╠══════════════════════════════════════════════════════════╣ ║ HTTP/SSE Mode - Ready for remote connections ║ ║ ║ ║ Endpoints: ║ ║ • SSE: http://localhost:${PORT}/sse ║ ║ • Message: http://localhost:${PORT}/message ║ ║ • Health: http://localhost:${PORT}/health ║ ║ ║ ║ Available Tools: ║ ║ • inspect_project ║ ║ • detect_stack ║ ║ • list_key_files ║ ╚══════════════════════════════════════════════════════════╝ `); });

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/QoutaID/qoutaMcp'

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