Skip to content

McpToolset connection failure (HTTP 401/403/5xx) causes ASGI crash instead of graceful error #4300

Closed

Description

@simone-viozzi

🔴 Required Information

Describe the Bug:
When an MCP server returns HTTP 401 Unauthorized (e.g., due to an expired/invalid JWT token), the entire ASGI application crashes instead of returning a proper error to the caller. The HTTP error is not propagated, causing the SSE response stream to terminate abruptly.

Steps to Reproduce:

  1. Configure McpToolset with a header_provider that returns a JWT token
  2. Make a request where the JWT is invalid/expired (or MCP server rejects it)
  3. The MCP server returns HTTP 401
  4. Observe app crash with ASGI callable returned without completing response
from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams

def _mcp_header_provider(ctx: ReadonlyContext) -> dict[str, str]:
    jwt = ctx.state.get("jwtToken")
    if not jwt:
        return {}
    return {"Authorization": f"Bearer {jwt}"}

toolset = McpToolset(
    connection_params=StreamableHTTPConnectionParams(
        url="https://example.com/mcp",
    ),
    header_provider=_mcp_header_provider,
    tool_filter=["some_tool"],
)

Expected Behavior:
The 401 error should be caught and propagated as a ConnectionError or similar, allowing the application to handle it gracefully (e.g., return an error message to the user, refresh token, etc.).

Observed Behavior:
The application crashes with:

ERROR - uvicorn.error - ASGI callable returned without completing response.

Environment Details:

  • ADK Library Version: 1.20.0
  • Desktop OS: Linux
  • Python Version: 3.13.8
  • MCP library version: 1.25.0

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: N/A (crash happens before LLM interaction)

🟡 Optional Information

Logs:

2026-01-27 16:18:49,502 DEBUG - mcp.client.streamable_http - Connecting to StreamableHTTP endpoint: https://example.com/mcp
2026-01-27 16:18:49,502 DEBUG - mcp.client.streamable_http - Sending client message: root=JSONRPCRequest(method='initialize', ...)
2026-01-27 16:18:49,583 DEBUG - httpcore.http11 - receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'', [..., (b'WWW-Authenticate', b'Basic realm="Realm"'), ...])
2026-01-27 16:18:49,584 DEBUG - httpcore.http11 - response_closed.complete
2026-01-27 16:18:49,585 ERROR - uvicorn.error - ASGI callable returned without completing response.

Additional Context:
The issue may originate in the MCP Python SDK (streamable_http.py) where HTTP errors like 401 are not sent through the response stream, unlike 404 which is handled explicitly. This might warrant an upstream issue to https://github.com/modelcontextprotocol/python-sdk as well.

How often has this issue occurred?:

  • Always (100%) — whenever MCP server returns a non-2xx/non-404 status code

Activity

  1. added
    mcp[Component] This issues is related to MCP support
    tools[Component] This issue is related to tools
    on Jan 28, 2026
  2. added theissue type on Jan 28, 2026
  3. jfox1-02 commented on Feb 5, 2026

    @jfox1-02

    Seeing this same behavior when a remote mcp server becomes unavailable. Right now it seems like the adk assumes that the mcp server will always be available and will return a 2xx response. Would be nice to be able to handle this from the adk

  4. klateefa commented on Feb 6, 2026

    @klateefa

    @simone-viozzi Thanks for reporting!
    Validate the HTTP response status before entering the SSE loop. If the server returns a non-2xx status, raise an exception immediately so the ASGI response is not left incomplete.

    async with client.stream("POST", url, headers=headers, json=payload) as response:
        if not (200 <= response.status_code < 300):
            body = await response.aread()
            raise ConnectionError(
                f"MCP server returned HTTP {response.status_code}: "
                f"{body.decode(errors='ignore')}"
            )
    
        async for line in response.aiter_lines():
            if line.strip():
                yield line
    

    This prevents stream interruption on 401/403/500 responses and avoids the ASGI crash
    Thank you.

  5. self-assigned this
    on Feb 6, 2026
  6. added
    answered[Status] This issue has been answered by the maintainer
    on Feb 6, 2026
  7. simone-viozzi commented on Feb 6, 2026

    @simone-viozzi
    Author

    Validate the HTTP response status before entering the SSE loop. If the server returns a non-2xx status, raise an exception immediately so the ASGI response is not left incomplete.

    Sorry, where should I validate the request?

    Do I need to override StreamableHTTPConnectionParams?

    I'm using the ADK interface to debug.

  8. challenger71498 commented on Feb 7, 2026

    @challenger71498

    Hi @simone-viozzi,

    could you try update adk to latest version(1.24.1) and reproduce your issue? Recent changes with handling MCP client at #4025 may resolve your issue.

    You may also check the comment here for more details.

  9. surajksharma07 commented on Feb 8, 2026

    @surajksharma07
    Collaborator

    @simone-viozzi This was fixed in v1.23.0 via PR #4025 — SessionContext now wraps the MCP session lifecycle in a dedicated task, so HTTP errors during initialization propagate as ConnectionError instead of crashing the ASGI app.

    Upgrade to v1.24.1 (pip install google-adk==1.24.1) and wrap your get_tools() call in try/except ConnectionError to handle auth failures gracefully.

    The underlying MCP SDK still doesn't validate HTTP status before the SSE loop (streamable_http.py), but the ADK-side error propagation now prevents the crash you're seeing.

  10. surajksharma07 commented on Feb 9, 2026

    @surajksharma07
    Collaborator

    Confirmed fixed in v1.23.0 (PR #4025) — tested on v1.24.1 with mock servers returning 401/403/500, all now raise ConnectionError instead of crashing the ASGI app.

    The SessionContext wrapper at session_context.py:189 catches the MCP SDK error and propagates it through mcp_toolset.py:286-287.

    @challenger71498 also pointed to the same fix.

    Closing this issue for now as completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

answered[Status] This issue has been answered by the maintainermcp[Component] This issues is related to MCP supporttools[Component] This issue is related to tools

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions