Outils MCP
Model Context Protocol (MCP) est un standard ouvert qui permet aux LLMs de communiquer avec des applications externes, des sources de données et des outils. ADK-Rust fournit un support MCP complet via le McpToolset, vous permettant de vous connecter à n'importe quel serveur compatible MCP et d'exposer ses outils à vos agents.
Aperçu
MCP suit une architecture client-serveur :
- MCP Servers exposent des outils, des ressources et des invites
- MCP Clients (comme les agents ADK) se connectent aux serveurs et utilisent leurs capacités
Avantages de l'intégration de MCP :
- Connectivité universelle - Connectez-vous à n'importe quel serveur compatible MCP
- Découverte automatique - Les outils sont découverts dynamiquement à partir du serveur
- Indépendance linguistique - Utilisez des outils écrits dans n'importe quel langage
- Écosystème en croissance - Accédez à des milliers de serveurs MCP existants
Prérequis
Les serveurs MCP sont généralement distribués sous forme de packages npm. Vous aurez besoin de :
- Node.js et npm installés
- Une LLM API key (Gemini, OpenAI, etc.)
Démarrage rapide
Connectez-vous à un serveur MCP et utilisez ses outils :
use adk_agent::LlmAgentBuilder;
use adk_core::{Content, Part, ReadonlyContext, Toolset};
use adk_model::GeminiModel;
use adk_tool::McpToolset;
use rmcp::{ServiceExt, transport::TokioChildProcess};
use tokio::process::Command;
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenvy::dotenv().ok();
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.0-flash")?);
// 1. Démarrer le serveur MCP et se connecter
let mut cmd = Command::new("npx");
cmd.arg("-y").arg("@modelcontextprotocol/server-everything");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
// 2. Créer un toolset à partir du client
let toolset = McpToolset::new(client)
.with_tools(&["echo", "add"]); // N'exposez que ces outils
// 3. Obtenir le jeton d'annulation pour le nettoyage
let cancel_token = toolset.cancellation_token().await;
// 4. Découvrir les outils et les ajouter à l'agent
let ctx: Arc<dyn ReadonlyContext> = Arc::new(SimpleContext);
let tools = toolset.tools(ctx).await?;
let mut builder = LlmAgentBuilder::new("mcp_agent")
.model(model)
.instruction("Vous disposez d'outils MCP. Utilisez 'echo' pour répéter les messages, 'add' pour additionner les nombres.");
for tool in tools {
builder = builder.tool(tool);
}
let agent = builder.build()?;
// 5. Exécuter la console interactive
adk_cli::console::run_console(
Arc::new(agent),
"mcp_demo".to_string(),
"user".to_string(),
).await?;
// 6. Nettoyage : arrêter le serveur MCP
cancel_token.cancel();
Ok(())
}
// Contexte minimal pour la découverte d'outils
struct SimpleContext;
#[async_trait::async_trait]
impl ReadonlyContext for SimpleContext {
fn invocation_id(&self) -> &str { "init" }
fn agent_name(&self) -> &str { "init" }
fn user_id(&self) -> &str { "user" }
fn app_name(&self) -> &str { "mcp" }
fn session_id(&self) -> &str { "init" }
fn branch(&self) -> &str { "main" }
fn user_content(&self) -> &Content {
static CONTENT: std::sync::OnceLock<Content> = std::sync::OnceLock::new();
CONTENT.get_or_init(|| Content::new("user").with_text("init"))
}
}
Exécutez avec :
GOOGLE_API_KEY=your_key cargo run --bin basic
API McpToolset
Création d'un Toolset
use adk_tool::McpToolset;
// Création de base
let toolset = McpToolset::new(client);
// Avec un nom personnalisé
let toolset = McpToolset::new(client)
.with_name("filesystem-tools");
Filtrage des Tool
Filtrez les Tool à exposer :
// Filtrer par fonction de prédicat
let toolset = McpToolset::new(client)
.with_filter(|name| {
matches!(name, "read_file" | "write_file" | "list_directory")
});
// Filtrer par noms exacts (méthode de commodité)
let toolset = McpToolset::new(client)
.with_tools(&["echo", "add", "get_time"]);
Nettoyage avec un Jeton d'Annulation
Obtenez toujours un jeton d'annulation pour arrêter proprement le serveur MCP :
let toolset = McpToolset::new(client);
let cancel_token = toolset.cancellation_token().await;
// ... utiliser le toolset ...
// Avant de quitter, arrêter le serveur MCP
cancel_token.cancel();
Ceci prévient les erreurs EPIPE et assure une terminaison propre du processus.
Connexion aux Serveurs MCP
Serveurs Locaux (Stdio)
Connectez-vous à un serveur MCP local via l'entrée/sortie standard :
use rmcp::{ServiceExt, transport::TokioChildProcess};
use tokio::process::Command;
// Serveur de package NPM
let mut cmd = Command::new("npx");
cmd.arg("-y")
.arg("@modelcontextprotocol/server-filesystem")
.arg("/path/to/allowed/directory");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
// Serveur binaire local
let mut cmd = Command::new("./my-mcp-server");
cmd.arg("--config").arg("config.json");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
Serveurs Distants (SSE)
Connectez-vous à un serveur MCP distant via Server-Sent Events :
use rmcp::{ServiceExt, transport::SseClient};
let client = ().serve(
SseClient::new("http://localhost:8080/sse")?
).await?;
Découverte de Tool
Le McpToolset découvre automatiquement les Tool du serveur connecté :
use adk_core::{ReadonlyContext, Toolset};
// Obtenir les Tool découverts
let tools = toolset.tools(ctx).await?;
println!("Discovered {} tools:", tools.len());
for tool in &tools {
println!(" - {}: {}", tool.name(), tool.description());
}
Chaque Tool découvert :
- Possède son nom et sa description du serveur MCP
- Inclut des schémas de paramètres pour la précision des LlmAgent
- S'exécute via le protocole MCP lorsqu'il est appelé
Ajout de Tool à l'Agent
Il existe deux modèles pour ajouter des Tool MCP à un Agent :
Modèle 1 : Ajouter en tant que Toolset
let toolset = McpToolset::new(client);
let agent = LlmAgentBuilder::new("agent")
.model(model)
.toolset(Arc::new(toolset))
.build()?;
Modèle 2 : Ajouter des Tool Individuels
Cela vous donne plus de contrôle sur les Tool ajoutés :
let toolset = McpToolset::new(client)
.with_tools(&["echo", "add"]);
let tools = toolset.tools(ctx).await?;
let mut builder = LlmAgentBuilder::new("agent")
.model(model);
for tool in tools {
builder = builder.tool(tool);
}
let agent = builder.build()?;
Serveurs MCP Populaires
Voici quelques serveurs MCP couramment utilisés que vous pouvez intégrer :
Everything Server (Tests)
npx -y @modelcontextprotocol/server-everything
Tools: echo, add, longRunningOperation, sampleLLM, getAlerts, printEnv
Filesystem Server
npx -y @modelcontextprotocol/server-filesystem /path/to/directory
Tools: read_file, write_file, list_directory, search_files
GitHub Server
npx -y @modelcontextprotocol/server-github
Tools: search_repositories, get_file_contents, create_issue
Slack Server
npx -y @modelcontextprotocol/server-slack
Tools: send_message, list_channels, search_messages
Memory Server
npx -y @modelcontextprotocol/server-memory
Tools: store, retrieve, search
Trouvez plus de serveurs dans le MCP Server Registry.
Gestion des erreurs
Gérez les erreurs de connexion et d'exécution du MCP :
use adk_core::AdkError;
match toolset.tools(ctx).await {
Ok(tools) => {
println!("Discovered {} tools", tools.len());
}
Err(AdkError::Tool(msg)) => {
eprintln!("MCP error: {}", msg);
}
Err(e) => {
eprintln!("Other error: {}", e);
}
}
Erreurs courantes :
- Échec de la connexion - Le serveur ne fonctionne pas ou l'adresse est incorrecte
- Échec de l'exécution de l'outil - Le serveur MCP a renvoyé une erreur
- Paramètres invalides - L'outil a reçu des arguments incorrects
Bonnes pratiques
- Filtrer les outils - N'exposez que les outils dont l'Agent a besoin pour réduire la confusion
- Utiliser des jetons d'annulation - Appelez toujours
cancel()avant de quitter pour le nettoyage - Gérer les erreurs - Les serveurs MCP peuvent échouer ; implémentez une gestion appropriée des erreurs
- Utiliser des serveurs locaux - Pour le développement, le transport stdio est plus simple que le transport distant
- Vérifier le statut du serveur - Vérifiez que le serveur MCP est en cours d'exécution avant de créer un toolset
Exemple complet
Voici un exemple complet et fonctionnel avec un nettoyage approprié :
use adk_agent::LlmAgentBuilder;
use adk_core::{Content, Part, ReadonlyContext, Toolset};
use adk_model::GeminiModel;
use adk_tool::McpToolset;
use rmcp::{ServiceExt, transport::TokioChildProcess};
use std::sync::Arc;
use tokio::process::Command;
struct SimpleContext;
#[async_trait::async_trait]
impl ReadonlyContext for SimpleContext {
fn invocation_id(&self) -> &str { "init" }
fn agent_name(&self) -> &str { "init" }
fn user_id(&self) -> &str { "user" }
fn app_name(&self) -> &str { "mcp" }
fn session_id(&self) -> &str { "init" }
fn branch(&self) -> &str { "main" }
fn user_content(&self) -> &Content {
static CONTENT: std::sync::OnceLock<Content> = std::sync::OnceLock::new();
CONTENT.get_or_init(|| Content::new("user").with_text("init"))
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenvy::dotenv().ok();
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.0-flash")?);
println!("Starting MCP server...");
let mut cmd = Command::new("npx");
cmd.arg("-y").arg("@modelcontextprotocol/server-everything");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
println!("MCP server connected!");
// Créer un toolset filtré
let toolset = McpToolset::new(client)
.with_name("everything-tools")
.with_filter(|name| matches!(name, "echo" | "add" | "printEnv"));
// Obtenir le jeton d'annulation pour le nettoyage
let cancel_token = toolset.cancellation_token().await;
// Découvrir les outils
let ctx = Arc::new(SimpleContext) as Arc<dyn ReadonlyContext>;
let tools = toolset.tools(ctx).await?;
println!("Discovered {} tools:", tools.len());
for tool in &tools {
println!(" - {}: {}", tool.name(), tool.description());
}
// Construire l'Agent avec les outils
let mut builder = LlmAgentBuilder::new("mcp_demo")
.model(model)
.instruction(
"You have access to MCP tools:\n\
- echo: Repeat a message back\n\
- add: Add two numbers (a + b)\n\
- printEnv: Print environment variables"
);
for tool in tools {
builder = builder.tool(tool);
}
let agent = builder.build()?;
// Exécuter la console interactive
let result = adk_cli::console::run_console(
Arc::new(agent),
"mcp_demo".to_string(),
"user".to_string(),
).await;
// Nettoyage
println!("\nShutting down MCP server...");
cancel_token.cancel();
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
result?;
Ok(())
}
Avancé : Serveur MCP Personnalisé
Vous pouvez créer votre propre serveur MCP en Rust en utilisant le rmcp SDK :
use rmcp::{tool, tool_router, handler::server::tool::ToolRouter, model::*};
#[derive(Clone)]
pub struct MyServer {
tool_router: ToolRouter<Self>,
}
#[tool_router]
impl MyServer {
fn new() -> Self {
Self { tool_router: Self::tool_router() }
}
#[tool(description = "Add two numbers")]
async fn add(&self, a: i32, b: i32) -> Result<CallToolResult, ErrorData> {
Ok(CallToolResult::success(vec![Content::text((a + b).to_string())]))
}
#[tool(description = "Multiply two numbers")]
async fn multiply(&self, a: i32, b: i32) -> Result<CallToolResult, ErrorData> {
Ok(CallToolResult::success(vec![Content::text((a * b).to_string())]))
}
}
Consultez la documentation rmcp pour les détails complets de l'implémentation du serveur.
Connexe
- Function Tools - Création d'outils personnalisés en Rust
- Built-in Tools - Outils pré-intégrés inclus avec ADK
- LlmAgent - Ajout d'outils aux agents
- rmcp SDK - SDK MCP Rust Officiel
- Spécification MCP - Documentation du protocole
Précédent : ← UI Tools | Suivant : Sessions →