| Crates.io | toolsearch |
| lib.rs | toolsearch |
| version | 0.1.0 |
| created_at | 2025-11-26 01:33:01.877068+00 |
| updated_at | 2025-11-26 01:33:01.877068+00 |
| description | A Rust library for searching tools across MCP servers |
| homepage | |
| repository | https://github.com/yingkitw/toolsearch |
| max_upload_size | |
| id | 1950774 |
| size | 140,510 |
A Rust library for searching tools across multiple MCP (Model Context Protocol) servers.
📖 See ARCHITECTURE.md for detailed architecture documentation
📝 See TODO.md for planned features and improvements
When building agentic AI systems with MCP (Model Context Protocol), developers face a critical challenge:
Too Many Tools: There are hundreds of MCP servers available, each potentially exposing dozens of tools. A typical setup might connect to 10-20 MCP servers, resulting in hundreds or even thousands of available tools.
Context Token Explosion: LLMs need to see all available tools in their context to know what they can call. Including hundreds of tools in the context consumes massive amounts of tokens:
Tool Discovery Failure: When overwhelmed with too many options, LLMs struggle to:
The Missing Piece: While MCP provides excellent tool discovery protocols, there was no Rust library to intelligently search and filter tools before presenting them to the LLM.
toolsearch solves this by:
Result: Instead of sending 500 tools (150K tokens), send only 5-10 relevant tools (1.5K-3K tokens). This dramatically improves:
// 1. Load all your MCP servers
let servers = load_servers("servers.json")?;
// Example: 20 servers, 500+ tools total
// 2. User asks agent to perform a task
let user_query = "Read the configuration file from disk";
// 3. Search for tools relevant to current task
let relevant_tools = simple_search(&servers, "read file disk").await?;
// Returns: 3-5 relevant tools instead of 500
// 4. Send only relevant tools to LLM
// Before: 500 tools × 300 tokens = 150,000 tokens
// After: 5 tools × 300 tokens = 1,500 tokens
// Token savings: 99% reduction!
// 5. LLM can now accurately select the right tool
// Result: Faster, more accurate, and cost-effective!
Scenario: Agentic AI system with 15 MCP servers, 450 total tools
| Metric | Without toolsearch | With toolsearch | Improvement |
|---|---|---|---|
| Tools sent to LLM | 450 | 5-10 | 98% reduction |
| Context tokens | ~135,000 | ~2,000 | 98.5% reduction |
| Tool selection accuracy | ~60% | ~95% | 58% improvement |
| API cost per request | $0.15 | $0.002 | 98.7% cost savings |
| Response time | 3-5 seconds | 0.5-1 second | 80% faster |
While there are tool search solutions in other languages, Rust was missing this capability. This library fills that gap, providing:
rmcp)Add this to your Cargo.toml:
[dependencies]
toolsearch = "0.1.0"
The simplest way to search tools:
use toolsearch::{load_servers, simple_search};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load servers from config file (validates automatically)
let servers = load_servers("servers.json")?;
// Simple search - auto-detects search mode
let results = simple_search(&servers, "read file").await?;
for result in results {
println!("Found tool: {} on server: {}", result.tool_name(), result.server_name);
}
Ok(())
}
use toolsearch::{load_servers, SearchBuilder};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let servers = load_servers("servers.json")?;
let results = SearchBuilder::new(servers)
.query("read,file") // Comma-separated = keyword matching
.limit(10) // Limit to 10 results
.sort_by_tool() // Sort by tool name
.timeout(60) // 60 second timeout
.search()
.await?;
Ok(())
}
Auto-detection features:
^, $, |, *, etc.) → automatically uses regex modeThe CLI is designed to be simple and intuitive. Most options are auto-detected!
# Just search - auto-detects mode based on query
toolsearch search --config servers.json "read file"
Auto-detection:
"read file" → substring search"read,file" → keyword matching (both must be present)"^read|^write" → regex pattern matchingtoolsearch list --config servers.json
Limit results:
toolsearch search --config servers.json --limit 10 "query"
Sort by tool name:
toolsearch search --config servers.json --sort-by-tool "query"
Output formats:
# JSON output
toolsearch search --config servers.json --format json "query"
# Table output (better for many results)
toolsearch search --config servers.json --format table "query"
# Text output (default)
toolsearch search --config servers.json --format text "query"
toolsearch validate --config servers.json
Example output:
✓ Configuration file is valid!
✓ Found 3 server(s)
- file_operations_server
- database_server
- api_integration_server
Create a JSON configuration file (e.g., servers.json):
[
{
"name": "server1",
"transport": {
"type": "stdio",
"command": "mcp-server",
"args": [],
"env": {}
}
},
{
"name": "server2",
"transport": {
"type": "stdio",
"command": "another-mcp-server",
"args": ["--verbose"],
"env": {
"RUST_LOG": "debug"
}
}
}
]
search_tools_with_querySearch for tools matching a query string across multiple servers.
pub async fn search_tools_with_query(
servers: &[ServerConfig],
query: &str,
) -> Result<Vec<ToolSearchMatch>, ToolSearchError>
list_all_toolsList all tools from all configured servers.
pub async fn list_all_tools(
servers: &[ServerConfig],
) -> Result<Vec<ToolSearchMatch>, ToolSearchError>
search_tools_with_optionsAdvanced search with configurable options (timeout, sorting, error handling).
pub async fn search_tools_with_options(
servers: &[ServerConfig],
criteria: &SearchCriteria,
options: &SearchOptions,
) -> Result<Vec<ToolSearchMatch>, ToolSearchError>
list_tools_from_server_with_timeoutList tools from a single server with timeout support.
pub async fn list_tools_from_server_with_timeout(
config: &ServerConfig,
timeout_duration: Option<Duration>,
) -> Result<Vec<Tool>, ToolSearchError>
search_toolsAdvanced search with custom criteria.
pub async fn search_tools(
servers: &[ServerConfig],
criteria: &SearchCriteria,
) -> Result<Vec<ToolSearchMatch>, ToolSearchError>
use toolsearch::{SearchCriteria, SearchMode, SearchFields};
// Simple query search
let criteria = SearchCriteria::with_query("search".to_string());
// Regex pattern matching
let criteria = SearchCriteria::with_regex(r"^read|^write".to_string());
// Keyword matching (all keywords must be present)
let criteria = SearchCriteria::with_keywords(vec!["file".to_string(), "read".to_string()]);
// Word boundary matching
let criteria = SearchCriteria::with_query("read".to_string())
.with_mode(SearchMode::WordBoundary);
// Case-sensitive search
let criteria = SearchCriteria::with_query("Read".to_string())
.case_sensitive(true);
// Search only in names and titles (exclude descriptions)
let criteria = SearchCriteria::with_query("query".to_string())
.with_fields(SearchFields {
name: true,
title: true,
description: false,
input_schema: false,
});
// Search in input schema properties
let criteria = SearchCriteria::with_query("path".to_string())
.with_fields(SearchFields {
name: true,
title: true,
description: true,
input_schema: true, // Enable schema search
});
let criteria = SearchCriteria::with_regex(r"file|directory".to_string())
.with_fields(SearchFields {
name: true,
title: true,
description: true,
input_schema: true,
})
.case_sensitive(false);
use toolsearch::{SearchOptions, SortOrder};
use std::time::Duration;
let options = SearchOptions {
timeout: Some(Duration::from_secs(60)), // 60 second timeout
sort_order: SortOrder::ToolThenServer, // Sort by tool name first
continue_on_error: true, // Continue if a server fails
max_results: Some(100), // Limit to 100 results
};
let results = search_tools_with_options(&servers, &criteria, &options).await?;
for server in &servers {
match server.validate() {
Ok(_) => println!("✓ Server '{}' is valid", server.name),
Err(e) => eprintln!("✗ Server '{}' has errors: {}", server.name, e),
}
}
See the examples/ directory for complete examples:
simple_usage.rs - Start here! Shows the simplest API usagebasic_search.rs - Basic tool search examplelist_all_tools.rs - List all tools exampleadvanced_search.rs - Advanced search with pattern matching, keywords, and field-specific searchessearch_modes.rs - Comparison of different search modesconfig_example.json - Basic configuration file examplecomplex_config.json - Complex configuration with multiple servers and environment variablesRun examples with:
# Start with the simple usage example
cargo run --example simple_usage
# Other examples
cargo run --example basic_search
cargo run --example list_all_tools
cargo run --example advanced_search
cargo run --example search_modes
[
{
"name": "file_operations_server",
"transport": {
"type": "stdio",
"command": "mcp-file-server",
"args": [
"--verbose",
"--log-level=debug",
"--config=/etc/mcp/file-server.json"
],
"env": {
"RUST_LOG": "debug",
"MCP_SERVER_PORT": "8080",
"FILE_CACHE_SIZE": "1000",
"ENABLE_COMPRESSION": "true"
}
}
},
{
"name": "database_server",
"transport": {
"type": "stdio",
"command": "mcp-db-server",
"args": ["--host=localhost", "--port=5432"],
"env": {
"DB_USER": "admin",
"DB_PASSWORD": "secret",
"DB_POOL_SIZE": "10"
}
}
}
]
Run tests with:
cargo test
Before toolsearch:
User: "Read the config file"
Agent: [Receives 500 tools, 150K tokens]
Agent: [Struggles to find right tool]
Agent: [Makes wrong tool call]
Result: ❌ Failure
After toolsearch:
User: "Read the config file"
Agent: [Searches tools → finds 3 relevant tools, 1.5K tokens]
Agent: [Easily identifies correct tool]
Agent: [Makes correct tool call]
Result: ✅ Success
When managing multiple MCP servers:
For LLMs with limited context windows:
graph TB
A[Agentic AI System] --> B[toolsearch]
B --> C[Search Query]
C --> D[Parallel Server Queries]
D --> E[MCP Server 1<br/>50 tools]
D --> F[MCP Server 2<br/>30 tools]
D --> G[MCP Server N<br/>20 tools]
E --> H[Filter & Rank]
F --> H
G --> H
H --> I[Relevant Tools<br/>3-5 tools]
I --> J[LLM Context<br/>1.5K tokens]
K[Without toolsearch] --> L[All Tools<br/>500 tools]
L --> M[LLM Context<br/>150K tokens]
style B fill:#e1f5ff
style H fill:#fff4e1
style I fill:#e8f5e9
style J fill:#c8e6c9
style M fill:#ffcdd2
rmcp (0.8) - MCP protocol implementationtokio - Async runtimeclap - CLI argument parsingserde - Serialization/deserializationApache 2.0
Contributions are welcome! Please feel free to submit a Pull Request.