Skip to main content
Glama
mcp-stdin-server.js7.44 kB
#!/usr/bin/env node /** * WebDNA MCP Server using stdin/stdout for communication * This version doesn't require HTTP ports and works directly with Windsurf */ require('dotenv').config(); const readline = require('readline'); const { v4: uuidv4 } = require('uuid'); const path = require('path'); const fs = require('fs'); // Import MCP tools from external file const { mcp_tools } = require('./src/mcp-tools'); // Import database and documentation modules const { initializeDatabase } = require('./src/database'); const { searchDocumentation, getDocumentationById, getCategories, getRandomDocumentation, getDocumentationCount } = require('./src/documentation'); // Configure logging const LOG_DIR = path.join(__dirname, 'logs'); if (!fs.existsSync(LOG_DIR)) { fs.mkdirSync(LOG_DIR, { recursive: true }); } const LOG_FILE = path.join(LOG_DIR, `mcp-stdin-server-${new Date().toISOString().split('T')[0]}.log`); const logStream = fs.createWriteStream(LOG_FILE, { flags: 'a' }); // Log to stderr for debugging (won't interfere with stdout communication) const log = (message, data = null) => { const timestamp = new Date().toISOString(); let logString = `[${timestamp}] ${message}`; if (data) { if (typeof data === 'object') { logString += ` ${JSON.stringify(data)}`; } else { logString += ` ${data}`; } } console.error(logString); logStream.write(logString + '\n'); }; // Set up readline interface const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); // Track connected status for rate limiting and health monitoring let isConnected = false; let messageCount = 0; let lastMessageTime = Date.now(); const startTime = Date.now(); // Initialize the system async function initialize() { try { log('Initializing WebDNA MCP Server (stdin/stdout)...'); // Initialize database await initializeDatabase(); isConnected = true; log('WebDNA MCP Server (stdin/stdout) initialization complete'); } catch (error) { log(`Error during initialization: ${error.message}`); } } // Handle incoming messages rl.on('line', async (line) => { messageCount++; lastMessageTime = Date.now(); try { const message = JSON.parse(line); log(`Received message: ${message.type}`); switch (message.type) { case 'init': // Respond to initialization await initialize(); sendMessage({ type: 'ready' }); break; case 'list_tools': // Send available tools sendMessage({ type: 'tools', tools: mcp_tools }); break; case 'invoke_tool': // Handle tool invocation await handleToolInvocation(message); break; default: log(`Unknown message type: ${message.type}`); } } catch (error) { log(`Error processing message: ${error.message}`); log(`Problematic line: ${line}`); // If there's a message ID, send an error response try { const message = JSON.parse(line); if (message.id) { sendMessage({ type: 'tool_error', id: message.id, error: { message: `Error processing request: ${error.message}` } }); } } catch (parseError) { // If we can't parse the message, we can't send a proper error response log(`Cannot parse message to send error response: ${parseError.message}`); } } }); // Send a message to stdout function sendMessage(message) { console.log(JSON.stringify(message)); } // Handle tool invocation async function handleToolInvocation(message) { const { tool, id = uuidv4(), params } = message; log(`Invoking tool: ${tool} with params:`, params); try { switch (tool) { case 'search-webdna-docs': const searchResults = await searchDocumentation( params.query, { limit: params.limit, offset: params.offset, category: params.category } ); sendMessage({ type: 'tool_result', id, result: searchResults }); break; case 'get-webdna-doc': const docResult = await getDocumentationById(params.id); if (!docResult) { sendMessage({ type: 'tool_error', id, error: { message: `Documentation not found for ID: ${params.id}` } }); return; } sendMessage({ type: 'tool_result', id, result: { doc: docResult } }); break; case 'get-webdna-categories': const categories = await getCategories(); sendMessage({ type: 'tool_result', id, result: { categories } }); break; case 'get-random-webdna-docs': const randomDocs = await getRandomDocumentation(params.limit || 5); sendMessage({ type: 'tool_result', id, result: { docs: randomDocs } }); break; case 'get-webdna-stats': const docCount = await getDocumentationCount(); const statsCategories = await getCategories(); sendMessage({ type: 'tool_result', id, result: { total_docs: docCount, total_categories: statsCategories.length, server_uptime: Math.floor((Date.now() - startTime) / 1000), server_version: require('./package.json').version } }); break; default: log(`Unknown tool: ${tool}`); sendMessage({ type: 'tool_error', id, error: { message: `Unknown tool: ${tool}` } }); } } catch (error) { log(`Error invoking tool ${tool}: ${error.message}`); sendMessage({ type: 'tool_error', id, error: { message: `Error invoking tool ${tool}: ${error.message}` } }); } } // Handle process termination process.on('SIGINT', () => { log('Received SIGINT, shutting down'); logStream.end(); process.exit(0); }); process.on('SIGTERM', () => { log('Received SIGTERM, shutting down'); logStream.end(); process.exit(0); }); // Handle uncaught exceptions process.on('uncaughtException', (error) => { log(`Uncaught exception: ${error.message}`, error.stack); // Keep running despite the error }); // Send a ping every 30 seconds to keep the connection alive setInterval(() => { if (isConnected) { sendMessage({ type: 'ping', id: uuidv4(), timestamp: Date.now(), uptime: Math.floor((Date.now() - startTime) / 1000) }); } }, 30000); // Monitor system health setInterval(() => { const timeSinceLastMessage = Date.now() - lastMessageTime; log(`Health check: Connected=${isConnected}, Messages=${messageCount}, Last message=${Math.floor(timeSinceLastMessage / 1000)}s ago, Uptime=${Math.floor((Date.now() - startTime) / 1000)}s`); }, 60000); log('WebDNA MCP Server (stdin/stdout) starting...'); // Initialize the server initialize().then(() => { log('WebDNA MCP Server (stdin/stdout) ready'); });

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/jacgood/webdna-mcp-server'

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