Choose which deferred tools (built-in and MCP) are loaded up front instead of waiting behind ToolSearch

A Claude Mod that lets you choose which tools Claude Code defers behind ToolSearch and which it does not. By default many built-in tools and all MCP tools sit behind ToolSearch, so Claude must spend a turn fetching a schema before first use. Tools you list in eager have their schemas in the prompt from the first turn. Tools you list in defer go the other way: their schemas stay out of the prompt until Claude asks for them.
Requires Claude Code v2.1.287 or later.
Install it from the official Anthropic plugin directory. In case you haven't added this marketplace yet, add it first, and refresh it to get the latest listing:
claude plugin marketplace add anthropic-plugin-directory
claude plugin marketplace update anthropic-plugin-directory
claude plugin install delay-no-more@anthropic-plugin-directory
If you prefer using the TUI, inside a session, use /plugin marketplace add, /plugin marketplace update and /plugin install with the same arguments.
To update to a newer release later:
claude plugin marketplace update anthropic-plugin-directory
claude plugin update delay-no-more@anthropic-plugin-directory
To try a local checkout while developing, load it directly instead (its plugin id is then delay-no-more@inline):
claude --plugin-dir /path/to/delay-no-more
Two options, each a list of tool names separated by commas or spaces. * matches any characters. Names that match nothing are ignored.
eager: tools to load up front, taking them out from behind ToolSearch.defer: tools to put behind ToolSearch, so their schemas load only when Claude asks for them. Use it to trim prompt tokens, e.g. a built-in tool you rarely use, or a wildcard eager pattern with a few exceptions.If a tool matches both lists, eager wins. Tools matching neither are left as Claude Code places them. Both empty (the default) changes nothing, and the hook is not registered.
The older name tools still works and is merged with eager. Prefer eager in new configuration.
Set it in your Claude Code settings under pluginConfigs, keyed by the plugin id:
{
"pluginConfigs": {
"delay-no-more@anthropic-plugin-directory": {
"options": {
"eager": "WebSearch, WebFetch, mcp__github__*",
"defer": "mcp__github__create_repository"
}
}
}
}
When loaded locally with --plugin-dir, use delay-no-more@inline as the key instead:
{
"pluginConfigs": {
"delay-no-more@inline": {
"options": {
"eager": "WebSearch, WebFetch, mcp__github__*",
"defer": "mcp__github__create_repository"
}
}
}
}
The change applies to new sessions.
MCP tool names are mcp__<server>__<tool> with the server name as configured (hyphens included), e.g. mcp__persistent-monitor__waitpid.
Every eager tool's schema costs prompt tokens on every request. List the tools you use constantly, not whole servers with dozens of tools. defer is the opposite trade: it saves prompt tokens, but Claude needs an extra ToolSearch turn before first use.
A tool.describe hook moves each deferred tool matching eager into the prompt's tool list (isDeferred: false), and each non-deferred tool matching defer behind ToolSearch (isDeferred: true). Other tools are untouched.
tool.describe hook, written in TypeScript (hooks/register.ts and hooks/patterns.ts), inside Claude Code.The matching logic is in hooks/patterns.ts (parsePatterns, matchesAny), covered by unit tests in tests/patterns.test.ts.
claude plugin validate .
claude plugin testhooks/register.ts 20 lines1import { matchesAny, parsePatterns } from './patterns.ts'
2
3export function register(on: any, options?: Record<string, unknown>) {
4 // `tools` is the legacy name for `eager`; both are honored.
5 const load = [...parsePatterns(options?.eager), ...parsePatterns(options?.tools)]
6 const defer = parsePatterns(options?.defer)
7 if (!load.length && !defer.length) return
8 // The engine asks once per tool, when it first renders the tool's schema. A tool named by
9 // `eager` is moved from behind ToolSearch into the prompt's tool list; one named by `defer`
10 // goes the other way. `eager` wins when both match. Everything else is left as the engine
11 // placed it.
12 on('tool.describe', async ($: any, e: any, next: any) => {
13 const wantLoad = !!e.isDeferred && matchesAny(load, e.tool)
14 const wantDefer = !e.isDeferred && !matchesAny(load, e.tool) && matchesAny(defer, e.tool)
15 if (!wantLoad && !wantDefer) return next(e)
16 // The placement is part of the answer, so override it on the result, not on the event
17 return { ...(await next(e)), isDeferred: wantDefer }
18 })
19}
20hooks/patterns.ts 20 lines1// Parses the `eager` and `defer` options into patterns and matches tool names against them.
2// A pattern is a tool name, or a name with `*` wildcards (`mcp__github__*`).
3// Patterns are separated by commas or whitespace.
4
5export function parsePatterns(value: unknown): RegExp[] {
6 if (typeof value !== 'string') return []
7 return value
8 .split(/[\s,]+/)
9 .filter(Boolean)
10 .map((p) => new RegExp('^' + p.split('*').map(escape).join('.*') + '$'))
11}
12
13export function matchesAny(patterns: readonly RegExp[], tool: string): boolean {
14 return patterns.some((re) => re.test(tool))
15}
16
17function escape(s: string): string {
18 return s.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
19}
20