Last week I submitted my first open source PR to the MCP TypeScript SDK. Today I submitted my second: PR #1707, fixing incorrect HTTP status codes for invalid session IDs.

Finding the Issue

I was looking for another "good first issue" to work on when I found #389. The problem: the SDK's example servers were returning HTTP 400 for invalid session IDs when they should return 404.

Why does this matter? The distinction affects client behavior. Per the MCP spec:

When a client receives HTTP 404 in response to a request containing an Mcp-Session-Id, it MUST start a new session.

A 404 tells the client "this session doesn't exist anymore — start fresh." A 400 tells the client "your request is malformed" — which isn't helpful when the session just expired or the server restarted.

Understanding the Spec

The spec actually defines two different error cases:

  1. Missing session ID (non-initialization request with no Mcp-Session-Id header) → 400 Bad Request
  2. Invalid session ID (header present but session not found) → 404 Not Found

The existing code lumped these together:

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

The Fix

I split the condition to handle each case correctly:

if (!sessionId) {
    // No session ID provided
    res.status(400).send('Session ID required');
    return;
}
if (!transports[sessionId]) {
    // Session ID provided but not found
    res.status(404).send('Session not found');
    return;
}

For the JSON-RPC POST endpoints, the fix was similar but needed to maintain the JSON-RPC error format:

if (sessionId && !transports[sessionId]) {
    // Session ID provided but not found - return 404
    res.status(404).json({
        jsonrpc: '2.0',
        error: {
            code: -32_000,
            message: 'Session not found'
        },
        id: null
    });
    return;
}

Scope

The fix touched six example files:

  • jsonResponseStreamableHttp.ts
  • simpleStreamableHttp.ts
  • standaloneSseWithGetStreamableHttp.ts
  • elicitationFormExample.ts
  • elicitationUrlExample.ts
  • simpleTaskInteractive.ts

Each file had the same pattern in POST, GET, and DELETE handlers. The changes were mechanical once I understood what needed to happen.

What I Learned

Read the spec carefully. The issue comments had some back-and-forth about whether 400 or 404 was correct. Reading the actual spec made the answer clear — they're both correct, for different situations.

Examples matter. These are the files developers copy when building their own MCP servers. Getting the status codes wrong here means those mistakes propagate to every implementation based on these examples.

Small fixes compound. This PR changes 6 files but only 29 lines of actual logic. The impact is potentially every MCP client that connects to servers built from these examples.

Building a Pattern

This is my second PR to the same project in a week. The first added a utility method; this one fixed a spec compliance bug. Together they show I can:

  • Navigate an unfamiliar codebase
  • Understand protocol specifications
  • Make targeted, well-scoped changes
  • Write clear PR descriptions

That's the kind of evidence you can't fake on a resume.


PR: #1707 | Issue: #389

React to this post: