Skip to main content

McpServerManager

Struct McpServerManager 

Source
pub struct McpServerManager { /* private fields */ }
Available on crate features mcp and tools only.
Expand description

Manages the full lifecycle of multiple local MCP server child processes.

McpServerManager spawns processes, connects them via TokioChildProcess transport into McpToolset instances, monitors health, auto-restarts on crash with exponential backoff, and aggregates tools from all managed servers behind the Toolset trait.

§Construction

Use McpServerManager::new with a map of server configurations, then chain builder methods to configure handlers and intervals:

use adk_tool::mcp::manager::{McpServerConfig, McpServerManager};
use std::collections::HashMap;
use std::time::Duration;

let configs = HashMap::from([
    ("my-server".to_string(), McpServerConfig {
        command: "/opt/company/bin/workspace-mcp".to_string(),
        args: vec!["--stdio".to_string(), "--root".to_string(), "/srv/workspace".to_string()],
        ..Default::default()
    }),
]);

let manager = McpServerManager::new(configs)
    .with_health_check_interval(Duration::from_secs(15))
    .with_grace_period(Duration::from_secs(3))
    .with_name("my_manager");

Implementations§

Source§

impl McpServerManager

Source

pub fn new(configs: HashMap<String, McpServerConfig>) -> McpServerManager

Create a new McpServerManager from a map of server configurations.

Each entry is keyed by a unique server ID. Servers with disabled: true are initialized with ServerStatus::Disabled; all others start as ServerStatus::Stopped.

No servers are started automatically — call start_server or start_all to begin spawning processes.

Source

pub fn from_json(json: &str) -> Result<McpServerManager, AdkError>

Create a new McpServerManager by parsing a JSON string in Kiro mcp.json format.

The JSON must contain a top-level mcpServers object mapping server IDs to their configurations. CamelCase JSON field names are automatically mapped to snake_case Rust fields.

§Errors

Returns AdkError::Tool if the JSON is malformed or missing required fields.

§Example
let json = r#"{
    "mcpServers": {
        "workspace": {
            "command": "/opt/company/bin/workspace-mcp",
            "args": ["--stdio", "--root", "/srv/workspace"]
        }
    }
}"#;
let manager = McpServerManager::from_json(json)?;
Source

pub fn from_json_file( path: impl AsRef<Path>, ) -> Result<McpServerManager, AdkError>

Create a new McpServerManager by reading and parsing a JSON file from disk.

The file must contain JSON in Kiro mcp.json format (see from_json). File reading is synchronous, which is acceptable for config loading at startup.

§Errors

Returns AdkError::Tool if the file cannot be read or the JSON is malformed.

§Example
let manager = McpServerManager::from_json_file("mcp.json")?;
Source

pub fn with_elicitation_handler( self, handler: Arc<dyn ElicitationHandler>, ) -> McpServerManager

Set the elicitation handler used for all managed server connections.

The handler is preserved across server restarts via Arc sharing.

Source

pub fn with_resource_notification_handler( self, handler: Arc<dyn ResourceNotificationHandler>, ) -> McpServerManager

Set the resource notification handler used for all managed connections.

The handler is retained across manual and automatic server restarts.

Source

pub fn with_health_check_interval(self, interval: Duration) -> McpServerManager

Set the interval between health check cycles.

Default: 30 seconds.

Source

pub fn with_grace_period(self, period: Duration) -> McpServerManager

Set the grace period reserved for managed-session shutdown.

Default: 5 seconds.

Source

pub fn with_name(self, name: impl Into<String>) -> McpServerManager

Set the name returned by the Toolset::name() implementation.

Default: "mcp_server_manager".

Source

pub async fn start_server(&self, id: &str) -> Result<(), AdkError>

Start a managed MCP server by ID.

Spawns the configured command as a child process, creates a TokioChildProcess transport, and connects via McpToolset with the configured elicitation (and optionally sampling) handler.

If the server is already Running, this is a no-op and returns Ok(()).

§Errors

Returns AdkError::Tool if:

  • The server ID does not exist
  • The child process fails to spawn
  • The MCP handshake fails
§Example
manager.start_server("my-server").await?;
Source

pub async fn stop_server(&self, id: &str) -> Result<(), AdkError>

Stop a managed MCP server by ID.

Cancels the MCP session via the toolset’s cancellation token, drops the McpToolset connection, and sets the status to Stopped.

If the server is not running, this is a no-op and returns Ok(()).

§Errors

Returns AdkError::Tool if the server ID does not exist.

§Example
manager.stop_server("my-server").await?;
Source

pub async fn restart_server(&self, id: &str) -> Result<(), AdkError>

Restart a managed MCP server by ID.

Sets the status to Restarting, stops the server, then starts it again. The same ElicitationHandler and SamplingHandler Arcs are preserved across the restart.

§Errors

Returns AdkError::Tool if:

  • The server ID does not exist
  • The start phase fails (status set to FailedToStart)
§Example
manager.restart_server("my-server").await?;
Source

pub async fn server_status(&self, id: &str) -> Result<ServerStatus, AdkError>

Return the current ServerStatus for a given server ID.

§Errors

Returns AdkError::Tool if the server ID does not exist.

§Example
let status = manager.server_status("my-server").await?;
assert_eq!(status, ServerStatus::Running);
Source

pub async fn list_server_resources( &self, id: &str, ) -> Result<Vec<Resource>, AdkError>

List static resources published by one running managed server.

Source

pub async fn list_server_resource_templates( &self, id: &str, ) -> Result<Vec<ResourceTemplate>, AdkError>

List URI templates published by one running managed server.

Source

pub async fn list_server_prompts( &self, id: &str, ) -> Result<Vec<Prompt>, AdkError>

List prompt templates published by one running managed server.

Source

pub async fn read_server_resource( &self, id: &str, uri: &str, ) -> Result<Vec<ResourceContents>, AdkError>

Read one resource from a running managed server.

Source

pub async fn subscribe_server_resource( &self, id: &str, uri: &str, ) -> Result<(), AdkError>

Subscribe to updates for one resource on a running managed server.

Source

pub async fn unsubscribe_server_resource( &self, id: &str, uri: &str, ) -> Result<(), AdkError>

Remove a resource subscription from one running managed server.

Source

pub async fn get_server_prompt( &self, id: &str, name: &str, arguments: Option<Map<String, Value>>, ) -> Result<GetPromptResult, AdkError>

Resolve one prompt from a running managed server.

Source

pub async fn all_statuses(&self) -> HashMap<String, ServerStatus>

Return a map of all server IDs to their current ServerStatus.

§Example
let statuses = manager.all_statuses().await;
for (id, status) in &statuses {
    println!("{id}: {status:?}");
}
Source

pub async fn running_server_count(&self) -> usize

Return the number of servers currently in ServerStatus::Running status.

§Example
let count = manager.running_server_count().await;
println!("{count} servers running");
Source

pub fn start_monitoring(&self)

Start the background health monitoring task.

Spawns a tokio::spawn task that periodically checks each Running server by calling McpToolset::is_closed(). If a server’s connection is closed, the monitor sets its status to Crashed and, if a RestartPolicy is configured, attempts auto-restart with exponential backoff.

The monitoring loop runs until stop_monitoring is called, which cancels the background task via the internal CancellationToken.

§Example
manager.start_monitoring();
// ... later ...
manager.stop_monitoring();
Source

pub fn stop_monitoring(&self)

Stop the background health monitoring task.

Cancels the monitoring loop spawned by start_monitoring. This is a no-op if monitoring was never started or has already been stopped.

§Example
manager.stop_monitoring();
Source

pub async fn add_server( &self, id: String, config: McpServerConfig, ) -> Result<(), AdkError>

Register a new server configuration at runtime.

The new server is initialized with ServerStatus::Disabled if config.disabled is true, or ServerStatus::Stopped otherwise. It will not be started automatically — call start_server to begin spawning the process.

§Errors

Returns AdkError::Tool if a server with the given ID already exists.

§Example
let config = McpServerConfig {
    command: "/opt/company/bin/billing-mcp".to_string(),
    args: vec!["--stdio".to_string()],
    ..Default::default()
};
manager.add_server("new-server".to_string(), config).await?;
Source

pub async fn server_config(&self, id: &str) -> Result<McpServerConfig, AdkError>

Return a copy of one server’s current configuration.

Source

pub async fn all_configs(&self) -> HashMap<String, McpServerConfig>

Return a snapshot of every managed server configuration.

Source

pub async fn update_server( &self, id: &str, config: McpServerConfig, ) -> Result<(), AdkError>

Replace a server configuration at runtime.

A running server is stopped and restarted with the new configuration. If the replacement fails to start, the previous configuration is restored and restarted before the error is returned.

Source

pub async fn enable_server(&self, id: &str) -> Result<(), AdkError>

Enable a disabled server without starting it.

Source

pub async fn disable_server(&self, id: &str) -> Result<(), AdkError>

Stop and disable a server until it is explicitly enabled again.

Source

pub async fn to_json(&self) -> Result<String, AdkError>

Serialize the current in-memory configuration as compatible mcp.json.

Source

pub async fn save_json_file( &self, path: impl AsRef<Path>, ) -> Result<(), AdkError>

Persist the current configuration using an atomic temporary-file rename.

Source

pub async fn remove_server(&self, id: &str) -> Result<(), AdkError>

Remove a server configuration at runtime.

If the server is currently running, it is stopped first using the graceful stop sequence before being removed from the manager.

§Errors

Returns AdkError::Tool if the server ID does not exist.

§Example
manager.remove_server("old-server").await?;
Source

pub async fn start_all(&self) -> HashMap<String, Result<(), AdkError>>

Start all non-disabled servers and report each result independently.

Collects all server IDs where disabled == false, then starts each one via start_server. Failures are logged but do not prevent other servers from starting. Registry mutations are serialized while each child process completes its MCP handshake.

§Returns

A HashMap<String, Result<()>> with per-server outcomes. Disabled servers are not included in the result.

§Example
let results = manager.start_all().await;
for (id, result) in &results {
    match result {
        Ok(()) => println!("{id}: started"),
        Err(e) => eprintln!("{id}: failed to start: {e}"),
    }
}
Source

pub async fn shutdown(&self) -> Result<(), AdkError>

Shut down all managed servers and stop health monitoring.

This method first stops the health monitoring task, then stops all running servers by cancelling their MCP sessions and dropping the child transports. After shutdown, all server statuses are set to Stopped.

§Example
manager.shutdown().await?;
// All servers are now stopped, safe to drop the manager

Trait Implementations§

Source§

impl Drop for McpServerManager

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl Toolset for McpServerManager

Source§

fn name(&self) -> &str

Returns the name of this toolset.
Source§

fn tools<'life0, 'async_trait>( &'life0 self, ctx: Arc<dyn ReadonlyContext>, ) -> Pin<Box<dyn Future<Output = Result<Vec<Arc<dyn Tool>>, AdkError>> + Send + 'async_trait>>
where 'life0: 'async_trait, McpServerManager: 'async_trait,

Returns the tools available in this toolset for the given context.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> MaybeSend for T
where T: Send,

Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more