pub struct McpPool { /* private fields */ }Expand description
A shared MCP connection pool over one executor, caching each connected server’s advertised tool defs by config signature.
Implementations§
Source§impl McpPool
impl McpPool
Sourcepub fn new(shared: Arc<Mutex<ToolExecutor>>, reserved: HashSet<String>) -> Self
pub fn new(shared: Arc<Mutex<ToolExecutor>>, reserved: HashSet<String>) -> Self
Build a pool over shared, reserving reserved names from advertisement.
Sourcepub fn with_idle_disconnect_secs(self, secs: u64) -> Self
pub fn with_idle_disconnect_secs(self, secs: u64) -> Self
How long a per-agent server may sit unleased before disconnection.
0 disables it.
Sourcepub fn with_env_allowlist(self, allow: Vec<String>) -> Self
pub fn with_env_allowlist(self, allow: Vec<String>) -> Self
Allow these credential-shaped variables in MCP ${VAR} headers.
Sourcepub fn with_credential_store(self, kind: CredentialStoreKind) -> Self
pub fn with_credential_store(self, kind: CredentialStoreKind) -> Self
Read and write MCP grants through kind’s backend.
Sourcepub fn for_daemon(
shared_mcp: Arc<Mutex<ToolExecutor>>,
config_servers: &[MCPServerConfig],
) -> Arc<Self> ⓘ
pub fn for_daemon( shared_mcp: Arc<Mutex<ToolExecutor>>, config_servers: &[MCPServerConfig], ) -> Arc<Self> ⓘ
Build the daemon’s shared pool over shared_mcp: reserve built-in and
sub-agent tool names (so a server tool can’t shadow a core one) and seed
the already-connected global config_servers with empty defs, so a
blueprint that re-declares one doesn’t open a duplicate connection.
Sourcepub fn for_daemon_with(
shared_mcp: Arc<Mutex<ToolExecutor>>,
config_servers: &[MCPServerConfig],
credential_store: CredentialStoreKind,
allow_env_vars: Vec<String>,
idle_disconnect_secs: u64,
) -> Arc<Self> ⓘ
pub fn for_daemon_with( shared_mcp: Arc<Mutex<ToolExecutor>>, config_servers: &[MCPServerConfig], credential_store: CredentialStoreKind, allow_env_vars: Vec<String>, idle_disconnect_secs: u64, ) -> Arc<Self> ⓘ
for_daemon reading and writing MCP OAuth grants
through credential_store’s backend.
The pool refreshes lapsed tokens and writes them back, so it has to write them where the user asked for them to be kept - otherwise the first refresh after a keychain migration would put a fresh refresh token back on disk.
Sourcepub fn seed(&self, config: &MCPServerConfig, defs: Vec<Tool>)
pub fn seed(&self, config: &MCPServerConfig, defs: Vec<Tool>)
Seed the cache with an already-connected server’s defs (used at startup for
the global config servers, connected once by ToolRegistry::build).
Seeded servers are global: their lifecycle belongs to the daemon, so
they are exempt from lease-driven idle disconnection.
Sourcepub fn lease_blueprint(&self, blueprint_path: &str, run_id: &str)
pub fn lease_blueprint(&self, blueprint_path: &str, run_id: &str)
Record run_id as holding every per-agent server blueprint_path
declares, so the connections stay up exactly as long as some run needs
them. Global (seeded) servers are skipped. A missing or unreadable
manifest leases nothing.
Called from every path that brings a run into the world with a
blueprint: the spawner, the restart reloader, and the fan-out worker
spawner. The matching release is Self::release_run, from the reap
hook.
Sourcepub fn release_run(self: &Arc<Self>, run_id: &str)
pub fn release_run(self: &Arc<Self>, run_id: &str)
Release every lease run_id holds. Servers whose holder count reaches
zero get an idle-disconnect scheduled (when a runtime is available and
idle_disconnect is non-zero); a new lease during the grace window
bumps the generation and turns the pending disconnect into a no-op.
Sourcepub async fn disconnect_if_still_idle(
&self,
sig: &str,
name: &str,
generation: u64,
) -> bool
pub async fn disconnect_if_still_idle( &self, sig: &str, name: &str, generation: u64, ) -> bool
Tear a server down if nothing touched it since generation: forget its
cached defs (so the next spawn reconnects lazily), take its client out
of the shared executor, and shut it down - which is what actually ends
a stdio server’s child process. Returns whether it disconnected.
Sourcepub async fn ensure(&self, config: &MCPServerConfig) -> Vec<Tool>
pub async fn ensure(&self, config: &MCPServerConfig) -> Vec<Tool>
Ensure config is connected (idempotent by signature) and return its
advertised tool defs. A connection failure logs and returns no defs (the
agent simply doesn’t get that server’s tools); it is not cached, so a later
spawn retries.
Sourcepub async fn ensure_all(self: Arc<Self>, servers: Vec<MCPServerConfig>)
pub async fn ensure_all(self: Arc<Self>, servers: Vec<MCPServerConfig>)
Connect every server in servers (idempotent). Takes Arc<Self> + owned
servers so it can be tokio::spawned directly as a detached warm task
(e.g. by the fan-out spawner) without a wrapping closure.
Sourcepub async fn warm_recovered(&self, runs_dir: &Path)
pub async fn warm_recovered(&self, runs_dir: &Path)
Warm the per-agent [[mcp_servers]] of every non-terminal persisted run in
runs_dir, so a run reloaded on daemon restart can still execute its
blueprint MCP tools (their advertisement is restored from the snapshot;
only the shared connection is lost across a restart). Blueprint paths are
collected synchronously, then connected - no fs iterator is held across an
.await.
Sourcepub fn cached_defs_for(&self, configs: &[MCPServerConfig]) -> Vec<Tool>
pub fn cached_defs_for(&self, configs: &[MCPServerConfig]) -> Vec<Tool>
The cached defs for every config in configs (pool must already be warm
for them - call Self::ensure first). Unknown/unconnected configs
contribute nothing. This is the sync read the spawner uses.
Auto Trait Implementations§
impl !Freeze for McpPool
impl !RefUnwindSafe for McpPool
impl !UnwindSafe for McpPool
impl Send for McpPool
impl Sync for McpPool
impl Unpin for McpPool
impl UnsafeUnpin for McpPool
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> ConditionalSend for Twhere
T: Send,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more