hi @LucienShui
Deep Agents ships with a default set of built-in tools (ls, read_file, write_file, edit_file, glob, grep, execute, write_todos, task), and AFAIK right now there is no public, first-class parameter on create_deep_agent() to selectively exclude specific built-in tools. The tools parameter only adds custom tools on top of the built-in ones - it doesn’t replace them.
However, there is a clean, supported way to achieve this using custom middleware. Here are two approaches, from simplest to most advanced:
Custom tool-exclusion middleware
The LangChain AgentMiddleware system allows you to intercept model requests and filter tools before the LLM sees them. You can write a simple middleware that strips unwanted tools using wrap_model_call:
from langchain.agents.middleware import wrap_model_call
TOOLS_TO_EXCLUDE = {"grep", "glob", "write_todos", "task"}
@wrap_model_call
def exclude_tools(request, handler):
"""Remove unwanted built-in tools before the model sees them."""
filtered_tools = [
t for t in request.tools
if getattr(t, "name", None) not in TOOLS_TO_EXCLUDE
]
return handler(request.override(tools=filtered_tools))
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
tools=[search_by_tags], # your custom tools
middleware=[exclude_tools],
)
This works because:
- All built-in tools (from
FilesystemMiddleware, TodoListMiddleware, SubAgentMiddleware) are injected into request.tools via their respective middleware before your custom middleware runs.
- Your middleware sits in the stack after the tool-injecting middleware, so it can filter them out.
request.override(tools=filtered_tools) returns a new immutable request with only the tools you want - the LLM never sees the excluded ones.
Source: The Deep Agents customization guide documents custom middleware with wrap_model_call and wrap_tool_call decorators. The request.override() pattern is part of LangChain’s ModelRequest API.
Class-based middleware (more control)
If you need async support or more fine-grained control, subclass AgentMiddleware directly:
from langchain.agents.middleware.types import AgentMiddleware, ModelRequest, ModelResponse
class ToolExclusionMiddleware(AgentMiddleware):
"""Remove specific tools from the model's tool set."""
def __init__(self, exclude: set[str]):
self._exclude = exclude
def wrap_model_call(self, request, handler):
filtered = [
t for t in request.tools
if getattr(t, "name", None) not in self._exclude
]
return handler(request.override(tools=filtered))
async def awrap_model_call(self, request, handler):
filtered = [
t for t in request.tools
if getattr(t, "name", None) not in self._exclude
]
return await handler(request.override(tools=filtered))
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
tools=[search_by_tags],
middleware=[
ToolExclusionMiddleware(exclude={"grep", "glob", "write_todos", "task"}),
],
)
This is essentially what Deep Agents does internally - the framework has a private _ToolExclusionMiddleware class (source: deepagents/middleware/_tool_exclusion.py) that does exactly this, but it’s not part of the public API.
How built-in tools are injected (under the hood)
For context, here’s what happens inside create_deep_agent() when it builds the middleware stack (source: deepagents/graph.py):
Middleware Stack Order (simplified):
1. TodoListMiddleware → injects `write_todos`
2. SkillsMiddleware → (if skills provided)
3. FilesystemMiddleware → injects `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`, `execute`
4. SubAgentMiddleware → injects `task`
5. SummarizationMiddleware
6. PatchToolCallsMiddleware
7. YOUR CUSTOM MIDDLEWARE ← this is where your exclusion middleware runs
8. `_ToolExclusionMiddleware` ← (internal, driven by harness profiles)
9. AnthropicPromptCachingMiddleware
10. MemoryMiddleware ← (if memory provided)
11. HumanInTheLoopMiddleware ← (if `interrupt_on` provided)
12. `_PermissionMiddleware` ← (if permissions provided, always last)
Your custom middleware at position 7 runs after all the tool-injecting middleware (positions 1-4) have added their tools to request.tools, so you can filter them all.
Available built-in tool names
For reference, here are the exact tool names you can exclude:
| Tool Name |
Source Middleware |
Description |
ls |
FilesystemMiddleware |
List files in a directory |
read_file |
FilesystemMiddleware |
Read file contents |
write_file |
FilesystemMiddleware |
Create new files |
edit_file |
FilesystemMiddleware |
Edit files (string replacement) |
glob |
FilesystemMiddleware |
Find files matching patterns |
grep |
FilesystemMiddleware |
Search text in files |
execute |
FilesystemMiddleware |
Run shell commands (sandbox backends only) |
write_todos |
TodoListMiddleware |
Manage a todo list |
task |
SubAgentMiddleware |
Spawn and call subagents |
Important notes
-
Don’t remove task if you use subagents: The task tool is how the main agent delegates work to subagents. Removing it means no subagent spawning.
-
write_todos comes from LangChain, not Deep Agents itself - it’s injected via TodoListMiddleware (imported from langchain.agents.middleware).
-
Don’t mutate middleware state: As noted in the customization docs, do not mutate attributes after initialization. The exclusion set should be immutable (use frozenset if subclassing).
-
Feature request: If you’d like a first-class excluded_tools parameter on create_deep_agent(), consider opening an issue at github.com/langchain-ai/deepagents. Internally, the infrastructure already exists (_HarnessProfile.excluded_tools and _ToolExclusionMiddleware) - it’s just not exposed publicly yet.