Skip to content

When throwing the HTTP status exception, MCP client is disregarding the exception message! #1212

@aspk74

Description

@aspk74

Initial Checks

Description

Issue: MCP client is throwing the HTTP status exception disregarding the raw response
Background: I'm trying to test an edge case for the MCP client. I'm purposefully sending bad requests so that I can see how my end-customers will see the output in case of error. Like 400, 406 errors etc. Now, when it is a bad request, from the server side, I'm not only sending the HTTP error code like 400 or 406, I'm also sending the error description - like for 406 - "message":"MCP Accept header must contain: application/json, text/event-stream", for 400 - "message":"Invalid parameter xyz in request" or something like that.

When I send a bare HTTP request (without using the mcp client) like this -

...

async with httpx.AsyncClient(timeout=60) as client:
    try:
        response = await client.post(
            mcp_url, 
            headers=headers, 
            json=init_request.model_dump(by_alias=True, exclude_none=True)
        )
        print(f"Status: {response.status_code}")
        print(f"Raw response: {response.text}")
    except Exception as e:
        print(f"Request failed: {e}")
        traceback.print_exc()

This is the response I get -

python3 my_mcp_client_http_call.py
Status: 406
Raw response: {"jsonrpc":"2.0","error":{"code":-32006,"message":"MCP Accept header must contain: application/json, text/event-stream"},"id":"cbde555c-024d-46eb-b701-3be8de1fa5bb"}
(mcp-py311) ➜  mcp-agent 

Notice how the error message contains details so the caller knows what Accept header to call. This is the message I've added from my server side.

However, when I use the mcp client for this purpose -

    async with streamablehttp_client(mcp_url, headers, timeout=20, terminate_on_close=False) as (
        read_stream,
        write_stream,
        _,
    ):
        async with ClientSession(read_stream, write_stream) as session:
            init_result = await session.initialize()
            result = await session.call_tool(
                <tool-name>
                <tool-params>
            )
            print(f"Tool response: {result}")

I get this in my client -

  + Exception Group Traceback (most recent call last):
  |   File "/Volumes/workplace/synced/mcp-agent/my_mcp_client_remote.py", line 47, in <module>
  |     asyncio.run(main())
  |   File "/usr/local/Cellar/python@3.11/3.11.13/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 190, in run
  |     return runner.run(main)
  |            ^^^^^^^^^^^^^^^^
  |   File "/usr/local/Cellar/python@3.11/3.11.13/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 118, in run
  |     return self._loop.run_until_complete(task)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/usr/local/Cellar/python@3.11/3.11.13/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py", line 654, in run_until_complete
  |     return future.result()
  |            ^^^^^^^^^^^^^^^
  |   File "/Volumes/workplace/synced/mcp-agent/my_mcp_client_remote.py", line 27, in main
  |     async with streamablehttp_client(mcp_url, headers, timeout=20, terminate_on_close=False) as (
  |   File "/usr/local/Cellar/python@3.11/3.11.13/Frameworks/Python.framework/Versions/3.11/lib/python3.11/contextlib.py", line 231, in __aexit__
  |     await self.gen.athrow(typ, value, traceback)
  |   File "/Volumes/workplace/synced/mcp-agent/mcp-py311/lib/python3.11/site-packages/mcp/client/streamable_http.py", line 474, in streamablehttp_client
  |     async with anyio.create_task_group() as tg:
  |   File "/Volumes/workplace/synced/mcp-agent/mcp-py311/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
  |     raise BaseExceptionGroup(
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/Volumes/workplace/synced/mcp-agent/mcp-py311/lib/python3.11/site-packages/mcp/client/streamable_http.py", line 405, in handle_request_async
    |     await self._handle_post_request(ctx)
    |   File "/Volumes/workplace/synced/mcp-agent/mcp-py311/lib/python3.11/site-packages/mcp/client/streamable_http.py", line 277, in _handle_post_request
    |     response.raise_for_status()
    |   File "/Volumes/workplace/synced/mcp-agent/mcp-py311/lib/python3.11/site-packages/httpx/_models.py", line 829, in raise_for_status
    |     raise HTTPStatusError(message, request=request, response=self)
    | httpx.HTTPStatusError: Client error '406 Not Acceptable' for url 'https://rpjoshi-dud.dev.genesis-primitives.aws.dev/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-west-2%3A766807730965%3Aruntime%2FagentMcpTestLocal5-E7E7ISEnws/invocations?qualifier=DEFAULT'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406
    +------------------------------------

Notice it only talks about Client error '406 Not Acceptable' but not gives the error message description. Also, would prefer to get a nicer response than an unhandled errors in a TaskGroup message.

Relevant code link - streamable_http.py

<< I believe in that code, it should not completely disregard the error message, rather somehow send that error message (if it exists) back to the user for clarity.

Let me know what you think.

Example Code

Calling the server using client (In headers, purposefully pass in bad headers to test 406)- 


    async with streamablehttp_client(mcp_url, headers, timeout=20, terminate_on_close=False) as (
        read_stream,
        write_stream,
        _,
    ):
        async with ClientSession(read_stream, write_stream) as session:
            init_result = await session.initialize()
            result = await session.call_tool(
                <tool-name>
                <tool-params>
            )
            print(f"Tool response: {result}")




Calling the server via http call - 


    init_request = JSONRPCRequest(
        jsonrpc="2.0",
        id="1",
        method="initialize",
        params={"protocolVersion": "2025-06-18", "capabilities": {"sampling":{},"roots":{"listChanged":True}}},
        clientInfo={"name":"mcp-inspector","version":"0.16.1"}
    )
    
    async with httpx.AsyncClient(timeout=60) as client:
        try:
            response = await client.post(
                mcp_url, 
                headers=headers, 
                json=init_request.model_dump(by_alias=True, exclude_none=True)
            )
            print(f"Status: {response.status_code}")
            print(f"Raw response: {response.text}")
        except Exception as e:
            print(f"Request failed: {e}")
            traceback.print_exc()

Python & MCP Python SDK

python - 3.11
mcp - 1.12.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions