Skip to main content
Glama
http-server.js4.77 kB
#!/usr/bin/env node import express from 'express'; import cors from 'cors'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { captionsTool, handleCaptionsTool } from './tools/captions-tool.js'; import { workbookTools, handleWorkbookTools } from './tools/workbook-tools.js'; import { watchtowerTools, handleWatchtowerTools } from './tools/watchtower-tools.js'; import { searchBibleBooksTool, getBibleVerseTool, getVerseWithStudyTool, getBibleVerseURLTool, handleScriptureTools } from './tools/scripture-tools.js'; const app = express(); app.use(cors({ origin: '*', exposedHeaders: ['mcp-session-id', 'Mcp-Session-Id', 'Content-Type'], allowedHeaders: ['Content-Type', 'mcp-session-id', 'Mcp-Session-Id'] })); // NOTE: Do NOT use express.json() middleware here! // StreamableHTTPServerTransport needs access to the raw request stream. // express.json() consumes the stream, causing "stream is not readable" errors. // Collect all tools from different modules const allTools = [ captionsTool, ...workbookTools, ...watchtowerTools, searchBibleBooksTool, getBibleVerseTool, getVerseWithStudyTool, getBibleVerseURLTool ]; // Collect all tool handlers const toolHandlers = [ handleCaptionsTool, handleWorkbookTools, handleWatchtowerTools, handleScriptureTools ]; // Function to create and configure a new MCP Server instance function createServer() { const server = new Server( { name: 'jw-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Register tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: allTools, }; }); // Handle tool calls by delegating to appropriate handlers server.setRequestHandler(CallToolRequestSchema, async (request) => { // Try each handler until one returns a result for (const handler of toolHandlers) { const result = await handler(request); if (result !== null) { return result; } } // If no handler matched, return error return { content: [ { type: 'text', text: `Unknown tool: ${request.params.name}`, }, ], isError: true, }; }); return server; } // STATELESS MODE: Create fresh Server + Transport instances for EVERY request // This ensures compatibility with Smithery's distributed architecture where // requests may be routed to different container instances without sticky sessions. app.post('/mcp', async (req, res) => { // Create fresh instances for this request only const server = createServer(); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined // Critical: undefined for stateless mode }); try { // Connect server to transport await server.connect(transport); // Set up cleanup when response finishes res.on('close', () => { try { transport.close?.(); server.close?.(); } catch (cleanupError) { console.error('Error during cleanup:', cleanupError); } }); // Handle the request await transport.handleRequest(req, res); } catch (error) { console.error('Error handling POST request:', error); console.error('Error stack:', error.stack); if (!res.headersSent) { res.status(500).json({ error: 'Internal server error', details: error.message }); } } }); // GET endpoint - SSE streaming not supported in stateless mode // Stateless servers cannot maintain persistent connections for streaming app.get('/mcp', (req, res) => { res.status(501).json({ error: 'Not Implemented', message: 'SSE streaming (GET requests) is not supported in stateless mode. Use POST for all MCP requests.', mode: 'stateless-per-request' }); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'healthy', transport: 'streamable-http', mode: 'stateless-per-request', description: 'Each request creates fresh Server and Transport instances for Smithery compatibility' }); }); // Start the server const PORT = process.env.PORT || 8081; app.listen(PORT, () => { console.error(`JW MCP Server running in STATELESS mode`); console.error(`Transport: Streamable HTTP (per-request instances)`); console.error(`Port: ${PORT}`); console.error(`POST endpoint: http://localhost:${PORT}/mcp`); console.error(`Health check: http://localhost:${PORT}/health`); console.error(`Note: SSE streaming (GET) not supported in stateless mode`); });

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/advenimus/jw-mcp'

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