Skip to content

[BUG] RESTAPI.run() Fails in Async Context Due to Nested asyncio Event Loop with MCPServerStdio #199

@PrabhashDiss

Description

@PrabhashDiss

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

  1. Create an agent with MCPServerStdio (e.g., CloudWatch MCP server)
  2. Connect the MCP server with await mcp_server.connect()
  3. Initialize OpenAI module with agents
  4. Call RESTAPI.run() from within an async function
  5. 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:

  1. Provide an async version like await RESTAPI.serve() that doesn't create a new event loop
  2. Use uvicorn.Server with await server.serve() instead of uvicorn.run()
  3. Document the incompatibility and provide a workaround for using RESTAPI with MCPServerStdio

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions