Expand description
Channel-based MCP server lifecycle management.
The previous design attempted to put lifecycle timers (idle disconnect,
keep-alive health checks) inside the same McpManagerInner that is guarded
by a tokio::sync::Mutex. This caused a deadlock risk: an idle timer
callback firing on a background task would need to re-acquire the mutex,
while the agent thread might also be waiting for the callback.
To break this, lifecycle events are sent over an mpsc channel to a
dedicated background task that owns the timer handles. The background
task only holds a Weak<McpManager> (no reference cycles) and re-enters
the manager through a public async method that acquires the mutex on its
own — which can never deadlock with the agent thread because the agent
thread is not waiting for the lifecycle task while holding the lock.
[Agent task] [Lifecycle task] [McpManager]
│ │ │
│ call_tool() │ │
├─ lock(inner) ──────────────┼──────────────────────────► │
│ do work │ │
├─ unlock │ │
│ │ │
│ │ (idle timer fires) │
│ ├─ Weak::upgrade() ───────► │
│ │ disconnect_server() │
│ │ ├─ lock(inner) ───────► │
│ │ ├─ unlock │
│ │ │
│ ← no contention: agent is not holding the lock when │
│ the lifecycle task tries to acquire it. │Enums§
- Lifecycle
Event - Lifecycle events sent from
McpManagerto the background task.
Functions§
- channel
- Create a new lifecycle event channel.
- lifecycle_
event_ loop - The background task that owns all timer handles.
Type Aliases§
- Lifecycle
Rx - Receiver side of the lifecycle channel.
- Lifecycle
Tx - Sender side of the lifecycle channel.