Skip to main content

McpServerManager

Struct McpServerManager 

Source
pub struct McpServerManager { /* private fields */ }
Available on crate feature 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: "npx".to_string(),
        args: vec!["-y".to_string(), "@modelcontextprotocol/server-filesystem".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": {
        "filesystem": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
        }
    }
}"#;
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_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 to wait for a child process to exit before force-killing.

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 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: "npx".to_string(),
    args: vec!["-y".to_string(), "server".to_string()],
    ..Default::default()
};
manager.add_server("new-server".to_string(), config).await?;
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 concurrently.

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.

§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 using the graceful stop sequence (cancel token → grace period → force-kill). 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§

impl Toolset for McpServerManager

Source§

fn name(&self) -> &str

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,

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<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
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> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

Source§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: 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: 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> Same for T

Source§

type Output = T

Should always be Self
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