Tool Deferred Loading Override (Defer / NoDefer) β
CodeBuddy Code supports using Defer(...) / NoDefer(...) modifiers to temporarily change a tool's deferred loading status when specifying the available tool list, without modifying the global configuration. This document is the complete reference for this capability.
Background: By default, whether a tool uses deferred loading is determined by the product defaults or the MCP configuration's
defer_loadingfield (see MCP docs Β§Deferred Loading). These are globally applied default settings. This feature allows you to temporarily adjust the behavior for a single session or custom agent without changing the defaults.
1. Syntax Overview β
In any "tool list" field that accepts tool names, add a modifier before the tool name:
| Syntax | Meaning |
|---|---|
Read | Regular tool, deferred status determined by global defaults |
Defer(Glob) | Force Glob to use deferred loading in this session/agent (not directly in the model's tool list; discoverable via ToolSearch) |
NoDefer(Bash) | Force Bash to not use deferred loading in this session/agent (directly in the model's tool list) |
Defer(mcp__github__*) | Wildcard match, set an entire group of MCP tools to deferred loading |
Defer(*) | Set all tools in the current list to deferred loading (extreme trimming) |
* is the only supported wildcard character, matching any character sequence. Other characters (?, [], etc.) are treated as literals.
2. Available Channels β
Modifiers can be used in the following three types of "tool list" fields:
2.1 CLI --tools Parameter β
bash
codebuddy --tools "Read,Defer(Glob),NoDefer(Bash)"The tools field in ACP and SDK clients is equivalent to the CLI.
2.2 Custom Agent Frontmatter β
In the YAML frontmatter of .codebuddy/agents/*.md:
yaml
---
name: code-reader
description: Read-only code exploration agent
tools:
- Read
- Grep
- Defer(Glob) # Make Glob use deferred loading; discover via ToolSearch when needed
- NoDefer(Bash) # Even if Bash defaults to deferred loading, force it to be non-deferred in this agent
---See Sub-Agents Β§Configuration Fields for details.
3. Where Modifiers Cannot Be Used β
Modifiers only apply to "tool list" fields. The following "permission rule" fields do not accept modifiers:
--allowed-tools/settings.permissions.allow--disallowed-tools/settings.permissions.denymatcherin hooks configuration
bash
# β Wrong: Will immediately throw an error
codebuddy --allowed-tools "Defer(Glob)"
# Error: Defer(...) / NoDefer(...) modifiers belong in --tools, not in permission rule fields.
# β
Correct: Separate them
codebuddy --tools "...,Defer(Glob)" --allowed-tools "Glob(src/**)"Rationale: "Exposure strategy" (whether to use deferred loading) and "behavior constraints" (parameter filtering) are two orthogonal concerns β expressing them separately is clearer.
4. Priority and Merging β
The final decision follows this priority order (highest to lowest):
| Priority | Source | Description |
|---|---|---|
| 0a | Any layer NoDefer(X) matches | Force non-defer (regardless of what other sources say) |
| 0b | Any layer Defer(X) matches | Force defer |
| 1 | MCP tool-level tools[name].defer_loading | MCP static configuration |
| 2 | MCP server-level defer_loading | MCP static configuration |
| 3 | Environment variable CODEBUDDY_DEFER_TOOL_LOADING | Global switch |
| 4 | User setting settings.deferToolLoading | Global switch |
| 5 | Built-in default (CodeBuddy Code factory configuration) | Fallback |
Key Rules β
NoDeferalways wins overDefer: Even if a custom agent specifiesDefer(X), addingNoDefer(X)in the CLI--toolsmeans X will not be deferred in the current session β the user's runtime declaration takes priority.- CLI and agent configuration participate equally: Modifiers from both sources take effect as a union, with no precedence between them; the final result is determined by the "
NoDeferwins" rule. - Duplicate names: When the same tool appears multiple times in the same field, the last occurrence's modifier wins (consistent with the semantics of
--allowedToolsduplicates).
5. Auto-Append: ToolSearch and DeferExecuteTool β
Whenever the tool list contains at least one Defer(...), CodeBuddy Code automatically adds the following two tools (if they're not already in the list):
| Tool | Purpose |
|---|---|
ToolSearch | Lets the model search for and discover deferred tools |
DeferExecuteTool | Lets the model actually call deferred tools |
This means you can write:
bash
# Equivalent to codebuddy --tools "Bash,Read,Defer(Edit),ToolSearch,DeferExecuteTool"
codebuddy --tools "Bash,Read,Defer(Edit)"Why Auto-Append β
Deferred tools don't enter the model's tool list directly β the model must discover them via ToolSearch and call them via DeferExecuteTool. Without either one, Defer(...) effectively becomes "the model can never use this tool." Auto-append makes the most common usage pattern intuitive.
Wildcard Guard: Defer(*) Won't Swallow Itself β
When writing Defer(*), theoretically "all tools" would be deferred β including ToolSearch and DeferExecuteTool themselves, which would break the defer workflow. To prevent this "self-lockout," auto-append also adds a NoDefer guard for these two tools:
bash
codebuddy --tools "Defer(*)"
# Behavior equivalent to:
# codebuddy --tools "*,ToolSearch,DeferExecuteTool" + NoDefer(ToolSearch),NoDefer(DeferExecuteTool)The model can still see and use ToolSearch / DeferExecuteTool, enabling it to search for and call other deferred tools.
Cases That Don't Trigger Auto-Append β
- The tool list only has
NoDefer(...)without anyDefer(...): The user's intent is to pull tools back to direct invocation, unrelated to the defer workflow - The tool list has no modifiers at all: Original behavior is preserved
- The user has explicitly listed
ToolSearchorDeferExecuteTool: Idempotent, no duplicate addition
6. Typical Use Cases β
6.1 Temporarily Defer Tools to Save Tokens β
bash
# Know you only need Read/Edit this time; temporarily tuck away other large tools
codebuddy --tools "Read,Edit,Defer(Bash),Defer(Glob),Defer(Grep)"The model's context only shows Read, Edit, and ToolSearch; other tools can be retrieved via ToolSearch when needed.
6.2 Temporarily Force a Deferred Tool to Be Directly Available β
bash
# Suppose Bash is configured as deferred by default, but you need it directly available this time
codebuddy --tools "default,NoDefer(Bash)"Note:
defaultmeans "all built-in tools" and can be used alongside modifiers.
6.3 Fine-Grained Exposure Strategy for Custom Agents β
yaml
---
name: explorer
description: Agent specialized for large repo exploration; tuck search tools away to reduce noise
tools:
- Read
- Edit
- ToolSearch
- Defer(Glob)
- Defer(Grep)
- Defer(LSP)
---6.4 Expose MCP Tool Groups On-Demand β
bash
codebuddy --tools "default,Defer(mcp__github__*)"Defers all tools from the GitHub MCP server; the model can find them via ToolSearch by searching keywords like github pr, github issue, etc.
7. Troubleshooting β
7.1 Syntax Error β
text
Error: Invalid --tools value: Invalid tool spec "Defer(Read(*.md))": Defer(...) only accepts a tool name or glob; permission filters like Read(*.md) belong in --allowed-tools, not --tools.β Move the parameter filter to --allowed-tools:
bash
codebuddy --tools "Defer(Read)" --allowed-tools "Read(*.md)"7.2 Modifier in the Wrong Field β
text
Error: Invalid permission rule "Defer(Glob)" in --allowed-tools / settings.permissions.allow: Defer(...) / NoDefer(...) modifiers belong in --tools, not in permission rule fields.β See Β§3.
7.3 Nesting / Empty Content β
Defer(NoDefer(X)): Nested modifiers are rejected β semantically meaninglessDefer(): Empty content is rejecteddefer(Read): Lowercase is not recognized β treated as a literal "tool name with parentheses" and rejected
7.4 Typos in Custom Agent β
The tools field in custom agent frontmatter uses lenient validation: a single invalid entry only emits a warning log and is ignored, without causing CodeBuddy Code to fail on startup. Look for log messages like the following to locate the issue:
text
[AgentToolSpec] my-agent: invalid-pattern β Invalid tool spec "Defer(Read(*.md))": ...CLI and ACP client --tools input uses strict validation and will immediately throw an error on invalid entries.
8. Interaction with Permission Rules β
Modifiers and --allowed-tools are completely orthogonal and can be used together:
bash
codebuddy \
--tools "Read,Write,Defer(Glob)" \
--allowed-tools "Glob(src/**)"Meaning:
Globuses deferred loading (not directly in the model's tool list)- When the model calls
GlobviaToolSearchβDeferExecuteTool, it's still subject to theGlob(src/**)permission constraint - Calling
Glob(/etc/*)will be denied by permission rules
9. Interaction with ToolSearch / DeferExecuteTool β
Modifiers directly affect the behavior of these two tools:
| Modifier | Effect on ToolSearch | Effect on DeferExecuteTool |
|---|---|---|
Defer(X) (X was originally not deferred) | Automatically added to the searchable deferred tool set on first access | Automatically registered and allowed on first call |
NoDefer(X) (X was originally deferred) | Does not appear in search results (X is already directly in the model's tool list β no need to search) | Calling X returns an error, directing to call it directly |
Error example (NoDefer path):
textError: Tool "Bash" is not deferred in this session (NoDefer modifier). Call it directly instead of via DeferExecuteTool.
10. Known Limitations β
- MCP server name as target: Currently not supported to use a server name directly as a modifier target in
--toolsβ only tool names or wildcards are accepted (e.g.,Defer(mcp__github__*)) - Wildcard reverse enumeration: Wildcard entries like
Defer(mcp__*)cannot reverse-enumerate specific tool names; lazy indexing of MCP tools still relies on the mcp-server-manager's own asynchronous driving process