ls, read_file, write_file, edit_file, glob, and grep. These tools operate through a pluggable backend. The read_file tool natively supports image files (.png, .jpg, .jpeg, .gif, .webp) across all backends, returning them as multimodal content blocks.
Sandboxes and the LocalShellBackend also provide an execute tool.
This page explains how to:
- choose a backend,
- route different paths to different backends,
- implement your own virtual filesystem (e.g., S3 or Postgres),
- comply with the backend protocol,
Quickstart
Here are a few prebuilt filesystem backends that you can quickly use with your deep agent:| Built-in backend | Description |
|---|---|
| Default | agent = create_deep_agent() Ephemeral in state. The default filesystem backend for an agent is stored in langgraph state. Note that this filesystem only persists for a single thread. |
| Local filesystem persistence | agent = create_deep_agent(backend=FilesystemBackend(root_dir="/Users/nh/Desktop/")) This gives the deep agent access to your local machine’s filesystem. You can specify the root directory that the agent has access to. Note that any provided root_dir must be an absolute path. |
| Durable store (LangGraph store) | agent = create_deep_agent(backend=StoreBackend()) This gives the agent access to long-term storage that is persisted across threads. This is great for storing longer term memories or instructions that are applicable to the agent over multiple executions. |
| Sandbox | agent = create_deep_agent(backend=sandbox) Execute code in isolated environments. Sandboxes provide filesystem tools plus the execute tool for running shell commands. Choose from Modal, Daytona, Deno, or local VFS. |
| Local shell | agent = create_deep_agent(backend=LocalShellBackend(root_dir=".", env={"PATH": "/usr/bin:/bin"})) Filesystem and shell execution directly on the host. No isolation—use only in controlled development environments. See security considerations below. |
| Composite | Ephemeral by default, /memories/ persisted. The Composite backend is maximally flexible. You can specify different routes in the filesystem to point towards different backends. See Composite routing below for a ready-to-paste example. |
Built-in backends
StateBackend (ephemeral)
- Stores files in LangGraph agent state for the current thread via
StateBackend. - Persists across multiple agent turns on the same thread via checkpoints.
- A scratch pad for the agent to write intermediate results.
- Automatic eviction of large tool outputs which the agent can then read back in piece by piece.
FilesystemBackend (local disk)
FilesystemBackend reads and writes real files under a configurable root directory.
- Reads/writes real files under a configurable
root_dir. - You can optionally set
virtual_mode=Trueto sandbox and normalize paths underroot_dir. - Uses secure path resolution, prevents unsafe symlink traversal when possible, can use ripgrep for fast
grep.
- Local projects on your machine
- CI sandboxes
- Mounted persistent volumes
LocalShellBackend (local shell)
- Extends
FilesystemBackendwith theexecutetool for running shell commands on the host. - Commands run directly on your machine using
subprocess.run(shell=True)with no sandboxing. - Supports
timeout(default 120s),max_output_bytes(default 100,000),env, andinherit_envfor environment variables. - Shell commands use
root_diras the working directory but can access any path on the system.
- Local coding assistants and development tools
- Quick iteration during development when you trust the agent
StoreBackend (LangGraph store)
When deploying to LangSmith Deployment, omit the
store parameter. The platform automatically provisions a store for your agent.StoreBackendstores files in a LangGraphBaseStoreprovided by the runtime, enabling cross‑thread durable storage.
- When you already run with a configured LangGraph store (for example, Redis, Postgres, or cloud implementations behind
BaseStore). - When you’re deploying your agent through LangSmith Deployment (a store is automatically provisioned for your agent).
Namespace factories
A namespace factory controls whereStoreBackend reads and writes data. It receives a BackendContext and returns a tuple of strings used as the store namespace. Use namespace factories to isolate data between users, tenants, or assistants.
Pass the namespace factory to the namespace parameter when constructing a StoreBackend:
BackendContext provides:
-
ctx.runtime.context— User-supplied context passed via LangGraph’s context schema (for example,user_id) -
ctx.runtime.server_info— Server-specific metadata when running on LangGraph Server (assistant ID, graph ID, authenticated user) -
ctx.runtime.execution_info— Execution identity information (thread ID, run ID, checkpoint ID) -
ctx.state— Current agent state
ctx.runtime.server_info and ctx.runtime.execution_info require deepagents>=0.5.0.(user_id, thread_id) for per-user per-conversation isolation, or append a suffix like "filesystem" to disambiguate when the same scope uses multiple store namespaces.
Namespace components must contain only alphanumeric characters, hyphens, underscores, dots, @, +, colons, and tildes. Wildcards (*, ?) are rejected to prevent glob injection.
When no namespace factory is provided, the legacy default uses the
assistant_id from LangGraph config metadata. This means all users of the same assistant share the same storage. For multi-user going to production, always provide a namespace factory.CompositeBackend (router)
CompositeBackendroutes file operations to different backends based on path prefix.- Preserves the original path prefixes in listings and search results.
- When you want to give your agent both ephemeral and cross-thread storage, a
CompositeBackendallows you provide both aStateBackendandStoreBackend - When you have multiple sources of information that you want to provide to your agent as part of a single filesystem.
- e.g. You have long-term memories stored under
/memories/in one Store and you also have a custom backend that has documentation accessible at /docs/.
- e.g. You have long-term memories stored under
Specify a backend
- Pass a backend instance to
create_deep_agent(backend=...). The filesystem middleware uses it for all tooling. - The backend must implement
BackendProtocol(for example,StateBackend(),FilesystemBackend(root_dir="."),StoreBackend()). - If omitted, the default is
StateBackend().
Route to different backends
Route parts of the namespace to different backends. Commonly used to persist/memories/* and keep everything else ephemeral.
/workspace/plan.md→StateBackend(ephemeral)/memories/agent.md→FilesystemBackendunder/deepagents/myagentls,glob,grepaggregate results and show original path prefixes.
- Longer prefixes win (for example, route
"/memories/projects/"can override"/memories/"). - For StoreBackend routing, ensure a store is provided via
create_deep_agent(store=...)or provisioned by the platform.
Use a virtual filesystem
Build a custom backend to project a remote or database filesystem (e.g., S3 or Postgres) into the tools namespace. Design guidelines:-
Paths are absolute (
/x/y.txt). Decide how to map them to your storage keys/rows. -
Implement
lsandglobefficiently (server-side filtering where available, otherwise local filter). -
For external persistence (S3, Postgres, etc.), return
files_update=None(Python) or omitfilesUpdate(JS) in write/edit results — only in-memory state backends need to return a files update dict. -
Use
lsandglobas the method names. -
Return structured result types with an
errorfield for missing files or invalid patterns (do not raise).
- Table
files(path text primary key, content text, created_at timestamptz, modified_at timestamptz) - Map tool operations onto SQL:
lsusesWHERE path LIKE $1 || '%'globfilter in SQL or fetch then apply glob in Pythongrepcan fetch candidate rows by extension or last modified time, then scan lines
Add policy hooks
Enforce enterprise rules by subclassing or wrapping a backend. Block writes/edits under selected prefixes (subclass):Migrate from backend factories
Previously, backends likeStateBackend and StoreBackend required a factory function that received a runtime object, because they needed runtime context (state, store) to operate. Backends now resolve this context internally via LangGraph’s get_config(), get_store(), and get_runtime() helpers, so you can pass instances directly.
What changed
| Before (deprecated) | After |
|---|---|
backend=lambda rt: StateBackend(rt) | backend=StateBackend() |
backend=lambda rt: StoreBackend(rt) | backend=StoreBackend() |
backend=lambda rt: CompositeBackend(default=StateBackend(rt), ...) | backend=CompositeBackend(default=StateBackend(), ...) |
backend: (config) => new StateBackend(config) | backend: new StateBackend() |
backend: (config) => new StoreBackend(config) | backend: new StoreBackend() |
Deprecated APIs
| Deprecated | Replacement |
|---|---|
Passing a callable to backend= in create_deep_agent | Pass a backend instance directly |
runtime constructor argument on StateBackend(runtime) | StateBackend() (no arguments needed) |
runtime constructor argument on StoreBackend(runtime) | StoreBackend() or StoreBackend(namespace=..., store=...) |
files_update field on WriteResult and EditResult | State writes are now handled internally by the backend |
Command wrapping in middleware write/edit tools | Tools return plain strings; no Command(update=...) needed |
The factory pattern still works at runtime and emits a deprecation warning. Update your code to use direct instances before the next major version.
Migration example
Protocol reference
Backends must implementBackendProtocol.
Required methods:
ls(path: str) -> LsResult- Return entries with at least
path. Includeis_dir,size,modified_atwhen available. Sort bypathfor deterministic output.
- Return entries with at least
read(file_path: str, offset: int = 0, limit: int = 2000) -> ReadResult- Return file data on success. On missing file, return
ReadResult(error="Error: File '/x' not found").
- Return file data on success. On missing file, return
grep(pattern: str, path: Optional[str] = None, glob: Optional[str] = None) -> GrepResult- Return structured matches. On error, return
GrepResult(error="...")(do not raise).
- Return structured matches. On error, return
glob(pattern: str, path: str = "/") -> GlobResult- Return matched files as
FileInfoentries (empty list if none).
- Return matched files as
write(file_path: str, content: str) -> WriteResult- Create-only. On conflict, return
WriteResult(error=...). On success, setpathand for state backends setfiles_update={...}; external backends should usefiles_update=None.
- Create-only. On conflict, return
edit(file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult- Enforce uniqueness of
old_stringunlessreplace_all=True. If not found, return error. Includeoccurrenceson success.
- Enforce uniqueness of
LsResult(error, entries)—entriesis alist[FileInfo]on success,Noneon failure.ReadResult(error, file_data)—file_datais aFileDatadict on success,Noneon failure.GrepResult(error, matches)—matchesis alist[GrepMatch]on success,Noneon failure.GlobResult(error, matches)—matchesis alist[FileInfo]on success,Noneon failure.WriteResult(error, path, files_update)EditResult(error, path, files_update, occurrences)FileInfowith fields:path(required), optionallyis_dir,size,modified_at.GrepMatchwith fields:path,line,text.FileDatawith fields:content(str),encoding("utf-8"or"base64"),created_at,modified_at. :::
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

