Problem
MCP is currently modeled directly in AgentLoop through MCP-specific state and lifecycle methods such as _mcp_servers, _mcp_stacks, _connect_mcp(), and close_mcp(). This works for the immediate reconnect/crash fix in #4843, but it leaks a tool-specific provider lifecycle into the core agent loop.
MCP is not a single static tool. It is a tool provider: it connects to external servers, dynamically registers multiple tools/resources/prompts, supports reconnect and hot reload, and owns long-lived async transport cleanup. Those responsibilities are closer to nanobot/agent/tools/mcp.py or a general tool-provider runtime layer than to AgentLoop.
Why this matters
AgentLoop should coordinate turns, sessions, tasks, and shutdown, not know MCP transport details.
- MCP-specific cleanup concerns such as retired stacks, AnyIO cancel-scope behavior, and streamable-http teardown should stay behind the MCP/tool boundary.
- Future dynamic tool providers will likely need similar lifecycle hooks; copying MCP-style special cases into
AgentLoop would make the core path harder to maintain.
Desired direction
Introduce a small tool-provider lifecycle abstraction, or an equivalent local boundary, so providers can own their own runtime state and cleanup:
- provider start/connect against a
ToolRegistry
- provider close/drain cleanup
- provider reload/hot-reload hook where applicable
- provider runtime-context lines where applicable
- grouping/ownership of dynamically registered tools
MCP can then become the first implementation, with _mcp_servers, _mcp_stacks, retired stacks, reconnect, reload, and cleanup owned outside AgentLoop.
Scope notes
This should be a follow-up refactor after #4843, not part of the production crash fix. #4843 should stay focused on #4302 and its regression coverage.
Problem
MCP is currently modeled directly in
AgentLoopthrough MCP-specific state and lifecycle methods such as_mcp_servers,_mcp_stacks,_connect_mcp(), andclose_mcp(). This works for the immediate reconnect/crash fix in #4843, but it leaks a tool-specific provider lifecycle into the core agent loop.MCP is not a single static tool. It is a tool provider: it connects to external servers, dynamically registers multiple tools/resources/prompts, supports reconnect and hot reload, and owns long-lived async transport cleanup. Those responsibilities are closer to
nanobot/agent/tools/mcp.pyor a general tool-provider runtime layer than toAgentLoop.Why this matters
AgentLoopshould coordinate turns, sessions, tasks, and shutdown, not know MCP transport details.AgentLoopwould make the core path harder to maintain.Desired direction
Introduce a small tool-provider lifecycle abstraction, or an equivalent local boundary, so providers can own their own runtime state and cleanup:
ToolRegistryMCP can then become the first implementation, with
_mcp_servers,_mcp_stacks, retired stacks, reconnect, reload, and cleanup owned outsideAgentLoop.Scope notes
This should be a follow-up refactor after #4843, not part of the production crash fix. #4843 should stay focused on #4302 and its regression coverage.