ToolSearchProcessor
The ToolSearchProcessor is an input processor that enables runtime-defined tool discovery and loading. Instead of providing all tools to the agent upfront, it gives the agent two meta-tools (search_tools and load_tool) that let it find and load tools on demand. This reduces context token usage when working with large tool libraries.
Usage exampleUsage example的直接链接
import { ToolSearchProcessor } from '@mastra/core/processors'
const toolSearch = new ToolSearchProcessor({
tools: {
createIssue: githubTools.createIssue,
sendEmail: emailTools.send,
getWeather: weatherTools.forecast,
// ... many more tools
},
search: {
topK: 5,
minScore: 0.1,
},
})
Constructor parametersConstructor parameters的直接链接
options:
tools:
includeResolvedTools?:
search?:
search.topK?:
search.minScore?:
search.autoLoad?:
storage?:
ttl?:
filter?:
ReturnsReturns的直接链接
id:
name:
processInputStep:
MethodsMethods的直接链接
State inspection (legacy 'in-memory' store)state-inspection-legacy-in-memory-store的直接链接
These methods operate on the default 'in-memory' store only. They're no-ops for the 'context' store, whose state lives in the conversation messages rather than an in-process map.
clearState(threadId)clearstatethreadid的直接链接
Clears the loaded-tool state for a single thread.
processor.clearState('thread-123')
clearAllState()clearallstate的直接链接
Clears loaded-tool state for every thread.
processor.clearAllState()
getStateStats()getstatestats的直接链接
Returns the number of tracked threads and the oldest access time, for debugging in-memory growth.
const { threadCount, oldestAccessTime } = processor.getStateStats()
Returns: { threadCount: number; oldestAccessTime: number | null }
cleanupNow()cleanupnow的直接链接
Immediately runs TTL cleanup instead of waiting for the scheduled sweep.
const cleaned = processor.cleanupNow()
Returns: number: the count of threads cleaned up.
Request-aware filteringRequest-aware filtering的直接链接
Use filter to apply request-specific policy to runtime-defined tools. The hook receives the resolved tool ID as toolName, the tool, request context, and phase. toolName is the ID returned by search_tools, which may differ from the key used in the tools object.
import { ToolSearchProcessor } from '@mastra/core/processors'
const toolSearch = new ToolSearchProcessor({
tools: allTools,
filter: ({ toolName, requestContext, phase }) => {
const plan = requestContext?.get('plan')
if (phase === 'search') {
return true
}
return plan === 'pro' || !toolName.startsWith('premium_')
},
})
The phase value describes where the filter is being applied:
search: Filters results returned bysearch_tools.load: Blocksload_toolfrom loading disallowed tools.active: Hides already-loaded tools from the current request if they're no longer allowed.
If the hook throws or rejects, ToolSearchProcessor treats the tool as disallowed for that request. The hook may run for every matching search candidate, so keep async policy checks cheap or cached. The search_tools meta-tool is always available. load_tool is available unless search.autoLoad is enabled. Tools passed directly through the agent or processInputStep remain available unless you filter them outside ToolSearchProcessor, or enable includeResolvedTools.
Searching request-resolved toolsSearching request-resolved tools的直接链接
The tools option is fixed at construction, so you can't list tools that only exist per request (MCP tools that need the caller's credentials, or anything returned by a dynamic tools function). By default those tools bypass search and occupy prompt space on every turn.
Set includeResolvedTools: true to index them for the request and withhold them from the prompt until the agent loads them:
import { Agent } from '@mastra/core/agent'
import { ToolSearchProcessor } from '@mastra/core/processors'
const toolSearch = new ToolSearchProcessor({
tools: staticTools,
includeResolvedTools: true,
})
const agent = new Agent({
id: 'mcp-agent',
name: 'mcp-agent',
instructions: 'Search for a tool when you need a capability you do not have.',
model: 'openai/gpt-5.6-sol',
// Resolved per request, then searchable alongside staticTools
tools: async ({ requestContext }) => mcpClient.getTools(requestContext.get('userToken')),
inputProcessors: [toolSearch],
})
Each request is indexed on its own, so a tool loaded by one caller never resolves to another caller's instance of the same tool name.
This option applies to every tool resolved for the request, including memory, workspace, skill, and browser tools. Only the search_tools and load_tool meta-tools stay in the prompt, so any tool the agent relies on implicitly must be found through search before it can be called.
Extended usage exampleExtended usage example的直接链接
import { Agent } from '@mastra/core/agent'
import { ToolSearchProcessor } from '@mastra/core/processors'
// Tools from various integrations
import { githubTools } from './tools/github'
import { slackTools } from './tools/slack'
import { dbTools } from './tools/database'
const toolSearch = new ToolSearchProcessor({
tools: {
...githubTools, // createIssue, listPRs, mergePR, ...
...slackTools, // sendMessage, createChannel, ...
...dbTools, // query, insert, update, ...
},
search: {
topK: 5,
minScore: 0.1,
},
})
const agent = new Agent({
id: 'dynamic-tools-agent',
name: 'dynamic-tools-agent',
instructions:
'You are a helpful assistant with access to many tools. Use search_tools to find relevant tools, then load_tool to make them available.',
model: 'openai/gpt-5.6-sol',
inputProcessors: [toolSearch],
})
The agent workflow is:
- Agent receives a user message
- Agent calls
search_toolswith keywords (e.g., "github issue") - Agent reviews results and calls
load_toolwith the tool name - The loaded tool becomes available on the next turn
- Agent uses the loaded tool normally
Single-step discovery with autoLoadsingle-step-discovery-with-autoload的直接链接
Set search.autoLoad to true to skip the separate load step. The tools returned by search_tools are activated immediately, and the load_tool meta-tool isn't exposed. This removes one model turn per discovery, which lowers token usage and latency, and works the same across providers.
const toolSearch = new ToolSearchProcessor({
tools: allTools,
search: {
topK: 3,
autoLoad: true,
},
})
With autoLoad the workflow becomes:
- Agent receives a user message
- Agent calls
search_toolswith keywords - The matching tools are activated automatically and become available on the next turn
- Agent uses the tool normally
Every match is activated, so keep topK small (for example, 3) to avoid adding tools the agent didn't need. Activated tools are appended after existing tools, which keeps the cached prompt prefix stable for providers that support prompt caching.
Loaded-tool storageLoaded-tool storage的直接链接
The storage option controls where the set of loaded tools is tracked. The default is 'in-memory'. The 'context' store is opt-in.
'in-memory' (default)in-memory-default的直接链接
Loaded tools are tracked in an in-memory map per thread, with TTL-based cleanup controlled by the ttl option (default one hour). This is the original behavior:
- Requires no memory configuration.
- State is lost on process restart.
- Requests with no thread ID share a single
'default'entry.
Use clearState, clearAllState, getStateStats, and cleanupNow to inspect or reset this store.
'context'context的直接链接
Loaded state is derived from the conversation messages: a tool is loaded while a search_tools or load_tool result naming it remains in the messages. This mode:
- Works without memory configuration. Without memory, loaded tools are kept for the current process only, and only for requests with a real thread ID; anonymous requests retain nothing between calls unless the caller resends the relevant messages.
- Survives a restart whenever the messages are available again: from memory with message history, or because the caller resends the conversation. Without memory, a restart restores only what's in the messages the caller sends.
- De-loads a tool automatically once that result is no longer present in the messages.
import { ToolSearchProcessor } from '@mastra/core/processors'
const toolSearch = new ToolSearchProcessor({
tools: allTools,
storage: 'context',
})
Loading tools is cache-friendly in both modes: loads are append-only, so the cached prompt prefix stays stable for providers that support prompt caching.
Unloading a tool changes the definitions sent to the model, shifting the cached prefix so the next turn pays for a cache write instead of receiving a cache hit. In 'in-memory' mode, this happens when ttl evicts a thread's state; in 'context' mode, it happens when older-message trimming removes the tool's discovery result. The model must then search for the unloaded tool before reuse. This expected tradeoff exchanges one cache write for a smaller prefix on later turns.
Combining with other processorsCombining with other processors的直接链接
import { Agent } from '@mastra/core/agent'
import { ToolSearchProcessor, TokenLimiter } from '@mastra/core/processors'
const agent = new Agent({
id: 'my-agent',
name: 'my-agent',
model: 'openai/gpt-5.6-sol',
inputProcessors: [
new ToolSearchProcessor({
tools: allTools,
search: { topK: 5 },
}),
// Place TokenLimiter last to ensure context fits
new TokenLimiter(127000),
],
})