Install
$ agentstack add mcp-portkey-ai-mcp-tool-filter ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
@portkey-ai/mcp-tool-filter
Ultra-fast semantic tool filtering for MCP (Model Context Protocol) servers using embedding similarity. Reduce your tool context from 1000+ tools down to the most relevant 10-20 tools in under 10ms.
Features
- ⚡ Lightning Fast: `
Initialize the filter with MCP servers. This precomputes and caches all tool embeddings.
Note: Call this once during startup. It's an async operation that may take a few seconds depending on the number of tools.
await filter.initialize(servers);
filter(input: FilterInput, options?: FilterOptions): Promise
Filter tools based on the input context.
Input Types:
// String input
await filter.filter("Search my emails about the project");
// Chat messages
await filter.filter([
{ role: 'user', content: 'What meetings do I have today?' },
{ role: 'assistant', content: 'Let me check your calendar.' }
]);
Options (all optional, override defaults):
{
topK?: number, // Max tools to return
minScore?: number, // Minimum similarity score (0-1)
contextMessages?: number, // How many recent messages to use
alwaysInclude?: string[], // Tool names to always include
exclude?: string[], // Tool names to exclude
maxContextTokens?: number, // Max context size
}
Returns:
{
tools: ScoredTool[], // Filtered and ranked tools
metrics: {
totalTime: number, // Total time in ms
embeddingTime: number, // Time to embed context
similarityTime: number, // Time to compute similarities
toolsEvaluated: number, // Total tools evaluated
}
}
getStats()
Get statistics about the filter state.
const stats = filter.getStats();
// {
// initialized: true,
// toolCount: 25,
// cacheSize: 5,
// embeddingDimensions: 1536
// }
clearCache()
Clear the context embedding cache.
filter.clearCache();
Performance Optimization
Built-in Optimizations
The library includes several performance optimizations out of the box:
- 🚀 Loop-Unrolled Dot Product - Vector similarity computation is 6-8x faster through CPU pipeline optimization
- 📊 Smart Top-K Selection - Hybrid algorithm uses fast built-in sort for typical workloads, switches to heap-based selection for 500+ tools
- 💾 True LRU Cache - Intelligent cache eviction based on access patterns, not just insertion order
- 🎯 In-Place Operations - Reduced memory allocations through in-place vector normalization
- ⚡ Set-Based Lookups - O(1) exclusion checking instead of O(n) array scanning
These optimizations are automatic and transparent - no configuration needed!
Latency Breakdown
Typical performance for 1000 tools:
Building context: ({
type: 'function',
function: {
name: t.toolName,
description: t.tool.description,
parameters: t.tool.inputSchema,
}
}));
// Make LLM request with filtered tools
const completion = await portkey.chat.completions.create({
model: 'gpt-4',
messages: messages,
tools: openaiTools,
});
With LangChain
import { ChatOpenAI } from 'langchain/chat_models/openai';
import { MCPToolFilter } from '@portkey-ai/mcp-tool-filter';
const filter = new MCPToolFilter({ /* ... */ });
await filter.initialize(mcpServers);
// Create a custom tool selector
async function selectTools(messages) {
const { tools } = await filter.filter(messages);
return tools.map(t => convertToLangChainTool(t));
}
// Use in your agent
const model = new ChatOpenAI();
const tools = await selectTools(messages);
const response = await model.invoke(messages, { tools });
Caching Strategy
// Recommended: Initialize once at startup
let filterInstance: MCPToolFilter;
async function getFilter() {
if (!filterInstance) {
filterInstance = new MCPToolFilter({ /* ... */ });
await filterInstance.initialize(mcpServers);
}
return filterInstance;
}
// Use in request handlers
app.post('/chat', async (req, res) => {
const filter = await getFilter();
const result = await filter.filter(req.body.messages);
// ... use filtered tools
});
Benchmarks
Performance on various tool counts (M1 Max):
Local Embeddings (Xenova/all-MiniLM-L6-v2):
| Tools | Initialization | Filter (Cold) | Filter (Cached) | |-------|---------------|---------------|-----------------| | 10 | ~100ms | 2ms | 5000) { logger.warn('Slow filter request', result.metrics); }
## Advanced Usage
### Two-Stage Filtering
For very large tool sets, use hierarchical filtering:
```typescript
// Stage 1: Filter by server categories
const relevantServers = mcpServers.filter(server =>
server.categories?.some(cat => userIntent.includes(cat))
);
// Stage 2: Filter tools within relevant servers
const result = await filter.filter(messages);
Custom Scoring
Combine embedding similarity with keyword matching:
const { tools } = await filter.filter(input);
// Boost tools with exact keyword matches
const boostedTools = tools.map(tool => {
const hasKeywordMatch = tool.tool.keywords?.some(kw =>
input.toLowerCase().includes(kw.toLowerCase())
);
return {
...tool,
score: hasKeywordMatch ? tool.score * 1.2 : tool.score
};
}).sort((a, b) => b.score - a.score);
Always-Include Power Tools
Always include certain essential tools:
const filter = new MCPToolFilter({
// ...
defaultOptions: {
alwaysInclude: [
'web_search', // Always useful
'conversation_search', // Access to context
],
}
});
Troubleshooting
Slow First Request
Problem: First filter call is slow.
Solution: The embedding API call takes 3-5ms. Subsequent calls with similar context are cached and much faster.
// Warm up the cache
await filter.filter("hello"); // ~5ms
await filter.filter("hello"); // ~1ms (cached)
Poor Tool Selection
Problem: Wrong tools are being selected.
Solutions:
- Improve tool descriptions with more keywords and use cases
- Lower the
minScorethreshold - Increase
topKto include more tools - Add important tools to
alwaysInclude
Memory Usage
Problem: High memory usage with many tools.
Solution: Use smaller embedding dimensions:
embedding: {
dimensions: 512 // Instead of 1536
}
This reduces memory by ~66% with minimal accuracy loss.
License
MIT
Contributing
Contributions welcome! Please open an issue or PR.
Support
- GitHub Issues: github.com/portkey-ai/mcp-tool-filter
- Email: support@portkey.ai
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Portkey-AI
- Source: Portkey-AI/mcp-tool-filter
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.