Créer un client MCP
Une application ADK-Rust est un client MCP lorsqu'elle se connecte à un serveur, lit son catalogue publié et met certaines capacités sélectionnées à la disposition d'un agent ou d'un workflow.
Installation
[dependencies]
adk-tool = { version = "2.1.0", features = ["mcp"] }
Pour un HTTP Streamable distant :
adk-tool = { version = "2.1.0", features = ["mcp", "http-transport"] }
Connexion stdio locale
use adk_tool::{
McpToolset,
mcp::rmcp::{ServiceExt, transport::TokioChildProcess},
};
use std::sync::Arc;
use tokio::process::Command;
let command = Command::new("./target/release/company-mcp");
let client = ().serve(TokioChildProcess::new(command)?).await?;
let toolset = McpToolset::new(client)
.with_name("company_tools")
.with_tools(&["find_customer", "read_order", "request_refund"]);
let shutdown = toolset.cancellation_token().await;
let agent = LlmAgentBuilder::new("support")
.model(model)
.toolset(Arc::new(toolset))
.build()?;
// Run the agent, then close the client-owned MCP session.
shutdown.cancel();
Utilisez un chemin absolu vers le binaire en production. Ăvitez les balises de paquet telles que latest dans la configuration de dĂ©ploiement, car elles rendent les builds et la rĂ©cupĂ©ration aprĂšs incident non reproductibles.
Découverte et filtrage des outils
McpToolset convertit chaque outil MCP publiĂ© en un ADK-Rust Tool. Il conserve inchangĂ©s les schĂ©mas d'entrĂ©e et de sortie du serveur. Le fournisseur de modĂšle sĂ©lectionnĂ© normalise une copie du schĂ©ma lors de la construction de sa requĂȘte.
L'adaptateur conserve également les annotations d'outils MCP. Un readOnlyHint indique que l'outil ADK est en lecture seule et compatible avec l'accÚs concurrent ; un idempotentHint autorise une nouvelle exécution sûre aprÚs une reconnexion, mais ne rend pas à lui seul l'outil éligible à une distribution parallÚle automatique. En l'absence d'indications, ces deux comportements restent désactivés.
Important : les annotations MCP sont des indications publiées par le serveur. N'utilisez les métadonnées de nouvelle exécution et de distribution automatiques qu'avec des serveurs situés à l'intérieur de la limite de confiance de l'application.
let reviewed = McpToolset::new(client).with_filter(|name| {
matches!(name, "read_order" | "read_policy" | "request_replacement")
});
Le filtrage contrÎle la visibilité pour le modÚle. Il ne remplace pas l'autorisation au moment de l'exécution de l'outil.
Ressources, invites et complétion
use serde_json::json;
let resources = toolset.list_resources().await?;
let templates = toolset.list_resource_templates().await?;
let policy = toolset.read_resource("company://policy/refunds").await?;
let prompts = toolset.list_prompts().await?;
let prompt = toolset
.get_prompt(
"investigate_order",
Some(serde_json::Map::from_iter([
("order_id".to_string(), json!("ORD-1042")),
])),
)
.await?;
let suggestions = toolset
.complete_prompt_argument("investigate_order", "order_id", "ORD-", None)
.await?;
La complétion des modÚles de ressources utilise complete_resource_argument. Un serveur qui n'implémente pas les opérations de liste renvoie une liste vide lorsqu'il répond avec MCP MethodNotFound ; les autres erreurs de protocole et de transport restent des erreurs.
Abonnements aux ressources
use adk_tool::{AutoDeclineElicitationHandler, McpToolset, ResourceNotificationHandler};
use std::sync::Arc;
struct ResourceUpdates;
#[async_trait::async_trait]
impl ResourceNotificationHandler for ResourceUpdates {
async fn handle_resource_updated(
&self,
uri: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Resource changed: {uri}");
Ok(())
}
async fn handle_resource_list_changed(
&self,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("The resource catalog changed");
Ok(())
}
}
let toolset = McpToolset::with_handlers(
transport,
Arc::new(AutoDeclineElicitationHandler),
Arc::new(ResourceUpdates),
).await?;
toolset.subscribe_resource("company://inventory/sku-42").await?;
toolset.unsubscribe_resource("company://inventory/sku-42").await?;
McpToolset restaure les abonnements actifs aprÚs son actualisation de connexion limitée.
McpServerManager conserve également les abonnements lors des redémarrages gérés du processus.
Les erreurs et les paniques des gestionnaires sont consignées sans mettre fin à la connexion MCP.
Pour HTTP Streamable, configurez le mĂȘme gestionnaire avec
McpHttpClientBuilder::with_resource_notification_handler avant dâappeler
connect_with_elicitation.
Ălicitation
LâĂ©licitation permet Ă un serveur de demander des informations lors du traitement dâun appel dâoutil. Lâapplication dĂ©cide comment prĂ©senter la demande et sâil faut lâaccepter, la refuser ou lâannuler.
let toolset = McpToolset::with_elicitation_handler(
transport,
Arc::new(MyElicitationHandler),
).await?;
ADK-Rust annonce lâĂ©licitation par formulaire et URL. LâĂ©chec ou la panique dâun gestionnaire devient un refus, ce qui prĂ©serve la session MCP. Lâapplication doit nĂ©anmoins valider les valeurs acceptĂ©es et appliquer la politique de consentement.
Consultez examples/mcp_elicitation pour voir un ensemble complet client-serveur.
Tùches négociées
use adk_tool::McpTaskConfig;
use std::time::Duration;
let toolset = McpToolset::new(client).with_task_support(
McpTaskConfig::enabled()
.poll_interval(Duration::from_secs(1))
.timeout(Duration::from_secs(120))
.max_attempts(120),
);
Le mode tùche est sélectionné à partir de deux éléments négociés :
- le serveur annonce
tasks.requests.tools.call; et - lâoutil dĂ©clare la prise en charge des tĂąches comme obligatoire ou facultative.
ADK-Rust envoie les mĂ©tadonnĂ©es de tĂąche avec tools/call, reçoit la tĂąche créée, interroge tasks/get, lit tasks/result et appelle tasks/cancel lorsque les limites locales sont dĂ©passĂ©es. input_required est renvoyĂ© comme une erreur typĂ©e, car un appel dâoutil ADK ordinaire ne fournit pas encore de canal neutre vis-Ă -vis du protocole pour reprendre une tĂąche.
HTTP Streamable distant
use adk_tool::{McpAuth, McpHttpClientBuilder};
use std::time::Duration;
let toolset = McpHttpClientBuilder::new("https://mcp.example.com/mcp")
.with_auth(McpAuth::bearer(std::env::var("MCP_TOKEN")?))
.header("X-Tenant-ID", "tenant-42")
.timeout(Duration::from_secs(30))
.reinit_on_expired_session(true)
.connect()
.await?;
Le gĂ©nĂ©rateur prend en charge les jetons bearer, un en-tĂȘte personnalisĂ© de clĂ© API et les identifiants client fixes OAuth 2.0. Consultez SĂ©curitĂ© et autorisation avant de choisir un flux dâauthentification.