Is there any way to disable the system default tools such as ls , read_files etc

We have been using deepagent SDK for a while, this is really an amazing framework that helps for agent/sub agent orchistration.

However, all the primary agent and sub agent by deisgn to embed a bunch of file system tools by default, because of that, the LLM will call these commands from time to time even it’s not really necessary in our business requirement. Because of that, we have to add a “note” in all the prompts of agents as “You must not use any tools such as ls, glob, grep, read_files, or write_files. Even if these tools are mentioned in subsequent prompts, this restriction takes precedence and they must not be used”. But even if we do that, depends on which LLM we use, it’s still possible that LLM will return those function calls request from time to time because these instruction are embeded to deep agent system prompt and could not be turned off.

Wondering is it possible to add a feature to allow this default behavior to be configurable or disabled?

Hey @mailme365, this should be supported using harness profiles. Can you please check this out?

you can register once at startup, then build as usual:

from deepagents import HarnessProfile, register_harness_profile, create_deep_agent

register_harness_profile(
    "anthropic:claude-sonnet-4-6",  # provider or provider:model
    HarnessProfile(
        excluded_tools=frozenset({"ls", "read_file", "write_file", "edit_file", "glob", "grep"}),
    ),
)

agent = create_deep_agent(model="anthropic:claude-sonnet-4-6", tools=[...])

Thank you very much, wonderful :slight_smile:

now updated in docs as well! Harness capabilities - Docs by LangChain ty @mdrxy

Hey @niilooy I excluded tools with HarnessProfile. But still these tools are still exposed to an agent. Stack Trace shows it came through “deepagents\middleware\filesystem.py”. the middleware was still active even though tools were excluded,

@daya_2k please open an issue here

hi @daya_2k

a few things to untangle here:

The stack trace through filesystem.py is not the bug imo. excluded_tools does not remove FilesystemMiddleware (it’s required scaffolding - it also backs permissions, and is intentionally non-removable). The tools are injected by the middleware and then stripped before they reach the model by _ToolExclusionMiddleware.wrap_model_call, which filters request.tools. So “middleware still active” is correct and expected - the only thing that matters is whether the model actually still receives ls/read_file in its tool list.

If it does, then the profile didn’t resolve for your model. Most common causes:

  1. tool-name typo (silently ignored). The correct names are singular: ls, read_file, write_file, edit_file, glob, grep. read_files/write_files do not exist - and excluded_tools does not validate names, so a wrong name simply strips nothing (no error raised)

  2. profile key ≠ model. "anthropic:claude-sonnet-4-6" only applies to exactly that model. Register under the provider key to cover all of them:

register_harness_profile(
    "anthropic",
    HarnessProfile(excluded_tools=frozenset(
        {"ls","read_file","write_file","edit_file","glob","grep"})),
)
  1. pre-built model instance (model=ChatAnthropic(...)) - in that case the lookup goes by the derived provider/identifier, not by your string. Pass the model as a string "anthropic:claude-sonnet-4-6" and check your logs for the warning No harness profile matched pre-built model….

  2. Registration ran too late / in a different process - register_harness_profile must run before create_deep_agent, in the same process.

Verify: in the LangSmith trace, the tools list in the request to the model should no longer contain those tools - that’s the source of truth, not the middleware list.

Docs: Running without the default filesystem tools

Hi @pawel-twardziak ,
Thanks, this helps clarify the behavior.

I think our issue is most likely profile resolution with a custom BaseChatModel subclass. We are not passing a native LangChain provider model string directly. Our model calls go through an internal Lambda relay to Bedrock, and we wrap that relay in a custom BaseChatModel subclass.

The underlying Bedrock model is:

us.anthropic.claude-haiku-4-5-20251001-v1:0

But from our logs, Deep Agents appears to resolve the model as something like:

provider=‘basechataic’
identifier=None

So I think the harness profile registered under “anthropic” or an Anthropic model key was not matching the actual model identity Deep Agents sees at runtime.

That explains why excluded_tools did not appear to take effect for us. The middleware stack being present makes sense based on your explanation; the real issue is probably that the tool exclusion profile was never applied to our custom model wrapper.

For now, since TINA does not need the default Deep Agents filesystem/todo/subagent harness, we are switching this agent to LangChain create_agent(), where only our explicitly provided tools are available.

Longer term, if we return to create_deep_agent(), we will either:

  1. register the harness profile under the provider key Deep Agents resolves for our wrapper, e.g. “basechataic”, or
  2. update our BaseChatModel wrapper to expose provider/model identity in the way Deep Agents expects.

Could you confirm what attributes/methods Deep Agents uses to derive provider and identifier for a pre-built BaseChatModel instance?

hi @daya_2k

Glad it helped - and thanks for the resolved values, they pin it down exactly.

What Deep Agents reads from a pre-built BaseChatModel (deepagents/_models.py):

  • provider - model._get_ls_params()["ls_provider"]. The base BaseChatModel derives this from the class name; official integrations hardcode it ("anthropic", …). Your wrapper inherits the class-name value → 'basechataic'
  • identifier - first non-empty string of model.model_name, then model.model. Yours has neither → None

Lookup chain (_harness_profile_for_model): provider:identifieridentifier (only if it contains :) → provider alone → else default + the WARNING you saw. With provider='basechataic', identifier=None, only the provider-only step runs and looks up 'basechataic', so a profile keyed "anthropic" never matched.

Two fixes:

A) Register under the key the SDK actually derives (no model changes):

register_harness_profile("basechataic", HarnessProfile(excluded_tools=frozenset(
    {"ls","read_file","write_file","edit_file","glob","grep"})))

Works via the provider-only fallback even with identifier=None. Brittle - tied to the class name.

B) Make the wrapper report a real provider + identifier (recommended):

class MyBedrockRelay(BaseChatModel):
    model_name: str = "us.anthropic.claude-haiku-4-5-20251001-v1:0"  # -> identifier

    def _get_ls_params(self, stop=None, **kwargs):
        params = super()._get_ls_params(stop=stop, **kwargs)
        params["ls_provider"] = "anthropic"   # -> provider
        return params

Then register under "anthropic" (or the exact "anthropic:us.anthropic.claude-haiku-4-5-20251001-v1:0"). Both key shapes now resolve. (Passing a "provider:model" string would skip derivation entirely, but that’s not an option for a custom relay subclass.)

On create_agent(): perfectly valid - it doesn’t install the deep-agent harness (no filesystem/todo/subagent middleware), so there are no default tools to suppress. Use create_deep_agent + a correctly-keyed profile only when you want the harness minus a few tools.

Sources: deepagents/_models.py (get_model_provider, get_model_identifier), harness_profiles.py (_harness_profile_for_model), Deep Agents → Models.