Skip to content

Commit 01b993a

Browse files
committed
feat: add HTTP/SSE transport option with --transportType and --port support
Add support and documentation for running the MCP server with HTTP or SSE transport using --transportType and --port arguments Provide example commands and clarify default port usage for custom network endpoints
1 parent c437106 commit 01b993a

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@jest/globals": "^29.7.0",
3838
"@modelcontextprotocol/inspector": "^0.10.2",
3939
"@redocly/cli": "^1.34.2",
40+
"@types/express": "^5.0.1",
4041
"@types/jest": "^29.5.14",
4142
"@types/node": "^22.14.0",
4243
"@types/simple-oauth2": "^5.0.7",
@@ -65,6 +66,7 @@
6566
"@mongodb-js/devtools-connect": "^3.7.2",
6667
"@mongosh/service-provider-node-driver": "^3.6.0",
6768
"bson": "^6.10.3",
69+
"express": "^5.1.0",
6870
"lru-cache": "^11.1.0",
6971
"mongodb": "^6.15.0",
7072
"mongodb-connection-string-url": "^3.0.2",

src/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface UserConfig {
2323
connectOptions: ConnectOptions;
2424
disabledTools: Array<string>;
2525
readOnly?: boolean;
26+
transportType?: "stdio" | "sse" | "http";
27+
port: string;
2628
}
2729

2830
const defaults: UserConfig = {
@@ -37,6 +39,8 @@ const defaults: UserConfig = {
3739
disabledTools: [],
3840
telemetry: "enabled",
3941
readOnly: false,
42+
transportType: "stdio",
43+
port: "5700",
4044
};
4145

4246
export const config = {

src/index.ts

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import logger, { LogId } from "./logger.js";
44
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5+
import express from "express";
6+
import { randomUUID } from "node:crypto";
7+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
59
import { config } from "./config.js";
610
import { Session } from "./session.js";
711
import { Server } from "./server.js";
@@ -29,9 +33,81 @@ try {
2933
userConfig: config,
3034
});
3135

32-
const transport = createEJsonTransport();
36+
if (config.transportType === "http" || config.transportType === "sse") {
37+
const app = express();
38+
app.use(express.json());
3339

34-
await server.connect(transport);
40+
// Map to store transports by session ID
41+
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
42+
43+
// Handle POST requests for client-to-server communication
44+
app.post("/mcp", async (req, res) => {
45+
// Check for existing session ID
46+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
47+
let transport: StreamableHTTPServerTransport;
48+
49+
if (sessionId && transports[sessionId]) {
50+
// Reuse existing transport
51+
transport = transports[sessionId];
52+
} else if (!sessionId && isInitializeRequest(req.body)) {
53+
// New initialization request
54+
transport = new StreamableHTTPServerTransport({
55+
sessionIdGenerator: () => randomUUID(),
56+
onsessioninitialized: (sessionId: string) => {
57+
// Store the transport by session ID
58+
transports[sessionId] = transport;
59+
},
60+
});
61+
62+
// Clean up transport when closed
63+
transport.onclose = () => {
64+
if (transport.sessionId) {
65+
delete transports[transport.sessionId];
66+
}
67+
};
68+
69+
// Connect to the MCP server
70+
await server.connect(transport);
71+
} else {
72+
// Invalid request
73+
res.status(400).json({
74+
jsonrpc: "2.0",
75+
error: {
76+
code: -32000,
77+
message: "Bad Request: No valid session ID provided",
78+
},
79+
id: null,
80+
});
81+
return;
82+
}
83+
84+
// Handle the request
85+
await transport.handleRequest(req, res, req.body);
86+
});
87+
88+
// Reusable handler for GET and DELETE requests
89+
const handleSessionRequest = async (req: express.Request, res: express.Response) => {
90+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
91+
if (!sessionId || !transports[sessionId]) {
92+
res.status(400).send("Invalid or missing session ID");
93+
return;
94+
}
95+
96+
const transport = transports[sessionId];
97+
await transport.handleRequest(req, res);
98+
};
99+
100+
// Handle GET requests for server-to-client notifications via SSE
101+
app.get("/mcp", handleSessionRequest);
102+
103+
// Handle DELETE requests for session termination
104+
app.delete("/mcp", handleSessionRequest);
105+
const PORT = config.port;
106+
app.listen(PORT);
107+
} else {
108+
const transport = createEJsonTransport();
109+
await server.connect(transport);
110+
}
35111
} catch (error: unknown) {
36112
logger.emergency(LogId.serverStartFailure, "server", `Fatal error running server: ${error as string}`);
37113
process.exit(1);

0 commit comments

Comments
 (0)