Skip to content

Tools tab times out ("Request timed out") against a connected, OAuth-authenticated Streamable HTTP server running FastMCP's stateless_http=True mode #2187

Description

@hongwei1

Which version line?

v2 — current (@modelcontextprotocol/inspector@latest)

Which client?

Web

Inspector version

2.4.0 (reproduced both via npx @modelcontextprotocol/inspector and a global npm install -g @modelcontextprotocol/inspector install — same binary version, same result)

Node version

v26.5.0

Operating system (and browser, for the web client)

macOS 26.5.2, Chrome

Transport

Streamable HTTP

MCP server under inspection

Server: https://k8s-mcp2.openbankproject.com/mcp — a public, OAuth 2.1-protected FastMCP (Python) server (the Open Bank Project's MCP server, fronting their sandbox banking API).
Protocol era: modern (negotiated 2025-11-25; also reproduces with 2025-06-18)
OAuth: yes — Dynamic Client Registration + authorization code + PKCE (S256) against a separate OIDC provider (k8s-oidc.openbankproject.com). DCR, the authorization redirect, and the token exchange all succeed; the Inspector UI shows Connected after login, and initialize / notifications/initialized complete.

This server is public and self-service, so this should be directly reproducible without needing any credentials from me: anyone can create a free test account at https://k8s-portal.openbankproject.com/register (name/email/username/password, no approval step) and log in with it when the Inspector's OAuth flow redirects to the sign-in page.

The server is started with mcp.run(..., stateless_http=True) (FastMCP's stateless HTTP mode) and, by design, never returns an Mcp-Session-Id header on initialize or on any subsequent request.

Steps to reproduce

  1. Fresh launch: npx @modelcontextprotocol/inspector (also reproduced with the global install).
  2. Add the server above as a Streamable HTTP server and connect.
  3. Complete the OAuth login when prompted — the Servers tab shows the server as Connected, with the negotiated protocol version shown next to it.
  4. Open the Tools tab.
  5. Wait — no tool list ever loads.

Expected behavior

tools/list should return promptly, the same as it does for every other client tested against this server (see below).

Actual behavior

The Tools tab shows:

Couldn't load tools
Request timed out
[Retry]

Clicking Retry repeats the same outcome. This is fully and consistently reproducible — same result across multiple fresh Inspector instances (npx-launched and globally-installed), across two separate browser profiles, and across two different OAuth-authenticated user identities on the target server.

Independently, calling the exact same server with the exact same OAuth access token via a plain curl tools/list request (both mcp-protocol-version: 2025-06-18 and 2025-11-25) returns the correct 6-tool list in well under 100ms every time. mcp-remote-based clients (e.g. Claude Desktop / Claude Code style connections) also connect to and use this server successfully. So the server itself, the OAuth flow, and the protocol version are not the cause — the failure is isolated to something in the Inspector web client's own Streamable HTTP session handling once it needs to send tools/list.

The one server characteristic that stands out: Mcp-Session-Id is never present in any response (initialize included), because the server intentionally runs in FastMCP's stateless_http=True mode. A bare GET to the MCP endpoint (the notification stream) does return 200 with content-type: text/event-stream and then stays open indefinitely without ever emitting an event — which is correct behavior for a notification channel, but I could not fully trace whether the Inspector's StreamableHTTPClientTransport (from @modelcontextprotocol/sdk) has an implicit dependency on session-id continuity between that channel and the tools/list POST that causes the request to be misrouted or never actually sent once no session id is available. I did not find an explicit blocking await on the notification stream in send()/start() while reading the SDK source, so I can't pin the exact line — flagging the stateless-server angle as the strongest lead rather than a confirmed root cause.

Possibly related: #2028 also describes an Inspector-side timeout against a FastMCP server (there over stdio/legacy, triggered by ctx.report_progress/ctx.info), which suggests FastMCP-server interop may be a recurring soft spot for the Inspector's request/response tracking, even though the trigger and transport differ from this report.

Logs, errors, or screenshots

Inspector's own Network panel entry for the hung request shows:

Request Headers
  accept: text/event-stream
  authorization: [REDACTED]
  mcp-protocol-version: 2025-11-25
Response Headers
  cache-control: no-cache, no-transform
  content-type: text/event-stream
  date: ...
  strict-transport-security: max-age=31536000; includeSubDomains
Response Body
  Long-lived stream — body not captured

Direct curl against the same server, same token, for comparison (succeeds instantly both times):

$ curl -s -X POST https://k8s-mcp2.openbankproject.com/mcp \
    -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    -H "Accept: application/json, text/event-stream" \
    -H "mcp-protocol-version: 2025-11-25" \
    -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

event: message
data: {"jsonrpc":"2.0","id":2,"result":{"tools":[...6 tools...]}}

initialize response headers for reference (no Mcp-Session-Id):

HTTP/2 200
date: ...
content-type: text/event-stream
cache-control: no-cache, no-transform
strict-transport-security: max-age=31536000; includeSubDomains

Happy to provide a redacted HAR export if that would help, on top of the live server above.

Already prototyped a fix?

No — I don't have a fix, just the isolation above (server confirmed healthy via curl/other clients; failure confirmed specific to the Inspector web client once a tools/list is attempted against a session-id-less server).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv2Issues and PRs for v2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions