Crear un cliente MCP
Una aplicación ADK-Rust es un cliente MCP cuando se conecta a un servidor, lee su catálogo publicado y pone capacidades seleccionadas a disposición de un agente o flujo de trabajo.
Instalar
[dependencies]
adk-tool = { version = "2.1.0", features = ["mcp"] }
Para HTTP remoto:
adk-tool = { version = "2.1.0", features = ["mcp", "http-transport"] }
Conexión stdio local
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();
Usa una ruta binaria absoluta en producción. Evita etiquetas de paquetes como latest
en la configuración de despliegue, ya que hacen que las compilaciones y la
recuperación ante incidentes no sean reproducibles.
Descubrimiento y filtrado de herramientas
McpToolset convierte cada herramienta MCP publicada en una ADK-Rust Tool. Conserva
sin cambios los esquemas de entrada y salida del servidor. El proveedor de modelo
seleccionado normaliza una copia del esquema cuando crea su solicitud.
El adaptador también conserva las anotaciones de herramientas MCP. Un readOnlyHint
marca la herramienta ADK como de solo lectura y segura para la concurrencia; un
idempotentHint permite repetirla de forma segura después de volver a conectarse, pero no
hace que la herramienta sea apta por sí misma para el envío paralelo automático.
La ausencia de indicaciones mantiene ambos comportamientos deshabilitados.
Importante: Las anotaciones MCP son indicaciones publicadas por el servidor. Usa los metadatos de repetición y envío automáticos únicamente con servidores dentro del límite de confianza de la aplicación.
let reviewed = McpToolset::new(client).with_filter(|name| {
matches!(name, "read_order" | "read_policy" | "request_replacement")
});
El filtrado controla la visibilidad para el modelo. No sustituye la autorización en el momento de ejecutar la herramienta.
Recursos, indicaciones y finalización
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 finalización de plantillas de recursos usa complete_resource_argument. Un servidor que
no implementa operaciones de listado devuelve una lista vacía cuando responde con
MCP MethodNotFound; los demás errores de protocolo y transporte siguen siendo errores.
Suscripciones a recursos
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 restaura las suscripciones activas después de actualizar la conexión dentro de los límites establecidos.
McpServerManager también conserva las suscripciones durante los reinicios gestionados del proceso.
Los errores y pánicos de los controladores se registran sin terminar la conexión MCP.
Para HTTP transmisible, configura el mismo controlador con
McpHttpClientBuilder::with_resource_notification_handler antes de llamar a
connect_with_elicitation.
Solicitud de información
La solicitud de información permite que un servidor solicite datos mientras gestiona una llamada a una herramienta. La aplicación decide cómo presentar la solicitud y si la acepta, la rechaza o la cancela.
let toolset = McpToolset::with_elicitation_handler(
transport,
Arc::new(MyElicitationHandler),
).await?;
ADK-Rust anuncia la solicitud de información mediante formularios y URL. Un fallo o pánico del controlador se convierte en un rechazo, preservando la sesión MCP. La aplicación aún debe validar los valores aceptados y aplicar la política de consentimiento.
Consulta examples/mcp_elicitation para ver un par completo de cliente y servidor.
Tareas negociadas
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),
);
El modo de tarea se selecciona a partir de dos hechos negociados:
- el servidor anuncia
tasks.requests.tools.call; y - la herramienta declara que la compatibilidad con tareas es obligatoria u opcional.
ADK-Rust envía metadatos de tarea con tools/call, recibe la tarea creada, consulta periódicamente tasks/get, lee tasks/result y llama a tasks/cancel cuando se superan los límites locales. input_required se devuelve como un error tipado porque una llamada ordinaria a la herramienta ADK todavía no proporciona un canal neutral respecto al protocolo para reanudar tareas.
HTTP transmisible remoto
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?;
El creador admite tokens de portador, un encabezado de clave API personalizado y credenciales de cliente fijas de OAuth 2.0. Consulta Seguridad y autorización antes de elegir un flujo de autenticación.