MCP ツール
Model Context Protocol (MCP) は、LLM が外部アプリケーション、データソース、およびツールと通信することを可能にするオープンスタンダードです。ADK-Rust は、McpToolset を通じて完全な MCP サポートを提供し、あらゆる MCP 準拠サーバーに接続し、そのツールをエージェントに公開することを可能にします。
概要
MCP はクライアント-サーバーアーキテクチャに従います。
- MCP サーバー は、ツール、リソース、およびプロンプトを公開します
- MCP クライアント (ADK エージェントなど) は、サーバーに接続し、その機能を使用します
MCP 統合の利点:
- ユニバーサルな接続性 - あらゆる MCP 準拠サーバーに接続できます
- 自動検出 - ツールはサーバーから動的に検出されます
- 言語に依存しない - あらゆる言語で書かれたツールを使用できます
- 成長するエコシステム - 既存の何千もの MCP サーバーにアクセスできます
前提条件
MCP サーバーは通常 npm パッケージとして配布されます。以下が必要です:
- Node.js と npm がインストールされていること
- LLM API キー (Gemini、OpenAI など)
クイックスタート
MCP サーバーに接続し、そのツールを使用します:
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"))
}
}
次で実行:
GOOGLE_API_KEY=your_key cargo run --bin basic
McpToolset API
Toolsetの作成
use adk_tool::McpToolset;
// 基本的な作成
let toolset = McpToolset::new(client);
// カスタム名で
let toolset = McpToolset::new(client)
.with_name("filesystem-tools");
ツールのフィルタリング
公開するツールをフィルタリングします:
// 述語関数でフィルタリング
let toolset = McpToolset::new(client)
.with_filter(|name| {
matches!(name, "read_file" | "write_file" | "list_directory")
});
// 正確な名前でフィルタリング(便宜的なメソッド)
let toolset = McpToolset::new(client)
.with_tools(&["echo", "add", "get_time"]);
キャンセル・トークンによるクリーンアップ
MCPサーバーをクリーンにシャットダウンするために、常にキャンセル・トークンを取得してください:
let toolset = McpToolset::new(client);
let cancel_token = toolset.cancellation_token().await;
// ... toolsetを使用 ...
// 終了する前に、MCPサーバーをシャットダウン
cancel_token.cancel();
これにより、EPIPEエラーが防止され、プロセスのクリーンな終了が保証されます。
MCPサーバーへの接続
ローカル・サーバー (Stdio)
標準入出力経由でローカルMCPサーバーに接続します:
use rmcp::{ServiceExt, transport::TokioChildProcess};
use tokio::process::Command;
// 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?;
// ローカル・バイナリ・サーバー
let mut cmd = Command::new("./my-mcp-server");
cmd.arg("--config").arg("config.json");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
リモート・サーバー (SSE)
Server-Sent Events経由でリモートMCPサーバーに接続します:
use rmcp::{ServiceExt, transport::SseClient};
let client = ().serve(
SseClient::new("http://localhost:8080/sse")?
).await?;
ツールの発見
McpToolsetは接続されたサーバーからツールを自動的に発見します:
use adk_core::{ReadonlyContext, Toolset};
// 発見されたツールを取得
let tools = toolset.tools(ctx).await?;
println!("Discovered {} tools:", tools.len());
for tool in &tools {
println!(" - {}: {}", tool.name(), tool.description());
}
発見された各ツールは:
- MCPサーバーからの名前と説明を持ちます
- LLMの正確性のためのパラメーター・スキーマを含みます
- 呼び出されるとMCPプロトコル経由で実行されます
エージェントへのツール追加
エージェントにMCPツールを追加するには2つのパターンがあります:
パターン1: Toolsetとして追加
let toolset = McpToolset::new(client);
let agent = LlmAgentBuilder::new("agent")
.model(model)
.toolset(Arc::new(toolset))
.build()?;
パターン2: 個別のツールを追加
これにより、どのツールが追加されるかをより細かく制御できます:
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()?;
人気のMCPサーバー
統合できる一般的なMCPサーバーをいくつか紹介します:
Everythingサーバー (テスト用)
npx -y @modelcontextprotocol/server-everything
ツール: echo, add, longRunningOperation, sampleLLM, getAlerts, printEnv
ファイルシステム・サーバー
npx -y @modelcontextprotocol/server-filesystem /path/to/directory
ツール: read_file, write_file, list_directory, search_files
GitHubサーバー
npx -y @modelcontextprotocol/server-github
ツール: search_repositories, get_file_contents, create_issue
Slackサーバー
npx -y @modelcontextprotocol/server-slack
ツール: send_message, list_channels, search_messages
メモリー・サーバー
npx -y @modelcontextprotocol/server-memory
ツール: store, retrieve, search
MCP Server Registryでさらに多くのサーバーを見つけることができます。
エラーハンドリング
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);
}
}
一般的なエラー:
- 接続失敗 - サーバーが実行されていないか、アドレスが間違っています
- ツール実行失敗 - MCPサーバーがエラーを返しました
- 無効なパラメーター - ツールが誤った引数を受け取りました
ベストプラクティス
- ツールをフィルターする - エージェントが必要とするツールのみを公開し、混乱を減らします
- キャンセル・トークンを使用する - 終了する前に必ず
cancel()を呼び出し、クリーンアップを行います - エラーを処理する - MCPサーバーは失敗する可能性があります。適切なエラーハンドリングを実装してください
- ローカルサーバーを使用する - 開発用には、stdioトランスポートはリモートよりも簡単です
- サーバーのステータスを確認する - ツールセットを作成する前にMCPサーバーが実行されていることを確認します
完全な例
適切なクリーンアップを含む、完全に動作する例を以下に示します:
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(())
}
応用: カスタム MCP サーバー
独自の MCP サーバーは、rmcp SDK を使用して Rust で作成できます。
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())]))
}
}
完全なサーバー実装の詳細については、rmcp documentation を参照してください。
関連
- Function Tools - Rustでカスタムツールを作成する
- Built-in Tools - ADKに付属する組み込みツール
- LlmAgent - エージェントにツールを追加する
- rmcp SDK - 公式 Rust MCP SDK
- MCP Specification - プロトコル ドキュメント
前へ: ← UI Tools | 次へ: Sessions →