Skip to content

sdk/server/streamableHttp.js cannot support stateless mode #340

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

Closed
didiercolens opened this issue Apr 15, 2025 · 2 comments
Closed

sdk/server/streamableHttp.js cannot support stateless mode #340

didiercolens opened this issue Apr 15, 2025 · 2 comments
Labels
bug Something isn't working

Comments

@didiercolens
Copy link

didiercolens commented Apr 15, 2025

Describe the bug
https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/server/streamableHttp.ts#L373-L389

  private validateSession(req: IncomingMessage, res: ServerResponse): boolean {
    if (!this._initialized) {
      // If the server has not been initialized yet, reject all requests
      res.writeHead(400).end(JSON.stringify({
        jsonrpc: "2.0",
        error: {
          code: -32000,
          message: "Bad Request: Server not initialized"
        },
        id: null
      }));
      return false;
    }
    if (this.sessionId === undefined) {
      // If the session ID is not set, the session management is disabled
      // and we don't need to validate the session ID
      return true;
    }

can never succeed in stateless mode because the server cannot re-use an existing transport when there is no session-id. Therefore this._initialized is always false and validateSession always fails.
Note that this.sessionId is also not set in such a case.

In a scenario where the MCP server is a Kubernetes deployment, it is also possible a request reaches another pod which does not have the transport in its cache.

To Reproduce
Create a streamableHttp server with sessionId disabled for example:

app.post('/mcp', async (req: Request, res: Response) => {
  console.log('Received MCP request:', req.body);
  try {
    let transport: StreamableHTTPServerTransport;

    if (isInitializeRequest(req.body)) {
      // New initialization request - use JSON response mode
      transport = new StreamableHTTPServerTransport({
        sessionIdGenerator: () => undefined,
        enableJsonResponse: true,
      });

    } else {
        transport = new StreamableHTTPServerTransport({
            sessionIdGenerator: () => undefined
        });        
    }
    // Handle the request with the transport
    await server.connect(transport);
    await transport.handleRequest(req, res, req.body);
  } catch (error) {
    console.error('Error handling MCP request:', error);
    if (!res.headersSent) {
      res.status(500).json({
        jsonrpc: '2.0',
        error: {
          code: -32603,
          message: 'Internal server error',
        },
        id: null,
      });
    }
  }
});

and try to connect with a client

Expected behavior
client should be able to send the intialized message after initialisation even if there is no session-id to track sessions. i.E. stateless mode.

Logs

    Error: Error POSTing to endpoint (HTTP 400): {"jsonrpc":"2.0","error":{"code":-32000,"message":"Bad Request: Server not initialized"},"id":null}
        at StreamableHTTPClientTransport.send (file:///xxx/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js:172:23)

One solution is likely to move the if (this.sessionId === undefined) { block to the top of the function

@didiercolens didiercolens added the bug Something isn't working label Apr 15, 2025
@didiercolens
Copy link
Author

#335 seems to address this issue

@ihrpr
Copy link
Contributor

ihrpr commented Apr 18, 2025

@didiercolens it should be resolved now with 1.10.1 release. Please re-open id it's still an issue

@ihrpr ihrpr closed this as completed Apr 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants