Created
August 5, 2025 14:19
-
-
Save cjavdev/5505b24f6998b17d06df6a2c01c2f563 to your computer and use it in GitHub Desktop.
Minimal MCP server.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import express, { Request, Response } from "express"; | |
import { randomUUID } from "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"; | |
const app = express(); | |
app.use(express.json()); | |
// Session management for stateful connections | |
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {}; | |
function createMcpServer(): McpServer { | |
const server = new McpServer( | |
{ | |
name: "math-mcp-server", | |
version: "1.0.0" | |
} | |
); | |
server.registerTool("add", | |
{ | |
title: "Addition Tool", | |
description: "Add two numbers", | |
inputSchema: { a: z.number(), b: z.number() } | |
}, | |
async ({ a, b }) => ({ | |
content: [{ type: "text", text: String(a + b) }] | |
}) | |
); | |
server.registerTool("multiply", | |
{ | |
title: "Multiplication Tool", | |
description: "Multiply two numbers", | |
inputSchema: { a: z.number(), b: z.number() } | |
}, | |
async ({ a, b }) => ({ | |
content: [{ type: "text", text: String(a * b) }] | |
}) | |
); | |
return server; | |
} | |
// Initialze and start session connect requests | |
app.post('/mcp', async (req: Request, res: Response) => { | |
const sessionId = req.headers['mcp-session-id'] as string | undefined; | |
let transport: StreamableHTTPServerTransport; | |
try { | |
if (sessionId && transports[sessionId]) { | |
// Reuse existing transport | |
transport = transports[sessionId]; | |
} else if (!sessionId && isInitializeRequest(req.body)) { | |
// New initialization request | |
transport = new StreamableHTTPServerTransport({ | |
sessionIdGenerator: () => randomUUID(), | |
onsessioninitialized: (sessionId) => { | |
transports[sessionId] = transport; | |
}, | |
enableDnsRebindingProtection: process.env.NODE_ENV === 'production', | |
allowedHosts: ['localhost:3000'] // env.ALLOWED_DOMAINS | |
}); | |
// Clean up transport when closed | |
transport.onclose = () => { | |
if (transport.sessionId) { | |
delete transports[transport.sessionId]; | |
} | |
}; | |
const server = createMcpServer(); | |
await server.connect(transport); | |
} else { | |
res.status(400).json({ | |
jsonrpc: '2.0', | |
error: { | |
code: -32000, | |
message: 'Bad Request: No valid session ID provided', | |
}, | |
id: null, | |
}); | |
return; | |
} | |
await transport.handleRequest(req, res, req.body); | |
} catch (error) { | |
if (!res.headersSent) { | |
res.status(500).json({ | |
jsonrpc: '2.0', | |
error: { | |
code: -32603, | |
message: 'Internal server error', | |
}, | |
id: null, | |
}); | |
} | |
} | |
}); | |
const PORT = 3000; | |
app.listen(PORT, () => { | |
console.log(`🔗 Streamable HTTP MCP endpoint: http://localhost:${PORT}/mcp`); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment