Skip to content

[BUG][TRANSPORT]: server_id not injected for internally-forwarded Streamable HTTP requests #3018

@crivetimihai

Description

@crivetimihai

🐞 Bug Summary

When a Streamable HTTP POST request is forwarded between workers via the internal affinity mechanism (x-forwarded-internally=true), the server_id from the URL path (/servers/{id}/mcp) is not injected into the JSON-RPC params before posting to /rpc. This causes /rpc routing to fall into the unscoped list_tools path instead of the server-scoped list_server_tools path.

The local-owner branch correctly injects server_id (added in PR #2974), but the internally-forwarded branch was not updated.


🧩 Affected Component

  • Federation or Transports
  • mcpgateway - API

🔁 Steps to Reproduce

  1. Deploy with multi-worker session affinity enabled (MCPGATEWAY_SESSION_AFFINITY_ENABLED=true)
  2. Establish a stateful MCP session on /servers/{server_id}/mcp/
  3. Trigger a cross-worker forward (request arrives at worker B, but session is owned by worker A)
  4. The forwarded request posts to /rpc without params.server_id
  5. /rpc routes to unscoped handler → returns tools from all servers

🤔 Expected Behavior

Internally-forwarded requests to /servers/{id}/mcp should inject server_id into params before posting to /rpc, identical to the local-owner branch.


📍 Code References

  • Local-owner branch (correctly injects): mcpgateway/transports/streamablehttp_transport.py:2003-2010
  • Internally-forwarded branch (missing injection): mcpgateway/transports/streamablehttp_transport.py:1850-1873
  • /rpc expects params.server_id: mcpgateway/main.py:5670
  • Scoped routing: mcpgateway/main.py:5756 vs unscoped: mcpgateway/main.py:5771

💡 Suggested Fix

Add the same server_id injection block from the local-owner branch into the internally-forwarded branch (between line 1852 and 1861), using the match variable already available in scope (line 1776):

# Inject server_id from URL path into params for /rpc routing
if match:
    server_id = match.group("server_id")
    if "params" not in json_body:
        json_body["params"] = {}
    json_body["params"]["server_id"] = server_id
    body = orjson.dumps(json_body)

Key Value
Version or commit main (pre-existing, noted during PR #2974 review)
Runtime Python 3.13, Gunicorn multi-worker
Severity High (incorrect tool scoping in multi-worker deployments)

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafebugSomething isn't workingmcp-protocolAlignment with MCP protocol or specificationpythonPython / backend development (FastAPI)

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions