Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion tests/mcp/test_server_errors.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import sys
import pytest
import httpx
from unittest.mock import AsyncMock, MagicMock

# Handle Python version compatibility for ExceptionGroups
if sys.version_info < (3, 11):
from exceptiongroup import BaseExceptionGroup
else:
BaseExceptionGroup = ExceptionGroup

from agents import Agent
from agents.exceptions import UserError
from agents.mcp.server import _MCPServerWithClientSession
from agents.mcp.server import _MCPServerWithClientSession, MCPServerStreamableHttp
from agents.run_context import RunContextWrapper


Expand Down Expand Up @@ -45,3 +54,32 @@ async def test_not_calling_connect_causes_error():

with pytest.raises(UserError):
await server.call_tool("foo", {})


@pytest.mark.asyncio
async def test_call_tool_nested_exception_group_mapping():
"""
Regression test ensuring that nested ExceptionGroups containing HTTP errors
are recursively extracted and mapped to a UserError in call_tool().
"""
# 1. Initialize the server with mock streamable parameters
server = MCPServerStreamableHttp(params={"url": "http://fake-mcp-server"})

# 2. Simulate an active connection by mocking the session object
server.session = MagicMock()

# 3. Construct a nested ExceptionGroup hierarchy containing a connection error
http_error = httpx.ConnectError("Network unreachable")
inner_group = BaseExceptionGroup("inner_failures", [http_error])
outer_group = BaseExceptionGroup("outer_failures", [inner_group])
Comment on lines +73 to +74

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Implement recursive extraction before adding nested group test

With this nested ExceptionGroup, call_tool() still re-raises the group instead of producing UserError because src/agents/mcp/server.py::_extract_http_error_from_exception only checks direct children of e.exceptions and does not recurse into inner_group. As written, this new regression test fails in CI until the production extraction logic is updated to handle nested groups.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @codex, but this failure is entirely intentional. As requested by the maintainer, this is a regression test designed to fail on the base revision. The implementation that fixes this (the recursive extraction) is waiting in PR #3556.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# 4. Mock the internal retry handler to raise the nested exception group
server._call_tool_with_isolated_retry = AsyncMock(side_effect=outer_group)

# 5. Assert that call_tool() catches the nested group, extracts the error, and raises UserError
with pytest.raises(UserError) as exc_info:
await server.call_tool(tool_name="test_tool", arguments={})

# 6. Verify that the user-facing message is mapped correctly based on the root cause
assert "Connection lost" in str(exc_info.value)
assert exc_info.value.__cause__ is http_error