Skip to content

Glass-Box Transparency

Unlike black-box solutions, Anvil follows a “glass-box” philosophy. Every piece of generated code is:

  • Visible - Saved as plain Python files
  • Editable - You can modify the code directly
  • Controllable - “Eject” tools to take full ownership

All tools are saved to your anvil_tools/ directory:

Terminal window
ls anvil_tools/
# __init__.py
# tool_registry.json
# search_notion.py
# get_weather.py

View any tool’s code:

Terminal window
cat anvil_tools/search_notion.py

List all tools with their status:

Terminal window
anvil list

Output:

📦 Anvil Tools (3 total)
● search_notion (v1.0)
Search a Notion workspace for pages matching a query
● get_weather (v1.2)
Get current weather for a city
○ custom_tool (v1.0) [ejected]
Custom implementation
# Get tool code
code = anvil.get_tool_code("search_notion")
print(code)
# Get tool metadata
info = anvil.get_tool_info("search_notion")
print(info)

You can edit any generated tool directly:

anvil_tools/search_notion.py
# ---
# ANVIL-MANAGED: true
# INTENT-HASH: abc123...
# VERSION: 1.0
# ---
def run(query: str) -> dict:
# Add your custom logic here
import requests
response = requests.post(...)
return response.json()

If you edit a tool and the ANVIL-MANAGED: true header is still present:

  • Anvil will overwrite your changes if the intent changes
  • Self-healing will replace your code on failure

To prevent this, eject the tool.

“Ejecting” means taking full ownership of a tool. Anvil will never modify an ejected tool.

Change ANVIL-MANAGED: true to false:

1.0
# ---
# ANVIL-MANAGED: false
# INTENT-HASH: abc123...
# ---
def run(query: str) -> dict:
# Your custom implementation
...

Tools without a header are treated as ejected:

# No header = ejected
def run(query: str) -> dict:
# Your custom implementation
...
FeatureManaged ToolEjected Tool
Regeneration on intent change
Self-healing
Execution
Framework adapters

Ejected tools still work with all Anvil features except automatic code modification.

Metadata for all tools is stored in tool_registry.json:

{
"search_notion": {
"name": "search_notion",
"intent": "Search a Notion workspace",
"docs_url": "https://developers.notion.com/...",
"hash": "abc123...",
"version": "1.0",
"status": "active",
"created_at": "2025-01-18T10:00:00Z",
"last_generated": "2025-01-18T10:00:00Z",
"error_count": 0
}
}
StatusMeaning
activeTool is working normally
failedLast execution failed
ejectedUser has taken ownership

All generated code is plain text and should be committed to git:

Terminal window
git add anvil_tools/
git commit -m "Add search_notion tool"

Anvil’s init command creates a proper .gitignore:

# Protect API keys
.env
# Python cache
__pycache__/
*.pyc
# Optional: Ignore generated tools (if you prefer not to commit them)
# anvil_tools/

To force regeneration of all tools:

Terminal window
# Remove all managed tools (keeps ejected)
anvil clean
# Remove all tools including ejected
anvil clean --force
# Keep ejected tools explicitly
anvil clean --keep-ejected

When something goes wrong, you can inspect the exact code that ran:

# Check what Anvil generated
print(anvil.get_tool_code("broken_tool"))

For compliance or security review, all code is visible:

Terminal window
# Review all generated code
for f in anvil_tools/*.py; do
echo "=== $f ==="
cat "$f"
done

See how the LLM solves problems:

Terminal window
# Compare different versions
diff anvil_tools/search_v1.py anvil_tools/search_v2.py

Start with generated code, then customize:

  1. Generate initial tool
  2. Test and verify it works
  3. Eject and add custom logic
  4. Commit to version control