pub struct McpServerManager { /* private fields */ }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
impl McpServerManager
Sourcepub fn new(configs: HashMap<String, McpServerConfig>) -> McpServerManager
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.
Sourcepub fn from_json(json: &str) -> Result<McpServerManager, AdkError>
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)?;Sourcepub fn from_json_file(
path: impl AsRef<Path>,
) -> Result<McpServerManager, AdkError>
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")?;Sourcepub fn with_elicitation_handler(
self,
handler: Arc<dyn ElicitationHandler>,
) -> McpServerManager
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.
Sourcepub fn with_health_check_interval(self, interval: Duration) -> McpServerManager
pub fn with_health_check_interval(self, interval: Duration) -> McpServerManager
Set the interval between health check cycles.
Default: 30 seconds.
Sourcepub fn with_grace_period(self, period: Duration) -> McpServerManager
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.
Sourcepub fn with_name(self, name: impl Into<String>) -> McpServerManager
pub fn with_name(self, name: impl Into<String>) -> McpServerManager
Set the name returned by the Toolset::name() implementation.
Default: "mcp_server_manager".
Sourcepub async fn start_server(&self, id: &str) -> Result<(), AdkError>
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?;Sourcepub async fn stop_server(&self, id: &str) -> Result<(), AdkError>
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?;Sourcepub async fn restart_server(&self, id: &str) -> Result<(), AdkError>
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?;Sourcepub async fn server_status(&self, id: &str) -> Result<ServerStatus, AdkError>
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);Sourcepub async fn all_statuses(&self) -> HashMap<String, ServerStatus>
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:?}");
}Sourcepub async fn running_server_count(&self) -> usize
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");Sourcepub fn start_monitoring(&self)
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();Sourcepub fn stop_monitoring(&self)
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();Sourcepub async fn add_server(
&self,
id: String,
config: McpServerConfig,
) -> Result<(), AdkError>
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?;Sourcepub async fn start_all(&self) -> HashMap<String, Result<(), AdkError>>
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}"),
}
}Sourcepub async fn shutdown(&self) -> Result<(), AdkError>
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 managerTrait Implementations§
Source§impl Drop for McpServerManager
impl Drop for McpServerManager
Source§impl Toolset for McpServerManager
impl Toolset for McpServerManager
Auto Trait Implementations§
impl Freeze for McpServerManager
impl !RefUnwindSafe for McpServerManager
impl Send for McpServerManager
impl Sync for McpServerManager
impl Unpin for McpServerManager
impl UnsafeUnpin for McpServerManager
impl !UnwindSafe for McpServerManager
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
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 moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request