Skip to content
Conduit

mcp server

pydantic-ai-backend

File Storage & Sandbox Backends for Pydantic AI: console tools for file operations, Docker-isolated sandboxes for safe execution, and permission system with presets for access control. Enables secure

vstorm-co/pydantic-ai-backendcommunitylistedunreviewednot running here yet

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 14, 2026

Part of Pydantic Deep Agents — the open-source Claude Code alternative & Python agent framework. Use this library standalone, or get everything wired together in one create_deep_agent() call.

Pydantic AI Backend gives your Pydantic AI agent somewhere to work and the tools to work there. It supplies Pydantic AI workspaces — a Docker container, a sandboxd session, a Kubernetes pod, a Daytona sandbox, a JSON document — and a console toolset that reads, writes, searches and runs code in whichever workspace the run has, under a fine-grained permission system.

Use Cases

What You Want to BuildHow This Library Helps
AI Coding AssistantConsole tools with file ops + code execution, in any workspace
Multi-User Web AppA container per session, reached again by its ref
Code Review BotA read-only workspace: write and execute tools are never offered
Secure ExecutionPermission system blocks dangerous operations
Testing/CIStateWorkspace keeps files in memory, no container needed
Containerised SaaSsandboxd owns Docker so your app container never holds the socket

Installation

pip install pydantic-ai-backend

Or with uv:

uv add pydantic-ai-backend

Optional extras:

# Console tools (Pydantic AI 2.52+)
pip install "pydantic-ai-backend[console]"

# Workspaces (Pydantic AI 2.52+); add the provider's own extra
pip install "pydantic-ai-backend[workspaces,docker]"      # DockerWorkspace
pip install "pydantic-ai-backend[workspaces,kubernetes]"  # KubernetesWorkspace
pip install "pydantic-ai-backend[workspaces,daytona]"     # DaytonaWorkspace
pip install "pydantic-ai-backend[workspaces]"             # SandboxdWorkspace, StateWorkspace

# The sandbox service itself (install in the service image, not your app)
pip install "pydantic-ai-backend[server]"

# Reading what sandboxd sessions left behind
pip install "pydantic-ai-backend[remote]"

Quick Start

A workspace capability supplies the environment; ConsoleCapability gives the model tools that work in it:

from pydantic_ai import Agent

from pydantic_ai_backends import ConsoleCapability, DockerWorkspace

agent = Agent(
    "anthropic:claude-opus-5-5",
    capabilities=[DockerWorkspace(runtime="python-datascience"), ConsoleCapability()],
)

first = agent.run_sync("Create a script that calculates fibonacci and run it")
# Same container: the workspace ref travels on the message history.
second = agent.run_sync("Now add tests for it", message_history=first.all_messages())

That's it. Your agent can now:

  • List files and directories (ls)
  • Read and write files (read_file, write_file)
  • Edit files with string replacement (edit_file)
  • Search with glob patterns and regex (glob, grep)
  • Execute shell commands (execute)

The container is created on first use and kept after the run. Remove it when you are done with it: await workspace.destroy(ref).

With Permissions

from pydantic_ai_backends import ConsoleCapability
from pydantic_ai_backends.permissions import READONLY_RULESET

# Read-only agent — write/edit/execute tools are hidden from the model
console = ConsoleCapability(permissions=READONLY_RULESET)

On your own machine

Pydantic AI's own LocalWorkspace covers a directory on the host. It runs commands as you, so use it on your own code:

from pydantic_ai import Agent
from pydantic_ai.capabilities import LocalWorkspace

from pydantic_ai_backends import ConsoleCapability

agent = Agent(
    "anthropic:claude-opus-5-5",
    capabilities=[LocalWorkspace("./project"), ConsoleCapability()],
)

Available Workspaces

WorkspaceEnvironmentCommandsUse Case
StateWorkspaceA JSON documentNoTests, files kept in your database
DockerWorkspaceA container on this hostYesModel-written code, one host
SandboxdWorkspaceA container behind the sandboxd serviceYesContainerised apps that must not hold the Docker socket
KubernetesWorkspaceA pod, through pods/execYesSandboxes scheduled by a cluster
DaytonaWorkspaceA Daytona sandboxYesHosted sandboxes

Pydantic AI ships LocalWorkspace, and its harness covers E2B, Modal and Sprites; ConsoleCapability works in all of them. Every workspace here is created on first use, attached again by its ref, and never deleted for you. DockerWorkspace and SandboxdWorkspace pass Pydantic AI's own conformance suite against a real Docker daemon, and StateWorkspace passes it in CI.

Workspaces →

State documents

from pydantic_ai_backends import StateBackend, StateWorkspace

