Skip to main content

Monty-backed Python REPL middleware for Deep Agents, with a shared filesystem between the sandbox and the agent's file tools.

Project description

deepagents-monty

PyPI Python versions CI License: MIT

deepagents-monty adds a secure python_repl tool to Deep Agents using Pydantic's Monty sandboxed Python interpreter.

Agent-generated Python can inspect and transform the same virtual files used by read_file, write_file, ls, glob, grep, and edit_file, without giving that code host filesystem or network access.

Why use it?

  • Run model-written Python in a sandbox instead of on the host.
  • Let the model use loops, parsing, aggregation, and data transforms when file tools would be awkward.
  • Share one Deep Agents backend between the normal file tools and Monty code.
  • Expose carefully scoped host capabilities with external_functions.

Install

uv add deepagents-monty

Or with pip:

pip install deepagents-monty

Until the package is published on PyPI, install from this repository:

uv add 'deepagents-monty @ git+https://github.com/yesh0907/deepagents-monty'

Quickstart

from deepagents import create_deep_agent
from deepagents.backends import StateBackend
from deepagents_monty import MontyCodeMiddleware

backend = StateBackend()

agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-6",
    backend=backend,
    middleware=[MontyCodeMiddleware(backend=backend)],
)

result = await agent.ainvoke({
    "messages": [{
        "role": "user",
        "content": "Find all Python files larger than 1000 bytes and list their sizes.",
    }]
})

The model can now call python_repl with code like:

from pathlib import Path

sizes = []
for p in Path("/").iterdir():
    if str(p).endswith(".py"):
        content = p.read_text()
        if len(content) > 1000:
            sizes.append((str(p), len(content)))
sorted(sizes, key=lambda x: -x[1])

The files visible to Path("/") are the same files visible to the Deep Agents filesystem tools. python_repl preserves variables, imports, and function definitions between calls; pass restart=True to reset the REPL.

Security Model

Monty code runs in a restricted interpreter. By default, it has:

  • no host filesystem access
  • no network access
  • no arbitrary third-party imports
  • no access to environment variables
  • only the virtual filesystem backed by the Deep Agents backend you provide

Host capabilities must be explicitly exposed through external_functions. Treat those functions as part of the trusted boundary: validate inputs, keep return values simple, and expose only the operations the model should be allowed to use.

Limitations

deepagents-monty inherits Monty's current Python subset:

  • no class definitions
  • no match statements, generators, or context managers
  • no third-party libraries such as pandas, requests, or numpy
  • only a small standard-library subset
  • no wall-clock or timing primitives
  • no file deletes or renames through pathlib
  • filesystem bridging expects sync Deep Agents backend methods

See docs/limitations.md for the detailed list.

External Functions

Use external_functions when sandboxed code needs a carefully scoped host capability, such as CSV parsing, an API client, or a domain-specific helper.

from typing import Any

from deepagents import create_deep_agent
from deepagents.backends import StateBackend
from deepagents_monty import MontyCodeMiddleware


def read_csv(path: str) -> list[dict[str, Any]]:
    ...


backend = StateBackend()
agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-6",
    backend=backend,
    middleware=[
        MontyCodeMiddleware(
            backend=backend,
            external_functions={"read_csv": read_csv},
        )
    ],
)

The model can call the function from python_repl:

rows = read_csv("/transactions.csv")
sum(row["Amount"] for row in rows if row["Category"] == "Groceries")

Async external functions are supported. Sandbox code must call them with await:

data = await fetch_json("https://example.com/data.json")

Function signatures are inferred from Python annotations and added to Monty's type checker. Pass type_check_stubs when the public sandbox contract should be more precise than the host function signature.

Customizing the system prompt

MontyCodeMiddleware injects a default system prompt (the public MONTY_SYSTEM_PROMPT) that describes python_repl's mechanics and limitations to the model. There are two ways to customize it:

  • append_system_promptadd guidance after the default prompt. Use this when you want the built-in mechanics doc plus your own instructions (e.g. when to reach for python_repl):

    from deepagents_monty import MontyCodeMiddleware
    
    middleware = MontyCodeMiddleware(
        backend=backend,
        append_system_prompt=(
            "Reach for python_repl whenever a task involves loops, aggregation, "
            "or parsing JSON across many files — it is cheaper than a long chain "
            "of read_file calls."
        ),
    )
    
  • system_promptreplace the default prompt entirely. You lose the built-in mechanics/limitations doc, so only do this if you intend to describe the tool yourself.

The two compose independently: pass both to replace the base and append to it. The default prompt is exported as MONTY_SYSTEM_PROMPT if you want to introspect or compose against it:

from deepagents_monty import MONTY_SYSTEM_PROMPT

API

MontyCodeMiddleware(
    *,
    backend,
    system_prompt=None,
    append_system_prompt=None,
    external_functions=None,
    type_check_stubs=None,
    max_duration_secs=10.0,
    type_check=True,
)
  • backend: required Deep Agents backend. Pass the same backend you pass to create_deep_agent(backend=...).
  • system_prompt: optional replacement for the default prompt that describes python_repl to the model. Using this drops the built-in mechanics and limitations doc — prefer append_system_prompt when you just want to add guidance.
  • append_system_prompt: optional extra guidance appended after the base prompt (separated by a blank line). The base is system_prompt if provided, else the default. Composes independently of system_prompt. See Customizing the system prompt.
  • external_functions: optional host functions exposed as global names inside Monty code.
  • type_check_stubs: optional Python stub text for external functions and model-facing types.
  • max_duration_secs: per-call wall-clock cap for sandbox execution.
  • type_check: enables Monty's parse-time type checker. Defaults to True.

Advanced users can also build the standalone python_repl tool with make_execute_python(...).

Examples

Analyze Agent Files

from pathlib import Path
import json

records = []
for path in Path("/").iterdir():
    if str(path).endswith(".json"):
        records.append(json.loads(path.read_text()))

len(records)

Expose a Safe Reader

def read_transactions() -> list[dict[str, str]]:
    ...

middleware = MontyCodeMiddleware(
    backend=backend,
    external_functions={"read_transactions": read_transactions},
)

Use an Async Helper

async def lookup_customer(customer_id: str) -> dict[str, str]:
    ...

middleware = MontyCodeMiddleware(
    backend=backend,
    external_functions={"lookup_customer": lookup_customer},
)

Sandbox code:

customer = await lookup_customer("cust_123")
customer["segment"]

Development

uv sync --all-extras
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run pyright

Build the package locally:

uv build
uvx twine check dist/*

More Documentation

Credits

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

deepagents_monty-0.2.0.tar.gz (15.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

deepagents_monty-0.2.0-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file deepagents_monty-0.2.0.tar.gz.

File metadata

  • Download URL: deepagents_monty-0.2.0.tar.gz
  • Upload date:
  • Size: 15.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for deepagents_monty-0.2.0.tar.gz
Algorithm Hash digest
SHA256 9b285d3bd65af1bf485ca5ec66143fee7590bb3b6543ef93345f33f1be9d1d64
MD5 ba98e8b3657856da242e2013a5e33b6a
BLAKE2b-256 ef2689e0e43d3dfcb105ad786852c7138d62281c36bb857fa32acad79b02952f

See more details on using hashes here.

File details

Details for the file deepagents_monty-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: deepagents_monty-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for deepagents_monty-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 239a7c6a872b201006de729b74f14df936517e65a0c94996a4bbaa8471b95e53
MD5 8c7b56f38d376a1a3a38ec4a4b5bf3ba
BLAKE2b-256 6a0ad122fae4e6090124f29b85badac1aa82ea0696a010ded974e4b958911300

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page