# How to Integrate a Custom Backend Implementation Using the BackendProtocol in DeepAgents

> Learn to integrate a custom backend with BackendProtocol in DeepAgents. Implement the interface, override file operations, and pass it to DeepAgent for seamless integration.

- Repository: [LangChain/deepagents](https://github.com/langchain-ai/deepagents)
- Tags: how-to-guide
- Published: 2026-03-17

---

**To integrate a custom backend in DeepAgents, implement the `BackendProtocol` interface (or `SandboxBackendProtocol` for command execution), override the required file operation methods, and pass the instance or a factory function to `DeepAgent`.**

The `langchain-ai/deepagents` repository provides a pluggable backend architecture that abstracts file-system operations behind the `BackendProtocol` interface. When you integrate a custom backend implementation using the `BackendProtocol` in DeepAgents, you can connect agents to diverse storage systems—from in-memory mocks and cloud object stores to remote containerized sandboxes—without modifying the core agent logic.

## Understanding the BackendProtocol Architecture

DeepAgents defines a **pluggable backend architecture** that abstracts all file-system-like operations (`list`, `read`, `write`, `edit`, `upload`, `download`, `grep`, `glob`) behind the `BackendProtocol` interface. Backends that also need to run shell commands implement the derived `SandboxBackendProtocol`.

### Core Protocol Definitions in protocol.py

The `BackendProtocol` in [`libs/deepagents/deepagents/backends/protocol.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/protocol.py) (lines 44–512) declares the core contract for any backend. All file-related methods are declared here and raise `NotImplementedError` by default. Key methods include:

- `ls_info(path: str) -> LsResult`
- `read(file_path: str, offset: int = 0, limit: int = 2000) -> ReadResult`
- `write(file_path: str, content: str) -> WriteResult`
- `edit(file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult`

### SandboxBackendProtocol for Command Execution

For backends that support command execution, `SandboxBackendProtocol` (same file, lines 512–558) extends `BackendProtocol` with the `execute(command: str, *, timeout: int | None = None) -> ExecuteResponse` method. This is essential for remote containers, VMs, or any sandboxed runtime.

### The BaseSandbox Mixin

The `BaseSandbox` class in [`libs/deepagents/deepagents/backends/sandbox.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py) (lines 217–260) provides a reusable mix-in that implements all file operations by delegating to `execute()`. Custom sandboxes usually subclass this instead of re-implementing every method, as it automatically translates file commands into shell commands.

## Implementing a Custom Backend

To add a custom backend, choose the appropriate base protocol, implement the abstract methods your use-case requires, and optionally provide a **factory** that binds the backend to the runtime.

### Minimal Read-Only Backend Example

The following example implements a read-only in-memory backend by subclassing `BackendProtocol`:

```python

# file: my_backend.py

from __future__ import annotations
from typing import Final
from deepagents.backends.protocol import BackendProtocol, LsResult, ReadResult, WriteResult, EditResult, FileInfo

class InMemoryBackend(BackendProtocol):
    """A simple read‑only backend that stores files in a dict."""

    def __init__(self, files: dict[str, str]) -> None:
        # `files` maps absolute paths → file content (UTF‑8 string)

        self._store: Final = files

    # ---------- required file operations ----------

    def ls_info(self, path: str) -> LsResult:
        if not path.endswith("/"):
            path = path + "/"
        entries = [
            FileInfo(path=p, is_dir=False)
            for p in self._store
            if p.startswith(path)
        ]
        return LsResult(entries=entries)

    def read(self, file_path: str, offset: int = 0, limit: int = 2000) -> ReadResult:
        content = self._store.get(file_path)
        if content is None:
            return ReadResult(error="file_not_found")
        # emulate cat‑n format

        lines = content.splitlines()[offset : offset + limit]
        numbered = "\n".join(f"{i+1}\t{ln}" for i, ln in enumerate(lines, start=offset))
        return ReadResult(file_data={"content": numbered, "encoding": "utf-8",
                                    "created_at": "", "modified_at": ""})

    # The backend is read‑only, so write/edit just return an error.

    def write(self, file_path: str, content: str) -> WriteResult:
        return WriteResult(error="permission_denied")

    def edit(self, file_path: str, old_string: str, new_string: str,
             replace_all: bool = False) -> EditResult:
        return EditResult(error="permission_denied")

```

#### Using the backend in a DeepAgent

Pass the backend as a factory or instance when creating the agent:

```python
from deepagents import DeepAgent
from deepagents.backends.protocol import BackendFactory
from my_backend import InMemoryBackend

def my_factory(runtime) -> InMemoryBackend:   # type: ignore[override]

    # `runtime` can be used to read env vars, tool configs, etc.

    demo_files = {
        "/app/hello.py": "print('Hello, DeepAgents!')\n",
        "/app/readme.md": "# Demo project\n\nThis is a demo.\n",

    }
    return InMemoryBackend(demo_files)

agent = DeepAgent(backend=my_factory)   # pass a factory or an instance

agent.run("list the files in /app")

```

### Sandbox Backend with Command Execution

If you need to run shell commands, inherit from `SandboxBackendProtocol` or the helper `BaseSandbox`. Below is a **mock sandbox** that executes commands locally using `subprocess.run`:

```python

# file: mock_sandbox.py

import subprocess
from deepagents.backends.protocol import SandboxBackendProtocol, ExecuteResponse

class MockLocalSandbox(SandboxBackendProtocol):
    @property
    def id(self) -> str:
        return "mock-local-sandbox"

    def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse:
        try:
            completed = subprocess.run(
                command,
                shell=True,
                capture_output=True,
                text=True,
                timeout=timeout,
                check=False,
            )
            return ExecuteResponse(
                output=completed.stdout + completed.stderr,
                exit_code=completed.returncode,
                truncated=False,
            )
        except subprocess.TimeoutExpired:
            return ExecuteResponse(
                output="",
                exit_code=None,
                truncated=True,
            )

```

> **Note** – The `BaseSandbox` class (see [[`libs/deepagents/deepagents/backends/sandbox.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py)](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py#L217-L260)) already implements all file methods (`ls_info`, `read`, `write`, …) by delegating to `execute`. If you subclass `BaseSandbox` you only need to provide `id` and `execute`.

#### Registering the sandbox for the agent

```python
from deepagents import DeepAgent
from mock_sandbox import MockLocalSandbox

agent = DeepAgent(backend=MockLocalSandbox())
agent.run("run `ls -R /` and show the first 20 lines")

```

### Runtime Configuration with BackendFactory

When your backend requires runtime values (e.g., API keys or environment variables), use a `BackendFactory` instead of a direct instance:

```python
def s3_backend_factory(runtime) -> BackendProtocol:
    bucket = runtime.env.get("S3_BUCKET")   # runtime.env is a dict‑like view

    aws_key = runtime.env.get("AWS_ACCESS_KEY_ID")
    # Assume `S3Backend` is a concrete implementation you wrote elsewhere.

    return S3Backend(bucket_name=bucket, aws_key=aws_key)

agent = DeepAgent(backend=s3_backend_factory)

```

The runtime object provides access to environment variables and tool configurations, allowing lazy initialization of the backend when the agent starts.

## Reference Implementations and Key Files

Study these concrete implementations to understand the minimal set of methods required for different backend types:

| File | Role | Link |
|------|------|------|
| [`libs/deepagents/deepagents/backends/protocol.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/protocol.py) | Core `BackendProtocol` & `SandboxBackendProtocol` definitions | [protocol.py](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/protocol.py) |
| [`libs/deepagents/deepagents/backends/sandbox.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py) | `BaseSandbox` mix‑in that implements file ops via `execute` | [sandbox.py](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py) |
| [`libs/deepagents/deepagents/backends/filesystem.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/filesystem.py) | Real filesystem backend – good reference for path handling | [filesystem.py](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/filesystem.py) |
| [`libs/deepagents/deepagents/backends/local_shell.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/local_shell.py) | Combines `FilesystemBackend` with `SandboxBackendProtocol` – shows hybrid implementation | [local_shell.py](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/local_shell.py) |
| [`libs/harbor/deepagents_harbor/backend.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/harbor/deepagents_harbor/backend.py) | Example of a sandbox backend that talks to a remote Harbor service | [Harbor backend](https://github.com/langchain-ai/deepagents/blob/main/libs/harbor/deepagents_harbor/backend.py) |
| [`libs/deepagents/tests/unit_tests/backends/test_protocol.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/tests/unit_tests/backends/test_protocol.py) | Test suite that validates the abstract contract – useful to see expected behaviours | [test_protocol.py](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/tests/unit_tests/backends/test_protocol.py) |

## Summary

- **Choose the right protocol**: Use `BackendProtocol` for file-only backends or `SandboxBackendProtocol` if you need to execute shell commands.
- **Implement required methods**: Override `ls_info`, `read`, `write`, `edit`, and other file operations declared in [`libs/deepagents/deepagents/backends/protocol.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/protocol.py).
- **Leverage BaseSandbox**: Subclass `BaseSandbox` from [`libs/deepagents/deepagents/backends/sandbox.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py) to get default file operation implementations that delegate to your `execute` method.
- **Use factories for runtime config**: Pass a `BackendFactory` function to `DeepAgent` when your backend requires environment variables or lazy initialization.
- **Register and run**: Pass your backend instance or factory to the `backend` parameter of `DeepAgent`, and the runtime automatically wires it into all middleware and tools.

## Frequently Asked Questions

### What is the difference between BackendProtocol and SandboxBackendProtocol?

`BackendProtocol` defines the core contract for file-system-like operations such as `read`, `write`, and `ls_info`. `SandboxBackendProtocol` extends this interface with an `execute(command: str)` method for running shell commands. If your backend needs to support command execution (like a Docker container or remote VM), implement `SandboxBackendProtocol` or subclass `BaseSandbox`.

### Do I need to implement every method in BackendProtocol?

No. You only need to implement the methods your use case requires. The base protocol in [`libs/deepagents/deepagents/backends/protocol.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/protocol.py) raises `NotImplementedError` by default for all methods. However, for a complete backend, you should implement at least `ls_info`, `read`, `write`, and `edit` to support basic file operations.

### How do I access environment variables inside my backend?

Use a `BackendFactory` instead of instantiating the backend directly. The factory receives a `runtime` object (of type `ToolRuntime`) that exposes `runtime.env`, a dict-like view of environment variables. This pattern allows you to configure API keys, bucket names, or connection strings at runtime rather than hard-coding them.

### Can I combine file operations with command execution without writing boilerplate?

Yes. Subclass `BaseSandbox` from [`libs/deepagents/deepagents/backends/sandbox.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py). This mixin implements all file methods (`ls_info`, `read`, `write`, etc.) by translating them into shell commands and delegating to your `execute` method. You only need to implement `id` and `execute` to get a fully functional sandbox backend.