Gestion dynamique du serveur MCP

McpServerManager possĂšde un registre d’exĂ©cution des processus enfants de serveur MCP locaux. Utilisez-le lorsque les intĂ©grations changent selon l’espace de travail, le locataire, la sĂ©lection de l’administrateur ou la configuration de dĂ©ploiement.

Ce n’est pas le pool de connexions pour les services HTTP distants. Construisez ceux-ci avec McpHttpClientBuilder et conservez leur cycle de vie dans l’application qui possùde la configuration distante.

Cycle de vie

Rendering architecture


Le moniteur vérifie si la connexion MCP a été fermée. Il réessaie les démarrages plantés ou échoués uniquement tant que le RestartPolicy configuré a encore des tentatives restantes.

Réglages du gestionnaire

{
  "mcpServers": {
    "workspace-tools": {
      "command": "/opt/company/bin/workspace-mcp",
      "args": ["--stdio", "--root", "/srv/workspace"],
      "env": {
        "RUST_LOG": "info"
      },
      "disabled": false,
      "autoApprove": [],
      "restartPolicy": {
        "initialDelayMs": 500,
        "maxDelayMs": 15000,
        "backoffMultiplier": 2.0,
        "maxRestartAttempts": 5
      }
    }
  }
}

Les identifiants de serveur acceptent des lettres ASCII, des chiffres, des tirets et des underscores. Utilisez un ID stable, car il devient une partie d’un nom d’outil prĂ©fixĂ© par collision.

autoApprove est lu et Ă©crit pour la compatibilitĂ© de configuration. Le gestionnaire n’accorde pas d’approbation Ă  partir de ce champ.

Démarrer et agréger les outils

use adk_tool::mcp::manager::McpServerManager;
use std::sync::Arc;
use std::time::Duration;

let manager = Arc::new(McpServerManager::from_json_file("mcp.json")?
    .with_name("workspace_mcp")
    .with_health_check_interval(Duration::from_secs(15))
    .with_grace_period(Duration::from_secs(2)));

let outcomes = manager.start_all().await;
for (server_id, outcome) in outcomes {
    if let Err(error) = outcome {
        eprintln!("{server_id}: {error}");
    }
}

manager.start_monitoring();

let agent = LlmAgentBuilder::new("operator")
    .model(model)
    .toolset(manager.clone())
    .build()?;

Les mutations du registre sont sĂ©rialisĂ©es pendant qu’un enfant termine sa poignĂ©e de main MCP. start_all renvoie un rĂ©sultat indĂ©pendant pour chaque serveur activĂ©, mais le dĂ©marrage n’est pas actuellement un chemin de poignĂ©e de main parallĂšle.

Modifier le registre Ă  l’exĂ©cution

manager.add_server("billing".into(), billing_config).await?;
manager.start_server("billing").await?;

manager.update_server("billing", replacement_config).await?;
manager.disable_server("billing").await?;
manager.enable_server("billing").await?;

let snapshot = manager.all_configs().await;
manager.save_json_file("mcp.json").await?;

manager.remove_server("billing").await?;
manager.shutdown().await?;

La mise Ă  jour d’un serveur en cours d’exĂ©cution l’arrĂȘte et dĂ©marre le remplacement. Si le remplacement Ă©choue, le gestionnaire restaure et redĂ©marre la dĂ©finition prĂ©cĂ©dente avant de renvoyer l’erreur du remplacement.

save_json_file écrit un fichier temporaire dans le répertoire de destination puis le renomme par-dessus la destination.

Ressources, invites et notifications

Un serveur géré peut publier des ressources et des invites en plus des outils. Le gestionnaire expose la surface des ressources et des invites de chaque serveur par ID de serveur, et transmet les notifications resources/updated / resources/list_changed à un gestionnaire partagé entre toutes les connexions gérées.

Enregistrez le gestionnaire une seule fois ; il est conservé entre les redémarrages manuels et automatiques :

use adk_tool::{ResourceNotificationHandler, mcp::manager::McpServerManager};
use std::sync::Arc;

struct ReloadOnChange;

#[async_trait::async_trait]
impl ResourceNotificationHandler for ReloadOnChange {
    async fn handle_resource_updated(
        &self,
        uri: &str,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        tracing::info!(%uri, "resource changed; re-read it to refresh cached state");
        Ok(())
    }

    async fn handle_resource_list_changed(
        &self,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        Ok(())
    }
}

let manager = Arc::new(
    McpServerManager::from_json_file("mcp.json")?
        .with_resource_notification_handler(Arc::new(ReloadOnChange)),
);
manager.start_server("workspace-tools").await?;

Puis lisez et abonnez-vous par serveur :

let resources = manager.list_server_resources("workspace-tools").await?;
let templates = manager.list_server_resource_templates("workspace-tools").await?;
let contents = manager.read_server_resource("workspace-tools", "config://policy").await?;

let prompts = manager.list_server_prompts("workspace-tools").await?;
let review = manager
    .get_server_prompt("workspace-tools", "review_pr", None)
    .await?;

// Subscribe / unsubscribe. Subscriptions are restored automatically if the
// managed process reconnects.
manager.subscribe_server_resource("workspace-tools", "config://policy").await?;
manager.unsubscribe_server_resource("workspace-tools", "config://policy").await?;

Chaque mĂ©thode *_server_* cible un serveur en cours d’exĂ©cution par ID et renvoie AdkError::Tool si le serveur est inconnu ou n’est pas actuellement en cours d’exĂ©cution.

Pour une seule connexion (plutĂŽt que le registre du gestionnaire), la mĂȘme surface est disponible directement sur McpToolset via McpToolset::with_handlers, list_resources, read_resource, list_prompts, get_prompt, et subscribe_resource. Voir l’exemple agentique exĂ©cutable :

cargo run --manifest-path examples/mcp_resources/Cargo.toml --bin resources-client

Conflits de noms d’outils

Si deux serveurs en cours d’exĂ©cution publient search, les noms agrĂ©gĂ©s deviennent :

crm__search
knowledge__search

Un nom d’outil publiĂ© par un seul serveur reste inchangĂ©.

Comportement Ă  l’arrĂȘt

L’arrĂȘt d’un serveur annule sa session MCP, attend jusqu’à la pĂ©riode de grĂące configurĂ©e pour que la connexion se ferme, puis supprime le transport. Le processus enfant est dĂ©tenu par le transport TokioChildProcess plutĂŽt que par un handle de processus conservĂ© sĂ©parĂ©ment.

Appelez shutdown() avant de supprimer le gestionnaire. Supprimer un gestionnaire avec des serveurs en cours d’exĂ©cution Ă©met un avertissement car Drop ne peut pas attendre le nettoyage asynchrone.

Exemple vérifié

cargo run --manifest-path examples/mcp_manager/Cargo.toml

L’exemple dĂ©marre un vrai processus enfant Rust MCP, dĂ©couvre et appelle un outil, ajoute et active un second serveur, le met Ă  jour, enregistre le registre, dĂ©sactive et supprime celui-ci, et ferme toutes les sessions. Il ne nĂ©cessite aucun modĂšle, aucune clĂ© API, aucun tĂ©lĂ©chargement de paquet, ni accĂšs au rĂ©seau.

Gestion dynamique du serveur MCP - Documentation ADK-Rust | ADK-Rust