MCP-Tools
Model Context Protocol (MCP) ist ein offener Standard, der LLMs die Kommunikation mit externen Anwendungen, Datenquellen und Tools ermöglicht. ADK-Rust bietet volle MCP-Unterstützung durch das McpToolset, wodurch Sie sich mit jedem MCP-kompatiblen Server verbinden und dessen Tools Ihren Agents zugänglich machen können.
Übersicht
MCP folgt einer Client-Server-Architektur:
- MCP-Server stellen Tools, Ressourcen und Prompts bereit
- MCP-Clients (wie ADK-Agents) verbinden sich mit Servern und nutzen deren Fähigkeiten
Vorteile der MCP-Integration:
- Universelle Konnektivität - Verbinden Sie sich mit jedem MCP-kompatiblen Server
- Automatische Erkennung - Tools werden dynamisch vom Server erkannt
- Sprachunabhängig - Verwenden Sie Tools, die in jeder Sprache geschrieben sind
- Wachsendes Ökosystem - Greifen Sie auf Tausende bestehender MCP-Server zu
Voraussetzungen
MCP-Server werden typischerweise als npm-Pakete verteilt. Sie benötigen:
- Node.js und npm installiert
- Einen LLM API-Schlüssel (Gemini, OpenAI, etc.)
Schnellstart
Verbinden Sie sich mit einem MCP-Server und nutzen Sie dessen Tools:
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. Start MCP server and connect
let mut cmd = Command::new("npx");
cmd.arg("-y").arg("@modelcontextprotocol/server-everything");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
// 2. Create toolset from the client
let toolset = McpToolset::new(client)
.with_tools(&["echo", "add"]); // Only expose these tools
// 3. Get cancellation token for cleanup
let cancel_token = toolset.cancellation_token().await;
// 4. Discover tools and add to 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("You have MCP tools. Use 'echo' to repeat messages, 'add' to sum numbers.");
for tool in tools {
builder = builder.tool(tool);
}
let agent = builder.build()?;
// 5. Run interactive console
adk_cli::console::run_console(
Arc::new(agent),
"mcp_demo".to_string(),
"user".to_string(),
).await?;
// 6. Cleanup: shutdown MCP server
cancel_token.cancel();
Ok(())
}
// Minimal context for tool discovery
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"))
}
}
Run with:
GOOGLE_API_KEY=your_key cargo run --bin basic
McpToolset API
Ein Toolset erstellen
use adk_tool::McpToolset;
// Basic creation
let toolset = McpToolset::new(client);
// With custom name
let toolset = McpToolset::new(client)
.with_name("filesystem-tools");
Tool-Filterung
Filtern Sie, welche Tools verfügbar gemacht werden sollen:
// Filter by predicate function
let toolset = McpToolset::new(client)
.with_filter(|name| {
matches!(name, "read_file" | "write_file" | "list_directory")
});
// Filter by exact names (convenience method)
let toolset = McpToolset::new(client)
.with_tools(&["echo", "add", "get_time"]);
Bereinigung mit Cancellation Token
Holen Sie sich immer einen Cancellation Token, um den MCP-Server sauber herunterzufahren:
let toolset = McpToolset::new(client);
let cancel_token = toolset.cancellation_token().await;
// ... use the toolset ...
// Before exiting, shutdown the MCP server
cancel_token.cancel();
Dies verhindert EPIPE errors und gewährleistet eine saubere Prozessbeendigung.
Verbindung zu MCP-Servern herstellen
Lokale Server (Stdio)
Verbindung zu einem lokalen MCP-Server über Standard-Eingabe/Ausgabe herstellen:
use rmcp::{ServiceExt, transport::TokioChildProcess};
use tokio::process::Command;
// NPM package server
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?;
// Local binary server
let mut cmd = Command::new("./my-mcp-server");
cmd.arg("--config").arg("config.json");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
Remote-Server (SSE)
Verbindung zu einem Remote-MCP-Server über Server-Sent Events herstellen:
use rmcp::{ServiceExt, transport::SseClient};
let client = ().serve(
SseClient::new("http://localhost:8080/sse")?
).await?;
Tool-Erkennung
Das McpToolset erkennt automatisch Tools vom verbundenen Server:
use adk_core::{ReadonlyContext, Toolset};
// Get discovered tools
let tools = toolset.tools(ctx).await?;
println!("Discovered {} tools:", tools.len());
for tool in &tools {
println!(" - {}: {}", tool.name(), tool.description());
}
Jedes erkannte Tool:
- Hat seinen name und seine description vom MCP-Server
- Enthält Parameterschemata für die LLM-Genauigkeit
- Wird über das MCP-Protokoll ausgeführt, wenn es aufgerufen wird
Hinzufügen von Tools zu Agent
Es gibt zwei Muster, um MCP-Tools zu einem Agent hinzuzufügen:
Muster 1: Als Toolset hinzufügen
let toolset = McpToolset::new(client);
let agent = LlmAgentBuilder::new("agent")
.model(model)
.toolset(Arc::new(toolset))
.build()?;
Muster 2: Einzelne Tools hinzufügen
Dies gibt Ihnen mehr Kontrolle darüber, welche Tools hinzugefügt werden:
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()?;
Beliebte MCP-Server
Hier sind einige häufig verwendete MCP-Server, die Sie integrieren können:
Everything Server (Testen)
npx -y @modelcontextprotocol/server-everything
Tools: echo, add, longRunningOperation, sampleLLM, getAlerts, printEnv
Dateisystem-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
Speicher-Server
npx -y @modelcontextprotocol/server-memory
Tools: store, retrieve, search
Weitere Server finden Sie im MCP Server Registry.
Fehlerbehandlung
Behandeln Sie MCP Verbindungs- und Ausführungsfehler:
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);
}
}
Häufige Fehler:
- Verbindung fehlgeschlagen - Server läuft nicht oder falsche Adresse
- Tool-Ausführung fehlgeschlagen - MCP Server hat einen Fehler zurückgegeben
- Ungültige Parameter - Tool hat inkorrekte Argumente erhalten
Bewährte Praktiken
- Tools filtern - Nur die Tools bereitstellen, die der Agent benötigt, um Verwirrung zu vermeiden
- Abbruch-Tokens verwenden - Immer
cancel()aufrufen, bevor Sie beenden, um aufzuräumen - Fehler behandeln - MCP Server können fehlschlagen; implementieren Sie eine angemessene Fehlerbehandlung
- Lokale Server verwenden - Für die Entwicklung ist stdio Transport einfacher als Remote
- Serverstatus prüfen - Verifizieren Sie, dass der MCP Server läuft, bevor Sie das Toolset erstellen
Vollständiges Beispiel
Hier ist ein vollständig funktionierendes Beispiel mit ordnungsgemäßer Bereinigung:
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!");
// Create filtered toolset
let toolset = McpToolset::new(client)
.with_name("everything-tools")
.with_filter(|name| matches!(name, "echo" | "add" | "printEnv"));
// Get cancellation token for cleanup
let cancel_token = toolset.cancellation_token().await;
// Discover tools
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());
}
// Build agent with tools
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()?;
// Run interactive console
let result = adk_cli::console::run_console(
Arc::new(agent),
"mcp_demo".to_string(),
"user".to_string(),
).await;
// Cleanup
println!("\nShutting down MCP server...");
cancel_token.cancel();
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
result?;
Ok(())
}
Fortgeschritten: Benutzerdefinierter MCP-Server
Sie können Ihren eigenen MCP-Server in Rust mit dem rmcp SDK erstellen:
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())]))
}
}
Siehe die rmcp documentation für vollständige Details zur Serverimplementierung.
Verwandt
- Function Tools - Erstellen benutzerdefinierter Tools in Rust
- Built-in Tools - Vorgefertigte Tools, die im ADK enthalten sind
- LlmAgent - Hinzufügen von Tools zu Agents
- rmcp SDK - Offizielles Rust MCP SDK
- MCP Specification - Protokolldokumentation
Zurück: ← UI Tools | Weiter: Sessions →