Skip to content

Python: [Bug]: MCP HTTP auth failures (401) surface as "Cancelled via cancel scope ..." instead of the actual error #7699

Description

Description

What happened?

When an MCP server rejects a request with HTTP 401, MCPStreamableHTTPTool fails with a misleading ToolException: MCP server failed to initialize: Cancelled via cancel scope ... instead of reporting the real 401. The underlying httpx.HTTPStatusError(401) is swallowed, making auth failures very hard to diagnose.

What did you expect to happen?

The 401 should surface as a proper error (e.g. a ToolException naming the HTTP status / auth failure) so a client can react to it.

Steps to reproduce

  1. Run a protected MCP server (streamable-http) whose JWT verifier rejects the provided token (e.g. wrong audience/scope -> 401).
  2. Connect via MCPStreamableHTTPTool with a header_provider sending a Bearer token (see code sample).
  3. Observe the ToolException with the cancellation message instead of a 401.

Root cause

In the mcp SDK's streamable HTTP transport, response.raise_for_status() (streamable_http.py) raises httpx.HTTPStatusError(401) inside a task spawned via tg.start_soon(...). The exception is never routed to the request's response stream, so the task group cancels all siblings - including session.initialize() - which then raises CancelledError. In agent-framework's _connect_on_owner (_mcp.py), _should_propagate_cancelled_error returns False (the cancellation comes from the group's cancel scope, not a caller-driven task.cancel()), so the CancelledError is wrapped into the ToolException above. The real 401 lives only in the cleanup ExceptionGroup, which agent-framework catches in _safe_close_exit_stack and only logs as a warning.

Suggested fix

Related issues

#5667 (fixed the raw CancelledError leak via #5687, but the underlying HTTP error is still lost), #4759 (same symptom via AKS Ingress), #1564 (same "root cause hidden" principle for stdio).

Code Sample

async with MCPStreamableHTTPTool(
    name="auth-mcp",
    url="http://127.0.0.1:8000/mcp",
    header_provider=lambda _kwargs: {"Authorization": f"Bearer {token}"},
) as mcp_tool:
    ...


Client-side workaround currently in use:


except ToolException as exc:
    if "Cancelled via cancel scope" in str(exc):
        probe = await client.post(MCP_SERVER_URL, headers={"Authorization": f"Bearer {token}"})
        if probe.status_code == 401:
            raise PermissionError("MCP auth failed: HTTP 401") from exc
    raise

Error Messages / Stack Traces

ToolException: MCP server failed to initialize: Cancelled via cancel scope ...


preceded by the warning:


Could not cleanly close MCP exit stack due to cleanup error group. Error: unhandled errors in a TaskGroup (1 sub-exception)


Environment: agent-framework 1.14.0 (also reproduced on earlier versions), mcp 1.29.0 (pinned constraint in agent-framework is >=1.27.2), Python 3.14, Windows.

Package Versions

agent-framework: 1.14.0 (also reproduced on earlier versions), mcp: 1.29.0

Python Version

Python 3.14

Additional Context

No response

Activity

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

Metadata

Metadata

Labels

mcpUsage: [Issues, PRs], Target: MCPpythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions