By Alexandro · Updated 2026-11-06
Filter a large tool catalog before a model request
@stackline/tool-router 1.0.5 ranks tool definitions locally. It does not execute tools, authorize requests or prove that a model will choose the right operation. Keep authorization and argument validation at execution time. Keep provider-compatible schemas in the outbound catalog; normalization for retrieval is not conversion of every provider's wire format.
npm install --save-exact @stackline/[email protected]
Start with a small reproducible evaluation
The following executable source builds four MCP-shaped definitions and checks three known-vocabulary queries, plus an unknown query. It records warm in-process route latency over 300 calls. These queries are synthetic and were chosen for this example; the result is not production recall or an independent benchmark.
import assert from 'node:assert/strict';
import {performance} from 'node:perf_hooks';
import {createToolRouter} from '@stackline/tool-router';
const catalog = [
['github_create_issue','Create a GitHub issue for a bug in a repository'],
['drive_search_files','Search documents and files in Google Drive'],
['calendar_list_events','List upcoming meetings in the calendar'],
['weather_forecast','Read the weather forecast for a city']
].map(([name,description])=>({name,description,inputSchema:{type:'object',properties:{query:{type:'string'}}}}));
const queries = [['create a github bug issue','github_create_issue'],['find documents in drive','drive_search_files'],['upcoming calendar meetings','calendar_list_events']];
const router = createToolRouter(catalog);
let hits=0;const latency=[];
for(let i=0;i<300;i++) {
const [query,expected]=queries[i%queries.length];const start=performance.now();
const result=router.route(query,{maxTools:1,fallback:'none'});
latency.push(performance.now()-start);if(result.tools[0]?.name===expected)hits++;
}
latency.sort((a,b)=>a-b);
assert.equal(hits,300);
const absent=router.route('zzqxxvv',{maxTools:1,fallback:'none'});
assert.equal(absent.tools.length,0);
console.log(JSON.stringify({corpus:'4 synthetic tools, 3 known-vocabulary queries, repeated 100 times',node:process.version,recallAt1:hits/300,p50Milliseconds:latency[150],p95Milliseconds:latency[285],unknownQuery:absent.tools.length,estimatedTokens:router.route(queries[0][0],{maxTools:1}).estimatedTokens}));
Expected: all three known queries choose the named tool at rank one and the unknown query returns no tools with fallback: 'none'. Actual timing depends on Node, CPU and vocabulary; the command prints measurements rather than a hard-coded speed claim.
Production evaluation and fallback
Split representative real queries into development and held-out sets. Measure recall at your allowed tool count, misses by tool family, latency percentiles, build time and memory at the actual catalog size. Include ambiguous requests, multilingual vocabulary, missing tools, similarly named read/write operations and schema changes. Rebuild the index when definitions change.
An empty result needs a product decision: ask for clarification, retry with a broader authorized catalog, or stop. Never silently execute the nearest write operation. Pinned tools can exceed a token budget; check the returned budgetExceeded flag before a request. Test fallback with the exact versioned reference, not an assumed option name.
Interpreting token estimates
The library's estimate uses JSON character length divided by four. It is a relative schema-size estimate, not a provider tokenizer or a measured reduction in billing. Compare the selected definitions against the complete catalog with the tokenizer used by your model, and measure answer quality as well as schema size.
The existing benchmark methodology describes a 30-tool evaluation and a separate synthetic 10,000-tool load test. Keep corpus, commit, runtime, CPU and raw output together. Do not extrapolate this four-tool smoke test to those experiments.