27 releases (1 stable)
Uses new Rust 2024
| 1.0.0 | Jun 7, 2026 |
|---|---|
| 0.9.1 | May 24, 2026 |
| 0.8.4 | May 18, 2026 |
| 0.5.0 | Mar 29, 2026 |
| 0.1.1 | Nov 30, 2025 |
#74 in Asynchronous
2,996 downloads per month
Used in 16 crates
(8 directly)
1MB
19K
SLoC
adk-tool
Tool system for Rust Agent Development Kit (ADK-Rust) agents (FunctionTool, MCP, Google Search).
Overview
adk-tool provides the tool infrastructure for the Rust Agent Development Kit (ADK-Rust):
- FunctionTool - Create tools from async Rust functions
- StatefulTool<S> - Wrap shared state (
Arc<S>) with a tool handler - SimpleToolContext - Lightweight
ToolContextfor non-agent callers (testing, MCP servers) - AgentTool - Use agents as callable tools for composition (runs sub-agents in non-streaming mode for reliable response capture)
- GoogleSearchTool - Web search via Gemini's grounding
- Provider-native wrappers - Typed declarations for Gemini, Anthropic, and OpenAI built-in tools
- McpToolset - Model Context Protocol integration (local & remote servers)
- McpServerManager - Multi-server lifecycle management with health monitoring and auto-restart
- BasicToolset - Group multiple tools together
- FilteredToolset - Filter tools from any toolset by predicate
- MergedToolset - Combine multiple toolsets into one
- PrefixedToolset - Namespace tool names with a prefix
- ExitLoopTool - Control flow for loop agents
- LoadArtifactsTool - Inject binary artifacts into context
- LoadMemoryTool - Agent-callable tool for on-demand memory search (feature:
memory-tools) - PreloadMemoryTool - Auto-loads relevant memories at turn start (feature:
memory-tools)
Installation
[dependencies]
adk-tool = "1.0.0"
# For remote MCP servers via HTTP:
adk-tool = { version = "1.0.0", features = ["http-transport"] }
Or use the meta-crate:
[dependencies]
adk-rust = { version = "1.0.0", features = ["tools"] }
Quick Start
Function Tool
use adk_tool::FunctionTool;
use adk_core::{ToolContext, Result};
use serde_json::{json, Value};
use std::sync::Arc;
async fn get_weather(_ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
let city = args["city"].as_str().unwrap_or("Unknown");
Ok(json!({
"city": city,
"temperature": 72,
"condition": "sunny"
}))
}
let tool = FunctionTool::new(
"get_weather",
"Get current weather for a city",
get_weather,
);
With Parameter Schema (Recommended)
Always add a schema so the LLM knows what parameters to pass:
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(JsonSchema, Serialize, Deserialize)]
struct WeatherParams {
/// The city to get weather for
city: String,
}
let tool = FunctionTool::new("get_weather", "Get weather", get_weather)
.with_parameters_schema::<WeatherParams>();
Tool Metadata
Mark tools as read-only or concurrency-safe for smarter dispatch:
let lookup = FunctionTool::new("lookup", "Look up data", handler)
.with_read_only(true) // safe for concurrent dispatch in Auto mode
.with_concurrency_safe(true);
StatefulTool
Wrap shared state with a tool handler — the Arc<S> is cloned per invocation:
use adk_tool::StatefulTool;
use tokio::sync::RwLock;
struct Counter { count: RwLock<u64> }
let state = Arc::new(Counter { count: RwLock::new(0) });
let tool = StatefulTool::new("increment", "Increment counter", state, |s, _ctx, _args| async move {
let mut count = s.count.write().await;
*count += 1;
Ok(json!({"count": *count}))
});
SimpleToolContext
Call tools outside the agent loop (testing, MCP servers, sub-agent delegation):
use adk_tool::SimpleToolContext;
let ctx = SimpleToolContext::new("my-test-harness");
let result = my_tool.execute(Arc::new(ctx), json!({"key": "value"})).await?;
Defaults: user_id() → "anonymous", session_id() → "", unique UUIDs for invocation and function call IDs.
MCP Server Manager (Multi-Server Lifecycle)
Manage multiple MCP server processes with health monitoring, auto-restart, and tool aggregation:
use adk_tool::mcp::manager::{McpServerManager, McpServerConfig};
use std::collections::HashMap;
use std::time::Duration;
// Load from Kiro mcp.json format
let manager = McpServerManager::from_json(r#"{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["--yes", "@playwright/mcp@latest"],
"autoApprove": ["browser_click"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
}
}"#)?
.with_health_check_interval(Duration::from_secs(30))
.with_grace_period(Duration::from_secs(5));
// Start all non-disabled servers
let results = manager.start_all().await;
// Use as a Toolset — tools from all servers are aggregated
// Name collisions are resolved with {server_id}__{tool_name} prefixes
let agent = LlmAgentBuilder::new("agent")
.model(model)
.toolset(Arc::new(manager))
.build()?;
// Dynamic management at runtime
manager.add_server("github".into(), github_config).await?;
manager.start_server("github").await?;
manager.remove_server("github").await?;
// Graceful shutdown
manager.shutdown().await?;
MCP Tools (Local Server via stdio)
Connect to local MCP servers running as child processes:
use adk_tool::McpToolset;
use rmcp::{ServiceExt, transport::TokioChildProcess};
use tokio::process::Command;
// Connect to a local MCP server
let cmd = Command::new("npx")
.arg("-y")
.arg("@modelcontextprotocol/server-filesystem")
.arg("/path/to/files");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
let toolset = McpToolset::new(client)
.with_name("filesystem-tools")
.with_filter(|name| matches!(name, "read_file" | "write_file"));
// Get cancellation token for graceful shutdown
let cancel_token = toolset.cancellation_token().await;
// ... use toolset with agent ...
// Cleanup before exit
cancel_token.cancel();
MCP Tools (Remote Server via HTTP)
Connect to remote MCP servers using HTTP transport (requires http-transport feature):
use adk_tool::McpHttpClientBuilder;
use std::time::Duration;
// Connect to a public remote MCP server
let toolset = McpHttpClientBuilder::new("https://remote.mcpservers.org/fetch/mcp")
.timeout(Duration::from_secs(30))
.connect()
.await?;
MCP Authentication
Connect to authenticated MCP servers:
use adk_tool::{McpHttpClientBuilder, McpAuth, OAuth2Config};
use std::time::Duration;
// Bearer token (e.g., GitHub Copilot MCP)
let toolset = McpHttpClientBuilder::new("https://api.githubcopilot.com/mcp/")
.with_auth(McpAuth::bearer(std::env::var("GITHUB_TOKEN")?))
.timeout(Duration::from_secs(60))
.connect()
.await?;
// API key in custom header
let toolset = McpHttpClientBuilder::new("https://mcp.example.com/v1")
.with_auth(McpAuth::api_key("X-API-Key", "your-api-key"))
.connect()
.await?;
// OAuth2 client credentials flow
let oauth_config = OAuth2Config::new(
"your-client-id",
"https://auth.example.com/oauth/token"
)
.with_secret("your-client-secret")
.with_scopes(vec!["mcp:read".into(), "mcp:write".into()]);
let toolset = McpHttpClientBuilder::new("https://mcp.example.com/v1")
.with_auth(McpAuth::oauth2(oauth_config))
.connect()
.await?;
MCP Task Support (Long-Running Operations)
Enable async task lifecycle for long-running MCP operations (SEP-1686):
use adk_tool::{McpToolset, McpTaskConfig};
use std::time::Duration;
let toolset = McpToolset::new(client)
.with_task_support(
McpTaskConfig::enabled()
.poll_interval(Duration::from_secs(2))
.timeout(Duration::from_secs(300))
.max_poll_attempts(100)
);
MCP Auto-Reconnect (Connection Resilience)
For long-running agents, use ConnectionRefresher to automatically reconnect when connections fail:
use adk_tool::mcp::{ConnectionRefresher, ConnectionFactory, RefreshConfig};
use rmcp::{RoleClient, ServiceExt, service::RunningService, transport::TokioChildProcess};
use std::sync::Arc;
use tokio::process::Command;
// Define a factory that can create new connections
struct MyConnectionFactory {
command: String,
args: Vec<String>,
}
#[async_trait::async_trait]
impl<S> ConnectionFactory<S> for MyConnectionFactory
where
S: rmcp::service::Service<RoleClient> + Send + Sync + 'static,
{
async fn create_connection(&self) -> Result<RunningService<RoleClient, S>, String> {
let cmd = Command::new(&self.command)
.args(&self.args)
.spawn()
.map_err(|e| e.to_string())?;
().serve(TokioChildProcess::new(cmd).map_err(|e| e.to_string())?)
.await
.map_err(|e| e.to_string())
}
}
// Create initial connection
let cmd = Command::new("npx")
.arg("-y")
.arg("@modelcontextprotocol/server-filesystem")
.arg("/path/to/files");
let client = ().serve(TokioChildProcess::new(cmd)?).await?;
// Wrap with auto-reconnect
let factory = Arc::new(MyConnectionFactory {
command: "npx".to_string(),
args: vec!["-y".into(), "@modelcontextprotocol/server-filesystem".into(), "/path".into()],
});
let refresher = ConnectionRefresher::new(client, factory)
.with_config(RefreshConfig::default()
.with_max_attempts(5)
.with_retry_delay_ms(2000));
// Operations automatically retry on connection failure
let tools = refresher.list_tools().await?;
if tools.reconnected {
println!("Connection was refreshed during operation");
}
The refresher handles these error conditions automatically:
- Connection closed / EOF
- Broken pipe / transport errors
- Session not found (server restart)
- Connection reset
Google Search
use adk_tool::GoogleSearchTool;
let search = GoogleSearchTool::new();
// Add to agent - enables grounded web search
Features
| Feature | Description |
|---|---|
| (default) | Local MCP servers via stdio transport |
http-transport |
Remote MCP servers via streamable HTTP |
MCP Server Examples
Available Public MCP Servers
https://remote.mcpservers.org/fetch/mcp- Web content fetchinghttps://remote.mcpservers.org/sequentialthinking/mcp- Step-by-step reasoning
GitHub Copilot MCP (40+ tools)
// Requires GITHUB_TOKEN with Copilot access
let toolset = McpHttpClientBuilder::new("https://api.githubcopilot.com/mcp/")
.with_auth(McpAuth::bearer(std::env::var("GITHUB_TOKEN")?))
.connect()
.await?;
// Discovered tools include:
// - search_repositories, search_code, search_issues
// - create_pull_request, merge_pull_request
// - get_file_contents, create_or_update_file
// - issue_read, issue_write, add_issue_comment
// - and 30+ more GitHub operations
Toolset Composition
Compose, filter, and namespace toolsets for complex agent configurations:
use adk_tool::{BasicToolset, FilteredToolset, MergedToolset, PrefixedToolset, string_predicate};
use std::sync::Arc;
// Group tools into named toolsets
let weather = Arc::new(BasicToolset::new("weather", vec![get_weather, get_forecast]));
let utils = Arc::new(BasicToolset::new("utils", vec![search, calculate]));
// Filter: expose only specific tools from a toolset
let filtered = FilteredToolset::new(weather.clone(), string_predicate(vec!["get_weather".into()]));
// Or use a custom predicate
let custom = FilteredToolset::with_name(
weather.clone(),
Box::new(|tool| tool.name().starts_with("get_")),
"get_only",
);
// Merge: combine multiple toolsets (first-wins deduplication)
let merged = MergedToolset::new("all_tools", vec![weather.clone(), utils.clone()]);
// Prefix: namespace tool names to avoid collisions
let prefixed = PrefixedToolset::new(weather.clone(), "wx"); // wx_get_weather, wx_get_forecast
// Chain them: prefix → filter → merge
let composed = MergedToolset::new("composed", vec![
Arc::new(PrefixedToolset::new(weather, "wx")) as Arc<dyn Toolset>,
Arc::new(FilteredToolset::new(utils, string_predicate(vec!["search".into()]))),
]);
// Register with an agent
let agent = LlmAgentBuilder::new("agent")
.model(model)
.toolset(Arc::new(composed))
.build()?;
All composition utilities implement Toolset and work with any Toolset implementation including McpToolset and BrowserToolset.
Migration from rmcp 0.9
No changes required! The rmcp 0.14 breaking changes were handled internally:
| What Changed | Impact |
|---|---|
CallToolRequestParam → CallToolRequestParams |
Internal only |
Added meta: None field |
Internal only |
| HTTP transport API | Internal only |
Your existing code using McpToolset::new(client) continues to work unchanged.
Related Crates
- adk-rust - Meta-crate with all components
- adk-core - Core
Tooltrait - adk-agent - Agents that use tools
License
Apache-2.0
Part of ADK-Rust
This crate is part of the ADK-Rust framework for building AI agents in Rust.
Dependencies
~12–39MB
~535K SLoC