Skip to content

fix: use POST ping for StreamableHTTP health checks and unauthenticated liveness probes#4146

Open
ecthelion77 wants to merge 1 commit intoIBM:mainfrom
forterro:fix/streamablehttp-health-check-405-upstream
Open

fix: use POST ping for StreamableHTTP health checks and unauthenticated liveness probes#4146
ecthelion77 wants to merge 1 commit intoIBM:mainfrom
forterro:fix/streamablehttp-health-check-405-upstream

Conversation

@ecthelion77
Copy link
Copy Markdown
Contributor

@ecthelion77 ecthelion77 commented Apr 13, 2026

🐛 Bug-fix PR

🔗 Issue

Closes #4154

📌 Summary

StreamableHTTP servers return 405 Method Not Allowed during health checks because the gateway uses a full MCP SDK client initialize() call which sends HTTP methods the server does not accept for health probing. Additionally, servers configured with authorization_code OAuth grant fail liveness checks because the gateway has no user token to authenticate with.

🔁 Reproduction Steps

  1. Register a remote MCP server using the StreamableHTTP transport
  2. Observe the health check cycle in the gateway logs
  3. Health checks return 405 errors, marking the server as unhealthy
  4. For authorization_code gateways: health checks fail with 401/403 even when the server is up

🐞 Root Cause

  1. The _check_streamablehttp_health() method creates a full streamablehttp_client() session and calls session.initialize(), which sends MCP protocol handshake messages. Many StreamableHTTP servers only accept POST with specific JSON-RPC payloads, returning 405 for the SDK's approach.
  2. For authorization_code grant type gateways, the health check runs as the gateway service account, which has no user-delegated OAuth token. The server correctly rejects the unauthenticated request.

💡 Fix Description

  1. POST-based ping: Replace the full SDK initialize() call with a lightweight POST request sending a JSON-RPC ping method. This is the standard MCP liveness check that StreamableHTTP servers accept. A 200 response (or even a valid JSON-RPC error response) confirms the server is alive.
  2. Unauthenticated probe for authorization_code gateways: When the server's gateway uses authorization_code grant type, skip OAuth token injection and send the ping without credentials. A 401/403 response is treated as "server is alive" (it responded), while connection errors or timeouts indicate the server is truly down.

🧪 Verification

Check Command Status
Lint suite make lint
Unit tests make test
Coverage ≥ 80 % make coverage
Manual regression no longer fails StreamableHTTP servers show healthy status

📐 MCP Compliance (if relevant)

  • Matches current MCP spec (JSON-RPC ping is a standard MCP method)
  • No breaking change to MCP clients

✅ Checklist

  • Code formatted (make black isort pre-commit)
  • No secrets/credentials committed

@ecthelion77
Copy link
Copy Markdown
Contributor Author

Suggested labels: bug, SHOULD, python, mcp-protocol

@ecthelion77 ecthelion77 force-pushed the fix/streamablehttp-health-check-405-upstream branch from 41ed1a6 to 099f3f0 Compare April 14, 2026 12:46
@ecthelion77 ecthelion77 force-pushed the fix/streamablehttp-health-check-405-upstream branch 2 times, most recently from 6c8dba8 to 6751f72 Compare April 14, 2026 15:35
Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
@ecthelion77 ecthelion77 force-pushed the fix/streamablehttp-health-check-405-upstream branch from 6751f72 to 7995bf4 Compare April 14, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][TRANSPORT]: StreamableHTTP health checks return 405 — full SDK initialize() incompatible with health probing

1 participant