1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! Process-global handle to the `McpServerManager`.
//!
//! The manager itself owns the child processes and the JSON-RPC
//! client pool; it's constructed once at startup (by the effect
//! runner's `Cmd::InitMcpServers` handler) and lives for the rest
//! of the session. Everything else in the codebase — today just the
//! `McpToolProxy` in `providers::tool::mcp` — reaches for it through
//! `get()`.
//!
//! Why a `OnceLock` instead of threading through `State`? The
//! manager is an async runtime object (holds tokio tasks, child
//! stdin/stdout pipes, a pending-response map). It can't live inside
//! the pure reducer's `State`, and plumbing a fat handle through
//! every `ExecContext` just so one tool can reach it would bloat the
//! common path. Process-scope is the right lifetime and a single
//! global lookup is cheap.
//!
//! `MCP_READY_NOTIFY` exists for the narrow case where a tool call
//! races the startup path — a model that spews an `mcp__…` call on
//! the very first message before any of the servers have finished
//! initializing. Callers that want a blocking "wait for ready" use
//! `notified().await` before dispatching.
use Arc;
use OnceLock;
use ;
use Notify;
use McpServerManager;
static MCP_MANAGER: = new;
/// True when MCP initialization is complete (either finished or not
/// configured). Starts true — the vast majority of sessions have no
/// MCP servers configured, and we don't want tool calls to block.
/// Flipped to false when `mark_init_started()` runs and back to true
/// when `mark_init_complete()` runs.
static MCP_INIT_COMPLETE: AtomicBool = new;
/// Wakes any waiters parked in `wait_ready()` once init completes.
static MCP_READY_NOTIFY: Notify = const_new;
/// Install the manager. Called once, at startup, by the effect
/// runner. Idempotent — subsequent calls are dropped. Returns true
/// on the first (accepted) install for callers that want to know
/// whether they were the one that won the race.
/// Read the installed manager. `None` when nothing has called
/// `set_manager` yet — typically because no MCP servers are
/// configured, so the effect runner's init handler skipped the
/// install entirely.
/// Mark init as in-progress. Pair with `mark_init_complete` when
/// done. Races are fine — this is a hint for tool calls that want
/// to wait, not a synchronization primitive.
/// Mark init as complete and wake any waiters.
/// True iff init has finished (or was never started).
/// Await init completion. Returns immediately when `is_ready()`.
/// Otherwise parks on `MCP_READY_NOTIFY`.
pub async