Skip to content

feat: add HTTP/SSE transport support and document usage in Quick Start #257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,28 @@ You can use environment variables in the config file or set them and run the ser
- Connection String via environment variables in the MCP file [example](#connection-string-with-environment-variables)
- Atlas API credentials via environment variables in the MCP file [example](#atlas-api-credentials-with-environment-variables)

#### Option 5: HTTP/SSE Transport (Custom Port)

You can run the MCP server using HTTP or SSE transport by specifying the `--transportType` and (optionally) `--port` arguments. The default port is `5700` if not specified.

**Example (HTTP):**

```shell
npx -y mongodb-mcp-server --transportType=http --port=3000 --connectionString="mongodb://localhost:27017"
```

**Example (SSE):**

```shell
npx -y mongodb-mcp-server --transportType=sse --port=3000 --connectionString="mongodb://localhost:27017"
```

- `--transportType` can be `http` or `sse`
- `--port` sets the port (default: 5700)
- All other configuration options (like `--connectionString`, `--apiClientId`, etc.) are supported

This allows you to expose the MCP server over HTTP/SSE for integration with clients that require a network endpoint.

## 🛠️ Supported Tools

### Tool List
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@jest/globals": "^29.7.0",
"@modelcontextprotocol/inspector": "^0.10.2",
"@redocly/cli": "^1.34.2",
"@types/express": "^5.0.1",
"@types/jest": "^29.5.14",
"@types/node": "^22.14.0",
"@types/simple-oauth2": "^5.0.7",
Expand Down Expand Up @@ -65,6 +66,7 @@
"@mongodb-js/devtools-connect": "^3.7.2",
"@mongosh/service-provider-node-driver": "^3.6.0",
"bson": "^6.10.3",
"express": "^5.1.0",
"lru-cache": "^11.1.0",
"mongodb": "^6.15.0",
"mongodb-connection-string-url": "^3.0.2",
Expand Down
4 changes: 4 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export interface UserConfig {
connectOptions: ConnectOptions;
disabledTools: Array<string>;
readOnly?: boolean;
transportType?: "stdio" | "sse" | "http";
port: string;
}

const defaults: UserConfig = {
Expand All @@ -37,6 +39,8 @@ const defaults: UserConfig = {
disabledTools: [],
telemetry: "enabled",
readOnly: false,
transportType: "stdio",
port: "5700",
};

export const config = {
Expand Down
80 changes: 78 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import logger, { LogId } from "./logger.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import express from "express";
import { randomUUID } from "node:crypto";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { config } from "./config.js";
import { Session } from "./session.js";
import { Server } from "./server.js";
Expand Down Expand Up @@ -29,9 +33,81 @@ try {
userConfig: config,
});

const transport = createEJsonTransport();
if (config.transportType === "http" || config.transportType === "sse") {
const app = express();
app.use(express.json());

await server.connect(transport);
// Map to store transports by session ID
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

// Handle POST requests for client-to-server communication
app.post("/mcp", async (req, res) => {
// Check for existing session ID
const sessionId = req.headers["mcp-session-id"] as string | undefined;
let transport: StreamableHTTPServerTransport;

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: string) => {
// Store the transport by session ID
transports[sessionId] = transport;
},
});

// Clean up transport when closed
transport.onclose = () => {
if (transport.sessionId) {
delete transports[transport.sessionId];
}
};

// Connect to the MCP server
await server.connect(transport);
} else {
// Invalid request
res.status(400).json({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Bad Request: No valid session ID provided",
},
id: null,
});
return;
}

// Handle the request
await transport.handleRequest(req, res, req.body);
});

// Reusable handler for GET and DELETE requests
const handleSessionRequest = async (req: express.Request, res: express.Response) => {
const sessionId = req.headers["mcp-session-id"] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send("Invalid or missing session ID");
return;
}

const transport = transports[sessionId];
await transport.handleRequest(req, res);
};

// Handle GET requests for server-to-client notifications via SSE
app.get("/mcp", handleSessionRequest);

// Handle DELETE requests for session termination
app.delete("/mcp", handleSessionRequest);
const PORT = config.port;
app.listen(PORT);
} else {
const transport = createEJsonTransport();
await server.connect(transport);
}
} catch (error: unknown) {
logger.emergency(LogId.serverStartFailure, "server", `Fatal error running server: ${error as string}`);
process.exit(1);
Expand Down
Loading