Bug Description
RESTAPI.run() causes nested event loop conflict when used with MCPServerStdio in an async context. The API server internally calls uvicorn.run() which attempts to create a new event loop with asyncio.run(), but this fails because it's called from within an already running event loop created by asyncio.run(main()).
Steps to Reproduce
- Create an agent with
MCPServerStdio (e.g., CloudWatch MCP server)
- Connect the MCP server with
await mcp_server.connect()
- Initialize OpenAI module with agents
- Call
RESTAPI.run() from within an async function
- Run the async function with
asyncio.run(main())
A Reproducible Example
import asyncio
import os
from agentkernel.api import RESTAPI
from agentkernel.openai import OpenAIModule
from agents import Agent
from agents.mcp import MCPServerStdio
cloudwatch_mcp_server = MCPServerStdio(
name="AWS CloudWatch MCP Server",
params={
"command": "uvx",
"args": ["awslabs.cloudwatch-mcp-server@latest"],
"env": {
"AWS_REGION": os.getenv("AWS_REGION", ""),
"AWS_ACCESS_KEY_ID": os.getenv("AWS_ACCESS_KEY_ID", ""),
"AWS_SECRET_ACCESS_KEY": os.getenv("AWS_SECRET_ACCESS_KEY", ""),
"AWS_SESSION_TOKEN": os.getenv("AWS_SESSION_TOKEN", ""),
"FASTMCP_LOG_LEVEL": "ERROR",
},
},
)
cloudwatch_agent = Agent(
name="cloudwatch",
instructions="You assist with AWS CloudWatch related queries.",
mcp_servers=[cloudwatch_mcp_server],
)
triage_agent = Agent(
name="triage",
instructions="You determine which agent to use based on the user's question.",
handoffs=[cloudwatch_agent],
)
async def main():
await cloudwatch_mcp_server.connect()
OpenAIModule([triage_agent, cloudwatch_agent])
RESTAPI.run() # This line causes the error
if __name__ == "__main__":
try:
asyncio.run(main())
except asyncio.CancelledError:
pass
Expected Behavior
The REST API should start successfully and serve requests while maintaining the MCP server connection, similar to how CLI().run() works in async contexts.
Actual Behavior
The application crashes with nested event loop errors when RESTAPI.run() is called.
Environment
- Agent Kernel Version: 0.2.10
- Python Version: 3.12.3
- Operating System: Linux
- MCP Server: awslabs.cloudwatch-mcp-server (via uvx)
- Additional dependencies: openai-agents-python with MCP support
Logs
RuntimeError: asyncio.run() cannot be called from a running event loop
Traceback (most recent call last):
File "/home/prabhash-dissanayake/Projects/p8-troubleshooter/demo.py", line 100, in <module>
asyncio.run(main())
File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
File "/usr/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete
return future.result()
File "/home/prabhash-dissanayake/Projects/p8-troubleshooter/demo.py", line 95, in main
RESTAPI.run()
File "/home/prabhash-dissanayake/Projects/p8-troubleshooter/.venv/lib/python3.12/site-packages/agentkernel/api/http.py", line 96, in run
uvicorn.run(app=app, host=host, port=port, reload=False)
File "/home/prabhash-dissanayake/Projects/p8-troubleshooter/.venv/lib/python3.12/site-packages/uvicorn/main.py", line 594, in run
server.run()
File "/home/prabhash-dissanayake/Projects/p8-troubleshooter/.venv/lib/python3.12/site-packages/uvicorn/server.py", line 67, in run
return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
RuntimeError: asyncio.run() cannot be called from a running event loop
Additionally:
RuntimeError: Attempted to exit cancel scope in a different task than it was entered in
asyncgen: <async_generator object stdio_client at 0x760127dfb0d0>
BaseExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
Additional Context
According to the OpenAI Agents Python MCP documentation, MCPServerStdio should be used with async context managers. The issue occurs because RESTAPI.run() uses uvicorn.run() which internally calls asyncio.run(), creating a nested event loop conflict.
Previously, the code worked with CLI().run(). The migration to REST API broke compatibility with MCP servers.
Suggested Solutions:
- Provide an async version like
await RESTAPI.serve() that doesn't create a new event loop
- Use
uvicorn.Server with await server.serve() instead of uvicorn.run()
- Document the incompatibility and provide a workaround for using RESTAPI with MCPServerStdio
Bug Description
RESTAPI.run()causes nested event loop conflict when used withMCPServerStdioin an async context. The API server internally callsuvicorn.run()which attempts to create a new event loop withasyncio.run(), but this fails because it's called from within an already running event loop created byasyncio.run(main()).Steps to Reproduce
MCPServerStdio(e.g., CloudWatch MCP server)await mcp_server.connect()RESTAPI.run()from within an async functionasyncio.run(main())A Reproducible Example
Expected Behavior
The REST API should start successfully and serve requests while maintaining the MCP server connection, similar to how
CLI().run()works in async contexts.Actual Behavior
The application crashes with nested event loop errors when
RESTAPI.run()is called.Environment
Logs
Additional Context
According to the OpenAI Agents Python MCP documentation,
MCPServerStdioshould be used with async context managers. The issue occurs becauseRESTAPI.run()usesuvicorn.run()which internally callsasyncio.run(), creating a nested event loop conflict.Previously, the code worked with
CLI().run(). The migration to REST API broke compatibility with MCP servers.Suggested Solutions:
await RESTAPI.serve()that doesn't create a new event loopuvicorn.Serverwithawait server.serve()instead ofuvicorn.run()