store = {"conv-42": StateBackend(files=row.files, directories=row.directories)}
workspace = StateWorkspace(store=store)

A StateBackend is a filesystem kept as a JSON document — files, and the directories created — so a host can keep a workspace in a database row and hand it back on the next turn. Binary content is held base64, so the document stays JSON even after an agent writes an image into it.

sandboxd: no docker.sock in your app

If your application runs in a container, giving it a Docker sandbox the obvious way means mounting /var/run/docker.sock — which is an unauthenticated API for root on the host. Docker-in-Docker needs --privileged and lands in the same place. So instead, one small service owns the socket and your app speaks HTTP to it:

from pydantic_ai import Agent

from pydantic_ai_backends import ConsoleCapability, SandboxdWorkspace

sandbox = SandboxdWorkspace(service_url="http://sandboxd:8080", token="...")
agent = Agent("anthropic:claude-opus-5-5", capabilities=[sandbox, ConsoleCapability()])

Nothing starts until it is used: the session — and the container behind it — opens on the first operation, so an agent granted a sandbox it never touches costs no container and not even a round trip. A later run with the same message history attaches to the same session, even after the idle reaper closed it, for as long as the service keeps its files.

The service is published as an image, and the client chooses nothing about the container:

services:
  app:
    environment: { SANDBOXD_URL: http://sandboxd:8080 }
    networks: [backend]          # no docker.sock here

  sandboxd:
    image: ghcr.io/vstorm-co/sandboxd:latest
    environment:
      SANDBOXD_TOKEN: ${SANDBOXD_TOKEN:?}
      SANDBOXD_HOST: 0.0.0.0
      SANDBOXD_WORKSPACE_ROOT: /workspaces      # files survive idle reaping
      SANDBOXD_MAX_SESSIONS_PER_TENANT: "5"     # one tenant cannot take the pool
    volumes: ["/var/run/docker.sock:/var/run/docker.sock"]
    group_add: ["${DOCKER_GID}"]  # it runs unprivileged; this reaches the socket
    networks: [backend]          # and no `ports:` either

Every field of SandboxdConfig is SANDBOXD_ plus its name in upper case — runtimes, ceilings, retention, the lot — so the whole policy is a compose file and there is no launcher to write. A value that will not parse, or a combination the service refuses, fails at startup naming the variable.

To embed it in something larger, create_app still takes the config directly:

from pydantic_ai_backends.remote.server import SandboxdConfig, create_app

app = create_app(
    SandboxdConfig(
        token="a-long-random-secret",
        runtimes={"python": "python:3.12-slim"},  # allowlist; a request sends an alias
        mem_limit="1g",
        cpus=2.0,
        network_mode="none",  # sandboxes get no network by default
        max_sessions=20,  # beyond this: 429, not unbounded containers
        workspace_root="/workspaces",  # files survive idle reaping
    )
)

Three settings decide what survives an idle timeout, and they cover different things: workspace_root keeps the work directory, persist_containers keeps the container's write layer so pip install survives too, and workspace_ttl reclaims workspaces nobody opens any more. await sandbox.destroy(ref) drops a session's files for good when its conversation is deleted.

Users can see what the agent wrote — including in a conversation from last week, long after its sandbox was reaped. WorkspaceArchive reads the stored workspace off the host volume, so no container starts:

from pydantic_ai_backends import WorkspaceArchive

archive = WorkspaceArchive("http://sandboxd:8080", token="...")
for entry in archive.ls(ref.id):
    print(entry["path"], entry["size"])
print(archive.read(ref.id, "report.md"))

Proxy it from your backend rather than handing a token to the browser — the service token can start containers.

The operator dashboard

SandboxdConfig(ui_enabled=True) serves a dashboard at /ui — one self-contained HTML file, no build step and no CDN, so it works offline and behind a strict CSP. Three views:

Sessions — capacity at a glance, and every open session with its tenant, idle time and memory against its own ceiling.

Workspace — one session at full width: a terminal with command history, the stored workspace's files, the activity log and session info.

Runtimes & policy — the allowlist with each runtime's image, ceilings and whether it gets a network, the config that produces it, and every limit in force.

Off by default: the page asks a human for the service token, and that token can start containers on the host.

sandboxd →

Console Tools

ConsoleCapability takes the toolset's options:

from pydantic_ai_backends import ConsoleCapability

# Without shell execution
ConsoleCapability(include_execute=False)

# Leaner descriptions for an agent that is not working in a repository
ConsoleCapability(profile="agent")

# Custom tool text — a string replaces the description, a ToolText
# replaces the per-argument text with it
ConsoleCapability(descriptions={"execute": "Run shell commands in the workspace"})

# read_file on .png/.jpg/.gif/.webp returns BinaryContent multimodal models can see
ConsoleCapability(image_support=True)

Prefer a bare toolset? create_console_toolset() returns the same tools; they work in ctx.workspace either way.

Available tools: ls, read_file, write_file, edit_file, glob, grep, execute

Permission System

Fine-grained access control, enforced by the tools on every call:

from pydantic_ai_backends import ConsoleCapability
from pydantic_ai_backends.permissions import DEFAULT_RULESET, READONLY_RULESET

# Safe defaults (allow reads, ask for writes)
ConsoleCapability(permissions=DEFAULT_RULESET)

# Read-only mode
ConsoleCapability(permissions=READONLY_RULESET)
PresetDescription
DEFAULT_RULESETAllow reads (except secrets), ask for writes/executes
PERMISSIVE_RULESETAllow most operations, deny dangerous commands
READONLY_RULESETAllow reads only, deny all writes and executes
STRICT_RULESETEverything requires approval

Docker Runtimes

Pre-configured environments for DockerWorkspace(runtime=...):

RuntimeImageWhat it adds
python-minimalpython:3.12-slimstandard library only
python-datasciencebuilt on python:3.12-slimpandas, numpy, matplotlib, scikit-learn, seaborn
python-analyticsbuilt on python:3.12-slimduckdb, polars, pyarrow
python-webbuilt on python:3.12-slimfastapi, uvicorn, sqlalchemy, httpx
python-scrapingbuilt on python:3.12-slimhttpx, beautifulsoup4, lxml, markdownify
python-documentsbuilt on python:3.12-slimpypdf, python-docx, openpyxl, pillow
node-minimalnode:20-slimnothing
node-typescriptbuilt on node:20-slimtypescript, tsx, vitest
node-reactbuilt on node:20-slimtypescript, vite, react, react-dom, @types/react
bunoven/bun:1-slimBun's own bundler, test runner and package manager
denodenoland/deno:alpineTypeScript with no install step
gogolang:1.23-alpineGo toolchain
rustrust:1-slimRust toolchain with cargo

A runtime naming an image starts as fast as a pull. One naming a base_image plus packages builds an image on first use and hits the cache afterwards, which is worth it when installing them per session would dominate.

Custom runtime:

from pydantic_ai_backends import DockerWorkspace, RuntimeConfig

runtime = RuntimeConfig(
    name="ml-env",
    base_image="python:3.12-slim",
    packages=["torch", "transformers"],
)
workspace = DockerWorkspace(runtime=runtime)

Why Choose This Library?

FeatureDescription
Native Pydantic AI workspacesDocker, sandboxd, Kubernetes, Daytona and state documents behind ctx.workspace
Console ToolsetReady-to-use tools that work in any workspace, including the harness's
Permission SystemPattern-based access control with presets
Docker IsolationSafe execution of untrusted code
No Docker-in-Dockersandboxd holds the socket; your app holds a token
Commands that stopA timed-out or cancelled command is stopped with everything it started
Image SupportMultimodal models can see images via BinaryContent
Pre-built RuntimesPython and Node.js environments ready to go

Vstorm OSS Ecosystem

This library is one piece of a broader open-source toolkit for production AI agents — all built on Pydantic AI.

ProjectDescriptionStars
Pydantic Deep AgentsThe full agent framework and terminal assistant — bundles every library below into one create_deep_agent() call.Stars
👉 pydantic-ai-backendPydantic AI workspaces — Docker / sandboxd / Kubernetes / Daytona / State — + console toolset.Stars
subagents-pydantic-aiDeclarative multi-agent orchestration — sync / async / auto, with token tracking.Stars
summarization-pydantic-aiUnlimited context for long-running agents — summarization or sliding window.Stars
pydantic-ai-shieldsDrop-in guardrails — cost caps, prompt-injection defense, PII & secret redaction, tool blocking.Stars
pydantic-ai-todoTask planning with subtasks, dependencies, and cycle detection.Stars
full-stack-ai-agent-templateZero to production AI app in 30 minutes — FastAPI + Next.js 15, RAG, 6 AI frameworks.Stars

Want it all wired together? Pydantic Deep Agents ships every library above integrated — planning, filesystem, subagents, memory, context management, and guardrails — behind a single function call. Browse all our open-source projects at github.com/vstorm-co.

Contributing

git clone https://github.com/vstorm-co/pydantic-ai-backend.git
cd pydantic-ai-backend
make install
make test  # 100% coverage required

Star History

If this library saved you from wiring an agent harness by hand — give it a ⭐. It's the single biggest thing that helps the project grow.


License

MIT — see LICENSE


Need help shipping AI agents in production?

Made with care by Vstorm

↑↓ navigate openesc close