pub struct McpServerManager { /* private fields */ }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
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": {
"workspace": {
"command": "/opt/company/bin/workspace-mcp",
"args": ["--stdio", "--root", "/srv/workspace"]
}
}
}"#;
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_resource_notification_handler(
self,
handler: Arc<dyn ResourceNotificationHandler>,
) -> McpServerManager
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.
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 reserved for managed-session shutdown.
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 list_server_resources(
&self,
id: &str,
) -> Result<Vec<Resource>, AdkError>
pub async fn list_server_resources( &self, id: &str, ) -> Result<Vec<Resource>, AdkError>
List static resources published by one running managed server.
Sourcepub async fn list_server_resource_templates(
&self,
id: &str,
) -> Result<Vec<ResourceTemplate>, AdkError>
pub async fn list_server_resource_templates( &self, id: &str, ) -> Result<Vec<ResourceTemplate>, AdkError>
List URI templates published by one running managed server.
Sourcepub async fn list_server_prompts(
&self,
id: &str,
) -> Result<Vec<Prompt>, AdkError>
pub async fn list_server_prompts( &self, id: &str, ) -> Result<Vec<Prompt>, AdkError>
List prompt templates published by one running managed server.
Sourcepub async fn read_server_resource(
&self,
id: &str,
uri: &str,
) -> Result<Vec<ResourceContents>, AdkError>
pub async fn read_server_resource( &self, id: &str, uri: &str, ) -> Result<Vec<ResourceContents>, AdkError>
Read one resource from a running managed server.
Sourcepub async fn subscribe_server_resource(
&self,
id: &str,
uri: &str,
) -> Result<(), AdkError>
pub async fn subscribe_server_resource( &self, id: &str, uri: &str, ) -> Result<(), AdkError>
Subscribe to updates for one resource on a running managed server.
Sourcepub async fn unsubscribe_server_resource(
&self,
id: &str,
uri: &str,
) -> Result<(), AdkError>
pub async fn unsubscribe_server_resource( &self, id: &str, uri: &str, ) -> Result<(), AdkError>
Remove a resource subscription from one running managed server.
Sourcepub async fn get_server_prompt(
&self,
id: &str,
name: &str,
arguments: Option<Map<String, Value>>,
) -> Result<GetPromptResult, AdkError>
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.
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: "/opt/company/bin/billing-mcp".to_string(),
args: vec!["--stdio".to_string()],
..Default::default()
};
manager.add_server("new-server".to_string(), config).await?;Sourcepub async fn server_config(&self, id: &str) -> Result<McpServerConfig, AdkError>
pub async fn server_config(&self, id: &str) -> Result<McpServerConfig, AdkError>
Return a copy of one server’s current configuration.
Sourcepub async fn all_configs(&self) -> HashMap<String, McpServerConfig>
pub async fn all_configs(&self) -> HashMap<String, McpServerConfig>
Return a snapshot of every managed server configuration.
Sourcepub async fn update_server(
&self,
id: &str,
config: McpServerConfig,
) -> Result<(), AdkError>
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.
Sourcepub async fn enable_server(&self, id: &str) -> Result<(), AdkError>
pub async fn enable_server(&self, id: &str) -> Result<(), AdkError>
Enable a disabled server without starting it.
Sourcepub async fn disable_server(&self, id: &str) -> Result<(), AdkError>
pub async fn disable_server(&self, id: &str) -> Result<(), AdkError>
Stop and disable a server until it is explicitly enabled again.
Sourcepub async fn to_json(&self) -> Result<String, AdkError>
pub async fn to_json(&self) -> Result<String, AdkError>
Serialize the current in-memory configuration as compatible mcp.json.
Sourcepub async fn save_json_file(
&self,
path: impl AsRef<Path>,
) -> Result<(), AdkError>
pub async fn save_json_file( &self, path: impl AsRef<Path>, ) -> Result<(), AdkError>
Persist the current configuration using an atomic temporary-file rename.
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 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}"),
}
}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 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 managerTrait Implementations§
Source§impl Drop for McpServerManager
impl Drop for McpServerManager
Source§impl Toolset for McpServerManager
impl Toolset for McpServerManager
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,
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§
impl !Freeze for McpServerManager
impl !RefUnwindSafe for McpServerManager
impl !UnwindSafe for McpServerManager
impl Send for McpServerManager
impl Sync for McpServerManager
impl Unpin for McpServerManager
impl UnsafeUnpin 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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.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 moreimpl<T> MaybeSend for Twhere
T: Send,
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
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
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
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
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.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
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.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
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.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
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.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
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.