Skip to main content
Glama
mcp.ts3.73 kB
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; import { createRequestLogger } from '../utils/logger.js'; import { mcpHandshakeTotal, mcpHandshakeDuration } from '../utils/metrics.js'; interface McpRequestBody { method?: string; params?: unknown; id?: string | number; } export async function mcpRoutes(fastify: FastifyInstance) { // CORS preflight is handled by the @fastify/cors plugin // MCP Streamable HTTP endpoint fastify.post('/mcp', async (request: FastifyRequest<{ Body: McpRequestBody }>, reply: FastifyReply) => { const startTime = Date.now(); const logger = createRequestLogger(request); const clientType = request.headers['user-agent']?.includes('mcp-inspector') ? 'mcp-inspector' : 'unknown'; logger.info({ event: 'mcp.handshake.start', client: clientType, body: request.body }, 'MCP handshake initiated'); try { // Set up Server-Sent Events headers reply .header('Content-Type', 'text/event-stream') .header('Cache-Control', 'no-store') .header('Connection', 'keep-alive') .header('Access-Control-Allow-Origin', '*') .status(200); // Send initial SSE response with Hello, World const response = { jsonrpc: '2.0', id: request.body.id || null, result: { message: 'Hello, World', timestamp: new Date().toISOString(), server: 'mcp-hello-world', version: '0.1.0' } }; // Format as SSE const sseData = `data: ${JSON.stringify(response)}\n\n`; reply.raw.write(sseData); // Log successful handshake const duration = (Date.now() - startTime) / 1000; logger.info({ event: 'mcp.handshake.success', client: clientType, latency_ms: Date.now() - startTime, duration_s: duration }, 'MCP handshake completed'); // Update metrics mcpHandshakeTotal.inc({ client: clientType, status: 'success' }); mcpHandshakeDuration.observe({ client: clientType, status: 'success' }, duration); // Keep connection open briefly then close gracefully setTimeout(() => { reply.raw.end(); }, 100); } catch (error) { const duration = (Date.now() - startTime) / 1000; logger.error({ event: 'mcp.handshake.error', client: clientType, error: error instanceof Error ? error.message : 'unknown', duration_s: duration }, 'MCP handshake failed'); // Update error metrics mcpHandshakeTotal.inc({ client: clientType, status: 'error' }); mcpHandshakeDuration.observe({ client: clientType, status: 'error' }, duration); // Send error response const errorResponse = { jsonrpc: '2.0', id: request.body.id || null, error: { code: -32603, message: 'Internal error', data: process.env.NODE_ENV === 'development' ? (error instanceof Error ? error.message : 'unknown') : undefined } }; reply.status(500).send(errorResponse); } }); // Handle other HTTP methods with helpful error const methodNotAllowedHandler = async (_request: FastifyRequest, reply: FastifyReply) => { reply.status(405).send({ error: { message: 'Method Not Allowed. Use POST for MCP requests.', code: 405, allowed_methods: ['POST', 'OPTIONS'] } }); }; fastify.get('/mcp', methodNotAllowedHandler); fastify.put('/mcp', methodNotAllowedHandler); fastify.delete('/mcp', methodNotAllowedHandler); fastify.patch('/mcp', methodNotAllowedHandler); fastify.head('/mcp', methodNotAllowedHandler); }

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/MillCityAI/mcp-hello-world'

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