Skip to content
Merged
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
53 changes: 47 additions & 6 deletions src/transports/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,15 +627,28 @@ export async function startHttpServer(

await server.connect(transport);
logger.debug("Server connected to transport");
} else if (mcpSessionId) {
// Unknown or expired session. The spec requires 404 here: it is the
// client's cue to re-initialize with a fresh session. Returning 400
// wedges the client until a human reconnects it, which matters now
// that idle sessions are swept (see session-registry.ts).
res.status(404).json({
jsonrpc: "2.0",
error: {
code: -32001,
message: "Session not found. Session may have expired.",
},
id: null,
});
return;
} else {
// Invalid request - no session ID and not an initialize request
// No session ID and not an initialize request
res.status(400).json({
jsonrpc: "2.0",
error: {
code: -32000,
message: mcpSessionId
? "Session not found. Session may have expired."
: "Invalid request. First request must be an initialize request.",
message:
"Invalid request. First request must be an initialize request.",
},
id: null,
});
Expand Down Expand Up @@ -668,7 +681,7 @@ export async function startHttpServer(
const handleMcpGet = async (req: Request, res: Response) => {
const sessionId = req.headers["mcp-session-id"] as string;

if (!sessionId || !transports.has(sessionId)) {
if (!sessionId) {
res.status(400).json({
jsonrpc: "2.0",
error: {
Expand All @@ -680,6 +693,20 @@ export async function startHttpServer(
return;
}

if (!transports.has(sessionId)) {
// 404 tells the client to re-initialize rather than treating this as a
// hard failure - see the POST handler for why this matters.
res.status(404).json({
jsonrpc: "2.0",
error: {
code: -32001,
message: "Session not found. Session may have expired.",
},
id: null,
});
return;
}

const transport = transports.get(sessionId)!;

// An SSE stream can stay open for hours with no other traffic. Count it as
Expand All @@ -703,7 +730,7 @@ export async function startHttpServer(
const handleMcpDelete = async (req: Request, res: Response) => {
const sessionId = req.headers["mcp-session-id"] as string;

if (!sessionId || !transports.has(sessionId)) {
if (!sessionId) {
res.status(400).json({
jsonrpc: "2.0",
error: {
Expand All @@ -715,6 +742,20 @@ export async function startHttpServer(
return;
}

if (!transports.has(sessionId)) {
// 404 tells the client to re-initialize rather than treating this as a
// hard failure - see the POST handler for why this matters.
res.status(404).json({
jsonrpc: "2.0",
error: {
code: -32001,
message: "Session not found. Session may have expired.",
},
id: null,
});
return;
}

const transport = transports.get(sessionId)!;
transports.touch(sessionId);
const apiKey = (req as any).apiKey;
Expand Down
Loading