Pydantic AI Filesystem Sandbox
File sandbox implementation with LLM-friendly errors for pydantic ai
Install / Use
npx skills add zby/pydantic-ai-filesystem-sandboxInstalls into whichever agent you are using.
README
pydantic-ai-filesystem-sandbox
Filesystem sandbox toolset for PydanticAI agents with LLM-friendly errors.
Why This Package?
When building LLM agents that interact with the filesystem, you need:
- Sandboxing - Restrict which directories the agent can access
- Read/Write Control - Fine-grained permissions per path
- LLM-Friendly Errors - Error messages that help the LLM correct its behavior
- Approval Integration - Works with human-in-the-loop approval flows
Architecture
- Sandbox - Security boundary for permission checking and path resolution
- FileSystemToolset - File I/O tools that use Sandbox
- ApprovalToolset - Optional wrapper for human-in-the-loop approval
Installation
pip install pydantic-ai-filesystem-sandbox
Quick Start
from pydantic_ai import Agent
from pydantic_ai_filesystem_sandbox import (
FileSystemToolset,
Sandbox,
SandboxConfig,
Mount,
)
# Configure sandbox with Docker-style mounts
config = SandboxConfig(mounts=[
Mount(host_path="./data/input", mount_point="/input", mode="ro"), # Read-only
Mount(host_path="./data/output", mount_point="/output", mode="rw"), # Read-write
])
# Create the sandbox (security boundary)
sandbox = Sandbox(config)
# Create the toolset (file I/O tools)
toolset = FileSystemToolset(sandbox)
# Use with PydanticAI agent
agent = Agent("openai:gpt-4", toolsets=[toolset])
The agent can now access files using virtual paths like /input/data.txt and /output/results.json.
Simple Usage
For simple cases, use the factory method:
from pydantic_ai_filesystem_sandbox import FileSystemToolset
# Single directory with read-write access
toolset = FileSystemToolset.create_default("./data", mode="rw")
agent = Agent("openai:gpt-4", toolsets=[toolset])
Configuration
See API Reference for complete details.
Mount Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| host_path | Path | required | Host directory to mount |
| mount_point | str | required | Virtual path (e.g., "/docs", "/data") |
| mode | "ro" | "rw" | "ro" | Access mode |
| suffixes | list[str] | None | None | Allowed file extensions (None = all) |
| max_file_bytes | int | None | None | Maximum file size limit |
| write_approval | bool | True | Require approval for writes |
| read_approval | bool | False | Require approval for reads |
Example Configuration
from pydantic_ai_filesystem_sandbox import Sandbox, SandboxConfig, Mount
config = SandboxConfig(mounts=[
# Read-only input - any file type
Mount(
host_path="./data/input",
mount_point="/input",
mode="ro",
),
# Read-write output - only markdown and text
Mount(
host_path="./data/output",
mount_point="/output",
mode="rw",
suffixes=[".md", ".txt"],
max_file_bytes=1_000_000, # 1MB limit
),
# Config files - read-only, requires approval
Mount(
host_path="./config",
mount_point="/config",
mode="ro",
read_approval=True,
),
])
Single Root Mount
For projects where you want the entire directory as virtual /:
from pydantic_ai_filesystem_sandbox import Sandbox, SandboxConfig, Mount
config = SandboxConfig(mounts=[
Mount(host_path=".", mount_point="/", mode="rw"),
])
sandbox = Sandbox(config)
sandbox.resolve("/src/main.py") # -> <cwd>/src/main.py
Deriving child sandboxes
Use Sandbox.derive() to create a restricted child sandbox. By default the child has no access unless you explicitly allow paths.
parent = Sandbox(config)
# Empty child (secure by default)
child = parent.derive()
# Allow read-only access to a subtree
reader = parent.derive(allow_read="/output/reports")
# Allow read/write access to a subtree
writer = parent.derive(allow_write="/output/reports")
Available Tools
The toolset provides seven tools to the agent:
read_file
Read a text file from the sandbox.
Note: read_file currently reads the entire file into memory (even when using max_chars); use max_file_bytes to bound file size.
Path format: '/mount/path' (e.g., '/docs/file.txt')
Parameters:
- path: str (required)
- max_chars: int (default: 20,000)
- offset: int (default: 0)
write_file
Write a text file to the sandbox (requires mode="rw"). Parent directories are created automatically.
Path format: '/mount/path' (e.g., '/output/file.txt')
Parameters:
- path: str (required)
- content: str (required)
edit_file
Edit a file by replacing exact text (requires mode="rw").
Note: edit_file reads the entire file into memory; use max_file_bytes to bound file size.
Path format: '/mount/path'
Parameters:
- path: str (required)
- old_text: str (required) - must match exactly and be unique
- new_text: str (required)
delete_file
Delete a file from the sandbox (requires mode="rw").
Path format: '/mount/path'
Parameters:
- path: str (required)
move_file
Move or rename a file within the sandbox (requires mode="rw" for both source and destination). Parent directories are created automatically.
Path format: '/mount/path'
Parameters:
- source: str (required)
- destination: str (required)
copy_file
Copy a file within the sandbox. Source can be read-only, destination requires mode="rw". Parent directories are created automatically.
Path format: '/mount/path'
Parameters:
- source: str (required)
- destination: str (required)
list_files
List files matching a glob pattern.
Parameters:
- path: str (default: "/" for all mounts)
- pattern: str (default: "**/*")
LLM-Friendly Errors
All errors include guidance on what IS allowed:
# PathNotInSandboxError
"Cannot access '/secret/file.txt': path is outside sandbox.
Readable paths: /input, /output"
# PathNotWritableError
"Cannot write to '/input/file.txt': path is read-only.
Writable paths: /output"
# SuffixNotAllowedError
"Cannot access '/output/data.json': suffix '.json' not allowed.
Allowed suffixes: .md, .txt"
# FileTooLargeError
"Cannot read '/output/huge.txt': file too large (5,000,000 bytes).
Maximum allowed: 1,000,000 bytes"
# EditError
"Cannot edit '/output/file.txt': text not found in file.
Searched for: 'old text...'"
Approval Integration
Works with pydantic-ai-blocking-approval for human-in-the-loop:
from pydantic_ai_filesystem_sandbox import (
ApprovableFileSystemToolset, Sandbox, SandboxConfig, Mount
)
from pydantic_ai_blocking_approval import ApprovalToolset, ApprovalController
# Create sandbox and toolset
config = SandboxConfig(mounts=[
Mount(host_path="./output", mount_point="/output", mode="rw", write_approval=True),
])
sandbox = Sandbox(config)
toolset = ApprovableFileSystemToolset(sandbox)
# Wrap with approval
controller = ApprovalController(mode="interactive")
approved_toolset = ApprovalToolset(
inner=toolset,
approval_callback=controller.approval_callback,
memory=controller.memory,
)
agent = Agent(..., toolsets=[approved_toolset])
ApprovableFileSystemToolset extends FileSystemToolset with needs_approval() (returns ApprovalResult) and get_approval_description() for the approval UI.
Using the Sandbox Directly
The Sandbox class can be used independently for permission checking:
sandbox = Sandbox(config)
# Check permissions
if sandbox.can_write("/output/file.txt"):
resolved = sandbox.resolve("/output/file.txt")
# ... perform operation
# Query boundaries
print(sandbox.readable_roots) # ["/input", "/output"]
print(sandbox.writable_roots) # ["/output"]
# Check approval requirements
sandbox.needs_write_approval("/output/file.txt") # True/False
ReadResult
The read_file tool returns a ReadResult object:
class ReadResult(BaseModel):
content: str # The file content read
truncated: bool # True if more content exists
total_chars: int # Total file size in characters
offset: int # Starting position used
chars_read: int # Characters actually returned
This allows agents to handle large files by reading in chunks:
# First read
result = toolset.read("/input/large.txt", max_chars=10000)
if result.truncated:
# Continue reading
result2 = toolset.read("/input/large.txt", max_chars=10000, offset=10000)
API Reference
See docs/api.md for full API documentation.
License
MIT
Related Skills
node-connect
385.5kDiagnose OpenClaw Android, iOS, or macOS node pairing, QR/setup code, route, auth, and connection failures.
blender-python-addon
40.5kBlender Python add-on rules for operators, panels, properties, registration, testing, and API-safe scripting
flutter-development-guidelines-cursorrules-prompt-file
40.5kCursor rules for Flutter development with MVVM architecture, Riverpod state management, Material widgets, and Dart style guidelines.
commit-push-pr
140.6kCommit, push, and open a PR
