-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Open
Labels
Description
Describe the bug
If you try to connect to mcp server that is not currently available, you will ruin subsequent await calls (and can't gracefully shutdown ever). Also the actual information about what happened (can't connect) is buried deep in exceptions chain.
Debug information
openai-agents==0.2.3
Python 3.13.5
Repro steps
import asyncio
from agents.mcp import MCPServerStreamableHttpParams, MCPServerStreamableHttp
async def do_other_stuff():
await asyncio.sleep(2)
async def main():
params = MCPServerStreamableHttpParams(url='http://localhost:3002') # I have no mcp server at this address.
server = MCPServerStreamableHttp(params)
try:
async with server:
pass
except BaseException as e:
print(e)
# await server.cleanup() # workaround, but root cause will be lost
print('lets do other stuff')
await do_other_stuff() # <- will cause "Cancelled by cancel scope ..."
print('done')
if __name__ == "__main__":
asyncio.run(main())
Expected behavior
httpcore.ConnectError: All connection attempts failed
lets do other stuff
done
Actual behavior
Cancelled by cancel scope 181bc7de3c0
lets do other stuff
an error occurred during closing of asynchronous generator <async_generator object streamablehttp_client at 0x00000181BC6079C0>
asyncgen: <async_generator object streamablehttp_client at 0x00000181BC6079C0>
+ Exception Group Traceback (most recent call last):
| File "[REDACTED]\venvs\dsa\Lib\site-packages\anyio\_backends\_asyncio.py", line 772, in __aexit__
| raise BaseExceptionGroup(
| "unhandled errors in a TaskGroup", self._exceptions
| ) from None
| BaseExceptionGroup: unhandled errors in a TaskGroup (2 sub-exceptions)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 101, in map_httpcore_exceptions
| yield
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 394, in handle_async_request
| resp = await self._pool.handle_async_request(req)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection_pool.py", line 256, in handle_async_request
| raise exc from None
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection_pool.py", line 236, in handle_async_request
| response = await connection.handle_async_request(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| pool_request.request
| ^^^^^^^^^^^^^^^^^^^^
| )
| ^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection.py", line 101, in handle_async_request
| raise exc
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection.py", line 78, in handle_async_request
| stream = await self._connect(request)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection.py", line 124, in _connect
| stream = await self._network_backend.connect_tcp(**kwargs)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_backends\auto.py", line 31, in connect_tcp
| return await self._backend.connect_tcp(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ...<5 lines>...
| )
| ^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_backends\anyio.py", line 113, in connect_tcp
| with map_exceptions(exc_map):
| ~~~~~~~~~~~~~~^^^^^^^^^
| File "[REDACTED]\Python313\Lib\contextlib.py", line 162, in __exit__
| self.gen.throw(value)
| ~~~~~~~~~~~~~~^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_exceptions.py", line 14, in map_exceptions
| raise to_exc(exc) from exc
| httpcore.ConnectError: All connection attempts failed
|
| The above exception was the direct cause of the following exception:
|
| Traceback (most recent call last):
| File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 405, in handle_request_async
| await self._handle_post_request(ctx)
| File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 259, in _handle_post_request
| async with ctx.client.stream(
| ~~~~~~~~~~~~~~~~~^
| "POST",
| ^^^^^^^
| ...<2 lines>...
| headers=headers,
| ^^^^^^^^^^^^^^^^
| ) as response:
| ^
| File "[REDACTED]\Python313\Lib\contextlib.py", line 214, in __aenter__
| return await anext(self.gen)
| ^^^^^^^^^^^^^^^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1583, in stream
| response = await self.send(
| ^^^^^^^^^^^^^^^^
| ...<4 lines>...
| )
| ^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1629, in send
| response = await self._send_handling_auth(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ...<4 lines>...
| )
| ^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1657, in _send_handling_auth
| response = await self._send_handling_redirects(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ...<3 lines>...
| )
| ^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1694, in _send_handling_redirects
| response = await self._send_single_request(request)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1730, in _send_single_request
| response = await transport.handle_async_request(request)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 393, in handle_async_request
| with map_httpcore_exceptions():
| ~~~~~~~~~~~~~~~~~~~~~~~^^
| File "[REDACTED]\Python313\Lib\contextlib.py", line 162, in __exit__
| self.gen.throw(value)
| ~~~~~~~~~~~~~~^^^^^^^
| File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions
| raise mapped_exc(message) from exc
| httpx.ConnectError: All connection attempts failed
+---------------- 2 ----------------
| Traceback (most recent call last):
| File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 498, in streamablehttp_client
| yield (
| ...<3 lines>...
| )
| GeneratorExit
+------------------------------------
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 474, in streamablehttp_client
async with anyio.create_task_group() as tg:
~~~~~~~~~~~~~~~~~~~~~~~^^
File "[REDACTED]\venvs\dsa\Lib\site-packages\anyio\_backends\_asyncio.py", line 778, in __aexit__
if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__):
~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[REDACTED]\venvs\dsa\Lib\site-packages\anyio\_backends\_asyncio.py", line 457, in __exit__
raise RuntimeError(
...<2 lines>...
)
RuntimeError: Attempted to exit cancel scope in a different task than it was entered in
Traceback (most recent call last):
File "[REDACTED]\scratch_2.py", line 25, in <module>
asyncio.run(main())
~~~~~~~~~~~^^^^^^^^
File "[REDACTED]\Python313\Lib\asyncio\runners.py", line 195, in run
return runner.run(main)
~~~~~~~~~~^^^^^^
File "[REDACTED]\Python313\Lib\asyncio\runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "[REDACTED]\Python313\Lib\asyncio\base_events.py", line 725, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "[REDACTED]\scratch_2.py", line 21, in main
await do_other_stuff() # <- will cause "Cancelled by cancel scope ..."
^^^^^^^^^^^^^^^^^^^^^^
File "[REDACTED]\scratch_2.py", line 7, in do_other_stuff
await asyncio.sleep(2)
File "[REDACTED]\Python313\Lib\asyncio\tasks.py", line 718, in sleep
return await future
^^^^^^^^^^^^
asyncio.exceptions.CancelledError: Cancelled by cancel scope 181bc7de3c0