Monty CodeAct
Collapse multi-step tool chains into a single sandboxed Python execution using Pydantic Monty — a minimal, secure Python interpreter written in Rust with sub-microsecond startup.
Syntax
Tools are called as regular Python functions with keyword arguments:
content = view(path="src/main.py")
files = glob(pattern="**/*.py")
hits = grep(pattern="TODO", paths="src")
result = bash(command="git log --oneline -5")
edit(path="config.json", old_str='"debug": false', new_str='"debug": true')
Chain by sequencing:
for f in glob(pattern="**/*.py", paths="src"):
content = view(path=f)
if "TODO" in content:
print(f + ": " + str(content.count("TODO")) + " TODOs")
Discover tools
python3 scripts/codeact.py --discover # JSON manifest
python3 scripts/codeact.py --instructions # LLM-ready reference
Tools are auto-detected based on what's installed on the host.
Execute
uv run --with pydantic-monty python3 scripts/codeact.py --auto --workspace . --code '...'
Output: {"stdout": "...", "stderr": "...", "return_value": null, "success": true}
Monty limitations
Monty runs a subset of Python. Will error on:
- Classes — no
classkeyword at all - Match statements — no
match/case - f-string format specs —
f"{x:<10}",f"{x:>5}",f"{x:.2f}"all fail str.format()—"{:<10}".format(x)failsstr.startswith()with tuple — useorinstead- Set comprehensions — build with list +
inchecks - Third-party imports — only stdlib subset
- Most stdlib — only: json, re, datetime, sys, os.environ (no os.path, no os.walk)
- Brace expansion in glob —
glob(pattern="src/{db,services}/**/*.py")is supported.
Sandbox tool return types (getting these wrong causes retries):
glob(pattern=...)→ list of strings like["src/app.py", "src/utils.py"]view(path=...)→ string (file content)mcp_call(server=..., tool=..., ...)→ stringbash(command=...)→ dict with keysstdout,stderr,returncode
Key usage patterns:
- Do NOT scout first.
glob()andview()are in the sandbox — discover files inside your codeact program, not with separate tool calls before it. - One program, one bash call. Do not run multiple codeact invocations. If the first one fails, fix the bug in the program, don't add a scouting step.
- Wrap file reads in try/except so one bad file doesn't abort the run.
- Use
for f in glob(pattern="**/*.py"):to iterate files. No os.walk or os.path.
Output formatting workaround (use instead of format specs):
def pad(s, w):
s = str(s)
return s + " " * max(0, w - len(s))
Tips: Use chr(10) for newlines. Use import json explicitly.
Use string concatenation (+) or simple f-strings (f"count: {n}").
Trust model
Sandboxed code can only reach the outside world through registered tool
functions. Use --workspace to restrict file tools to a directory tree.
Prerequisites
- Python 3.10+
uv(recommended) orpip install pydantic-monty
References
- references/tool-patterns.md