Skip to main content

construct/mcp_server/
runtime.rs

1//! Runtime handles the MCP server may optionally receive from the main daemon.
2//!
3//! The standalone MCP binary (now removed) had to boot blind: it only knew the
4//! workspace dir and a best-effort `Config`. Tools that depend on live runtime
5//! state — a delegate agent pool, the channel map, a workspace manager, a
6//! session store, a concrete provider — were silently skipped.
7//!
8//! Now that the MCP server runs as an in-process tokio task inside the main
9//! daemon, the gateway can hand those handles down via [`RuntimeHandles`] and
10//! the registry can register the previously-skipped tools.
11//!
12//! ## Design rules
13//!
14//! - Every field is `Option<Arc<_>>` so missing handles **degrade gracefully**
15//!   — the tool is listed in `skipped` with a reason, never a panic.
16//! - We deliberately **do not** import `gateway::AppState` here. That would
17//!   create a module-layer cycle (gateway → mcp_server → gateway). Callers
18//!   clone the Arcs they need out of `AppState` and pass them individually.
19//! - Types come from the modules that own them, not from some central bag.
20//!   If a caller wants to wire up `discord_search`, they pass the `Arc<dyn
21//!   Memory>` backing discord.db directly — same shape the gateway uses.
22
23use crate::channels::session_backend::SessionBackend;
24use crate::config::DelegateAgentConfig;
25use crate::config::workspace::WorkspaceManager;
26use crate::memory::Memory;
27use crate::providers::ProviderRuntimeOptions;
28use crate::tools::Tool;
29use crate::tools::ask_user::ChannelMapHandle;
30use std::collections::HashMap;
31use std::sync::Arc;
32use tokio::sync::RwLock as TokioRwLock;
33
34/// Aggregates `Arc`-clones of everything the previously-skipped MCP tools need.
35///
36/// The gateway builds this after its `AppState` is fully constructed, then
37/// hands it to [`build_tools_with_runtime`](super::registry::build_tools_with_runtime).
38///
39/// All fields are optional; the registry gracefully skips a tool whenever its
40/// required handle is `None`.
41#[derive(Default, Clone)]
42pub struct RuntimeHandles {
43    /// Workspace manager for the `workspace` tool.
44    pub workspace_manager: Option<Arc<TokioRwLock<WorkspaceManager>>>,
45
46    /// Shared channel map (platform name → `Arc<dyn Channel>`) used by the
47    /// `poll`, `reaction`, `ask_user`, and `escalate` tools.
48    ///
49    /// The same `Arc<RwLock<_>>` is threaded into every tool that needs it;
50    /// when the channel supervisor populates it, all tools see the update.
51    pub channel_map: Option<ChannelMapHandle>,
52
53    /// Dedicated channel map handle for the `reaction` tool (it keeps its own
54    /// handle so the gateway-facing tool and the MCP-facing tool share it).
55    pub reaction_channels: Option<ChannelMapHandle>,
56
57    /// Dedicated channel map handle for the `ask_user` tool.
58    pub ask_user_channels: Option<ChannelMapHandle>,
59
60    /// Dedicated channel map handle for the `escalate` tool.
61    pub escalate_channels: Option<ChannelMapHandle>,
62
63    /// Memory backend for `discord_search` (historically a separate
64    /// SQLite store; currently unwired — pending a Kumiho-backed
65    /// reimplementation).
66    pub discord_memory: Option<Arc<dyn Memory>>,
67
68    /// Session backend used by the `sessions_list`, `sessions_history`, and
69    /// `sessions_send` tools.
70    pub session_store: Option<Arc<dyn SessionBackend>>,
71
72    /// Agent configurations for `delegate` and `swarm`. Cloned out of
73    /// `Config.agents`.
74    pub agent_config: Option<Arc<HashMap<String, DelegateAgentConfig>>>,
75
76    /// Provider runtime options — supplies the credentials/url/reasoning
77    /// preferences delegate sub-agents need when invoking LLMs.
78    pub provider_runtime_options: Option<Arc<ProviderRuntimeOptions>>,
79
80    /// Fallback API key for delegate sub-agents when the per-agent config
81    /// doesn't supply one (same value passed to `all_tools_with_runtime`).
82    pub fallback_api_key: Option<Arc<str>>,
83
84    /// Escape hatch: pre-built tools constructed by the caller (e.g. the
85    /// gateway's `all_tools_with_runtime` list) that the MCP registry should
86    /// merge in wholesale.
87    ///
88    /// When present, each entry is folded in after the baseline — if a name
89    /// collides, the pre-built tool wins. This lets the gateway forward its
90    /// full live tool registry to MCP clients without the registry having to
91    /// re-invoke every tool's constructor.
92    pub pre_built_tools: Option<Vec<Arc<dyn Tool>>>,
93}
94
95impl RuntimeHandles {
96    /// Create an empty `RuntimeHandles` where every field is `None`.
97    ///
98    /// Used by tests and by the degraded boot path if AppState construction
99    /// failed before the MCP task spawned.
100    #[must_use]
101    pub fn empty() -> Self {
102        Self::default()
103    }
104}