Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .changeset/fix-session-status-codes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modelcontextprotocol/examples-server': patch
---

Example servers now return HTTP 404 (not 400) when a request includes an unknown session ID, so clients can correctly detect they need to start a new session. Requests missing a session ID entirely still return 400.
29 changes: 20 additions & 9 deletions examples/server/src/elicitationFormExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,17 @@ async function main() {

await transport.handleRequest(req, res, req.body);
return;
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
// Invalid request - no session ID or not initialization request
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32_000,
message: 'Bad Request: No valid session ID provided'
},
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand Down Expand Up @@ -399,8 +402,12 @@ async function main() {
// Handle GET requests for SSE streams
const mcpGetHandler = async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand All @@ -414,8 +421,12 @@ async function main() {
// Handle DELETE requests for session termination
const mcpDeleteHandler = async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down
29 changes: 20 additions & 9 deletions examples/server/src/elicitationUrlExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,14 +606,17 @@ const mcpPostHandler = async (req: Request, res: Response) => {

await transport.handleRequest(req, res, req.body);
return; // Already handled
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
// Invalid request - no session ID or not initialization request
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32_000,
message: 'Bad Request: No valid session ID provided'
},
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand Down Expand Up @@ -643,8 +646,12 @@ app.post('/mcp', authMiddleware, mcpPostHandler);
// Handle GET requests for SSE streams (using built-in support from StreamableHTTP)
const mcpGetHandler = async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down Expand Up @@ -682,8 +689,12 @@ app.get('/mcp', authMiddleware, mcpGetHandler);
// Handle DELETE requests for session termination (according to MCP spec)
const mcpDeleteHandler = async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down
13 changes: 8 additions & 5 deletions examples/server/src/jsonResponseStreamableHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,17 @@ app.post('/mcp', async (req: Request, res: Response) => {
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
return; // Already handled
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
// Invalid request - no session ID or not initialization request
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32_000,
message: 'Bad Request: No valid session ID provided'
},
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand Down
29 changes: 20 additions & 9 deletions examples/server/src/simpleStreamableHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,14 +689,17 @@ const mcpPostHandler = async (req: Request, res: Response) => {

await transport.handleRequest(req, res, req.body);
return; // Already handled
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
// Invalid request - no session ID or not initialization request
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32_000,
message: 'Bad Request: No valid session ID provided'
},
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand Down Expand Up @@ -730,8 +733,12 @@ if (useOAuth && authMiddleware) {
// Handle GET requests for SSE streams (using built-in support from StreamableHTTP)
const mcpGetHandler = async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down Expand Up @@ -761,8 +768,12 @@ if (useOAuth && authMiddleware) {
// Handle DELETE requests for session termination (according to MCP spec)
const mcpDeleteHandler = async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down
25 changes: 20 additions & 5 deletions examples/server/src/simpleTaskInteractive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,17 @@ app.post('/mcp', async (req: Request, res: Response) => {
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
return;
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
res.status(400).json({
jsonrpc: '2.0',
error: { code: -32_000, message: 'Bad Request: No valid session ID' },
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand All @@ -695,8 +702,12 @@ app.post('/mcp', async (req: Request, res: Response) => {
// Handle GET requests for SSE streams
app.get('/mcp', async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand All @@ -707,8 +718,12 @@ app.get('/mcp', async (req: Request, res: Response) => {
// Handle DELETE requests for session termination
app.delete('/mcp', async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down
21 changes: 14 additions & 7 deletions examples/server/src/standaloneSseWithGetStreamableHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,17 @@ app.post('/mcp', async (req: Request, res: Response) => {
// Handle the request - the onsessioninitialized callback will store the transport
await transport.handleRequest(req, res, req.body);
return; // Already handled
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
// Invalid request - no session ID or not initialization request
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32_000,
message: 'Bad Request: No valid session ID provided'
},
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand All @@ -119,8 +122,12 @@ app.post('/mcp', async (req: Request, res: Response) => {
// Handle GET requests for SSE streams (now using built-in support from StreamableHTTP)
app.get('/mcp', async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down
28 changes: 20 additions & 8 deletions test/conformance/src/authTestServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,13 +327,17 @@ async function startServer() {
await mcpServer.connect(transport);
await transport.handleRequest(req, res, req.body);
return;
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32_000,
message: 'Invalid or missing session ID'
},
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand All @@ -359,8 +363,12 @@ async function startServer() {
app.get('/mcp', bearerAuth, async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;

if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand All @@ -381,8 +389,12 @@ async function startServer() {
app.delete('/mcp', bearerAuth, async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;

if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down
28 changes: 20 additions & 8 deletions test/conformance/src/everythingServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -929,13 +929,17 @@ app.post('/mcp', async (req: Request, res: Response) => {
await mcpServer.connect(transport);
await transport.handleRequest(req, res, req.body);
return;
} else if (sessionId) {
res.status(404).json({
jsonrpc: '2.0',
error: { code: -32_001, message: 'Session not found' },
id: null
});
return;
} else {
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32_000,
message: 'Invalid or missing session ID'
},
error: { code: -32_000, message: 'Bad Request: Session ID required' },
id: null
});
return;
Expand All @@ -961,8 +965,12 @@ app.post('/mcp', async (req: Request, res: Response) => {
app.get('/mcp', async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;

if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand All @@ -988,8 +996,12 @@ app.get('/mcp', async (req: Request, res: Response) => {
app.delete('/mcp', async (req: Request, res: Response) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;

if (!sessionId || !transports[sessionId]) {
res.status(400).send('Invalid or missing session ID');
if (!sessionId) {
res.status(400).send('Missing session ID');
return;
}
if (!transports[sessionId]) {
res.status(404).send('Session not found');
return;
}

Expand Down
Loading