Skip to main content
Glama
moonlitknight

MCP TypeScript Template

index.ts4.11 kB
import express from "express"; import { randomUUID } from "node:crypto"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import { createTextResult } from "./lib/utils.ts"; import { logger } from "./logger.ts"; import { getConfig } from "./config.ts"; const getServer = () => { const config = getConfig(); const server = new McpServer({ name: config.SERVER_NAME, version: config.SERVER_VERSION, }); server.registerTool( "echo", { title: "Echo", description: "Echo back the provided message", inputSchema: { message: z.string().describe("The message to echo back"), }, }, async (args) => { const data = { echo: args.message }; return createTextResult(data); }, ); return server; }; const app = express(); app.use(express.json()); const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {}; const mcpHandler = async (req: express.Request, res: express.Response) => { const sessionId = req.headers["mcp-session-id"] as string | undefined; try { // Handle initialization requests (usually POST without session ID) if (req.method === "POST" && !sessionId && isInitializeRequest(req.body)) { logger.info("Initializing new MCP session"); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (sessionId) => { transports[sessionId] = transport; logger.info("MCP session initialized", { sessionId }); }, }); const server = getServer(); await server.connect(transport); await transport.handleRequest(req, res, req.body); return; } // Handle existing session requests if (sessionId && transports[sessionId]) { const transport = transports[sessionId]; await transport.handleRequest(req, res, req.body); return; } // Handle case where no session ID is provided for non-init requests if (req.method === "POST" && !sessionId) { logger.warn( "POST request without session ID for non-initialization request", ); res .status(400) .json({ error: "Session ID required for non-initialization requests" }); return; } // Handle unknown session if (sessionId && !transports[sessionId]) { logger.warn("Request for unknown session", { sessionId }); res.status(404).json({ error: "Session not found" }); return; } // For GET requests without session, return server info if (req.method === "GET") { const config = getConfig(); res.json({ name: config.SERVER_NAME, version: config.SERVER_VERSION, description: "TypeScript template for building MCP servers", capabilities: ["tools"], }); } } catch (error) { logger.error("Error handling MCP request", { error: error instanceof Error ? error.message : error, }); res.status(500).json({ error: "Internal server error" }); } }; // Handle MCP requests on /mcp endpoint app.post("/mcp", mcpHandler); app.get("/mcp", mcpHandler); async function main() { const config = getConfig(); // Graceful shutdown handling process.on("SIGTERM", () => { logger.info("SIGTERM received, shutting down gracefully"); process.exit(0); }); process.on("SIGINT", () => { logger.info("SIGINT received, shutting down gracefully"); process.exit(0); }); app.listen(config.PORT, () => { logger.info( `MCP TypeScript Template Server running on port ${config.PORT}`, { environment: config.NODE_ENV, serverName: config.SERVER_NAME, version: config.SERVER_VERSION, }, ); }); } main().catch((error) => { logger.error("Server startup error", { error: error instanceof Error ? error.message : error, }); process.exit(1); });

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/moonlitknight/mcp1template'

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