Heuristic:Microsoft Agent framework Async Context Manager Cleanup
| Knowledge Sources | |
|---|---|
| Domains | AI_Agents, Best_Practices |
| Last Updated | 2026-02-11 16:45 GMT |
Overview
Agents must be used with `async with` context managers to ensure proper cleanup of HTTP connections, MCP tool connections, and other managed resources.
Description
The `Agent` class implements `__aenter__` and `__aexit__` to manage the lifecycle of its internal resources. When an Agent is created with a chat client and MCP tools, those resources may hold open connections (HTTP sessions, WebSocket connections). The async context manager enters all sub-resources into an `AsyncExitStack` and ensures they are properly closed on exit. Not using the context manager causes resource leaks that accumulate over time.
Usage
Apply this heuristic every time you create and run an Agent instance. The pattern is especially critical when using MCP tools (which hold WebSocket connections) or when the chat client maintains persistent HTTP sessions.
The Insight (Rule of Thumb)
- Action: Always wrap Agent usage in `async with Agent(...) as agent:`.
- Value: Use `async with` for all Agent instances, even if you think the client doesn't need cleanup.
- Trade-off: Slightly more verbose code, but prevents resource leaks and connection exhaustion.
Reasoning
From `python/packages/core/agent_framework/_agents.py:730-762`:
async def __aenter__(self) -> Self:
"""Enter the async context manager.
If any of the client or local_mcp_tools are context managers,
they will be entered into the async exit stack to ensure proper cleanup.
"""
for context_manager in chain([self.client], self.mcp_tools):
if isinstance(context_manager, AbstractAsyncContextManager):
await self._async_exit_stack.enter_async_context(context_manager)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
"""Exit the async context manager.
Close the async exit stack to ensure all context managers are exited properly.
"""
await self._async_exit_stack.aclose()
The correct pattern:
# CORRECT - resources are properly cleaned up
async with Agent(client=client, name="my-agent", tools=[get_weather]) as agent:
response = await agent.run("What's the weather?")
print(response.text)
# INCORRECT - resource leak
agent = Agent(client=client, name="my-agent", tools=[get_weather])
response = await agent.run("What's the weather?")
# MCP connections and HTTP sessions remain open!