Skip to main content
Glama
raza10006

ElevenLabs MCP Backend

by raza10006
server.ts6.29 kB
/** * Main Fastify server * Exposes MCP endpoint, health check, and REST backup endpoint */ // Load environment variables from .env file import "dotenv/config"; import Fastify from "fastify"; import cors from "@fastify/cors"; import { handleMcpRequest } from "./mcp.js"; import { lookupOrder, formatOrderForAgent } from "./supabase.js"; import { requireAuth } from "./auth.js"; import { logger } from "./logger.js"; import { JsonRpcErrorCode } from "./types.js"; const PORT = parseInt(process.env.PORT || "3000", 10); // Initialize Fastify const fastify = Fastify({ logger: false, // We use our custom logger requestIdLogLabel: "requestId", genReqId: () => `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, }); // Health check endpoint (no auth required) fastify.get("/health", async (_request, _reply) => { return { ok: true, service: "elevenlabs-mcp-backend", timestamp: new Date().toISOString(), }; }); // Debug endpoint to check env vars (remove in production) fastify.get("/debug/env", async (_request, _reply) => { const mcpSecret = process.env.MCP_SECRET; return { hasMcpSecret: !!mcpSecret, mcpSecretLength: mcpSecret?.length || 0, mcpSecretStart: mcpSecret?.substring(0, 20) || "not set", hasSupabaseUrl: !!process.env.SUPABASE_URL, }; }); // MCP JSON-RPC endpoint (requires auth) fastify.post("/mcp", async (request, reply) => { // Check authentication await requireAuth(request, reply); if (reply.sent) { return; // Auth failed, response already sent } const requestId = request.id as string; const contextLogger = logger.withContext({ requestId }); try { // Parse request body let body: unknown; try { body = request.body; } catch (error) { contextLogger.error("Failed to parse request body", error); return reply.status(400).send({ jsonrpc: "2.0", id: null, error: { code: JsonRpcErrorCode.ParseError, message: "Parse error: Invalid JSON", }, }); } // Handle MCP request const response = await handleMcpRequest(body, requestId); return reply.send(response); } catch (error) { contextLogger.error("Unexpected error in /mcp endpoint", error); return reply.status(500).send({ jsonrpc: "2.0", id: null, error: { code: JsonRpcErrorCode.InternalError, message: "Internal server error", }, }); } }); // REST backup endpoint for lookup_order (requires auth) fastify.post("/lookup-order", async (request, reply) => { // Check authentication await requireAuth(request, reply); if (reply.sent) { return; // Auth failed, response already sent } const requestId = request.id as string; const contextLogger = logger.withContext({ requestId }); try { const body = request.body as { order_id?: string }; if (!body || typeof body.order_id !== "string" || body.order_id.trim() === "") { contextLogger.warn("Invalid lookup-order request", { body }); return reply.status(400).send({ error: "Invalid request", message: "order_id is required and must be a non-empty string", }); } const { order_id } = body; contextLogger.info("REST lookup-order request", { orderId: order_id }); const order = await lookupOrder(order_id, { requestId, orderId: order_id, }); if (!order) { contextLogger.info("Order not found via REST", { orderId: order_id }); return reply.status(404).send({ error: "Order not found", message: `Order with ID '${order_id}' was not found`, }); } // Return same data shape as MCP tool const formattedText = formatOrderForAgent(order); return reply.send({ order_id: order.order_id, status: order.status, eta: order.eta, carrier: order.carrier, tracking_number: order.tracking_number, last_update: order.last_update, issue_flag: order.issue_flag, notes: order.notes, formatted_text: formattedText, }); } catch (error) { contextLogger.error("Unexpected error in /lookup-order endpoint", error); return reply.status(500).send({ error: "Internal server error", message: error instanceof Error ? error.message : String(error), }); } }); // Error handler fastify.setErrorHandler((error, request, reply) => { const requestId = request.id as string; logger.error("Fastify error", error, { requestId, path: request.url }); reply.status(error.statusCode || 500).send({ error: "Internal server error", message: error.message, }); }); // 404 handler fastify.setNotFoundHandler((request, reply) => { logger.warn("Route not found", { path: request.url, method: request.method }); reply.status(404).send({ error: "Not Found", message: `Route ${request.method} ${request.url} not found`, }); }); // Start server async function start() { try { // Verify environment variables are loaded const mcpSecret = process.env.MCP_SECRET; if (!mcpSecret) { logger.error("MCP_SECRET not found in environment variables"); throw new Error("MCP_SECRET environment variable is required"); } logger.info("Environment variables loaded", { hasMcpSecret: !!mcpSecret, mcpSecretLength: mcpSecret.length, hasSupabaseUrl: !!process.env.SUPABASE_URL, }); // Register CORS plugin await fastify.register(cors, { origin: true, // Allow all origins (adjust for production) credentials: true, }); const address = await fastify.listen({ port: PORT, host: "0.0.0.0", // Listen on all interfaces for deployment }); logger.info(`Server listening on ${address}`, { port: PORT, environment: process.env.NODE_ENV || "development", }); } catch (error) { logger.error("Failed to start server", error); process.exit(1); } } // Handle graceful shutdown process.on("SIGTERM", async () => { logger.info("SIGTERM received, shutting down gracefully"); await fastify.close(); process.exit(0); }); process.on("SIGINT", async () => { logger.info("SIGINT received, shutting down gracefully"); await fastify.close(); process.exit(0); }); // Start the server start();

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/raza10006/elevenlabs-mcp-backend'

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