> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maadify.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Code Execution

> Give an agent a sandboxed Python tool that can compute, transform data, read attached files, and call other tools

The **Code execution** tool lets an agent run a small Python script inside a sandbox during a workflow. Use it when a task needs deterministic computation that the language model is not good at on its own — parsing or reshaping JSON, regex extraction, math and aggregation, sorting and deduplicating, joining results from several tools, or validating tool inputs and outputs before sending them on.

Code execution tools are configured from connectors as a **Code execution tool**. After you configure the tool, add it to a sub-agent or parent agent workflow. When the workflow runs, Maadify validates the script, executes it in an isolated sandbox, and returns the result, captured stdout, and the list of available linked tools to the agent.

<Tabs>
  <Tab title="Tool Configurations">
    <Frame>
      <img src="https://mintcdn.com/maadify/K7CT7HZ-nt5ok2U5/images/code_execution/code_execution_tool_configs.png?fit=max&auto=format&n=K7CT7HZ-nt5ok2U5&q=85&s=baea39158f43ce43afbf31fa49da1f68" alt="Code execution tool configuration with default code and include files toggle" width="1113" height="991" data-path="images/code_execution/code_execution_tool_configs.png" />
    </Frame>
  </Tab>

  <Tab title="Add Linked Tools">
    <Frame>
      <img src="https://mintcdn.com/maadify/K7CT7HZ-nt5ok2U5/images/code_execution/code_execution_linked_tools.png?fit=max&auto=format&n=K7CT7HZ-nt5ok2U5&q=85&s=ee38f68f63c4b9cf6861b395f4333c0f" alt="Code execution tool configuration with default code and include files toggle" width="1107" height="966" data-path="images/code_execution/code_execution_linked_tools.png" />
    </Frame>
  </Tab>
</Tabs>

## How code execution works

A code execution tool has three parts:

* **Default code** is an optional Python script that runs when the agent does not provide its own.
* **Default inputs** are JSON values exposed as variables inside the script.
* **Linked tools** are other Maadify tools that the script can call by name.

At runtime, Maadify merges the agent's request with any saved defaults, parses the Python code, runs it in a sandboxed interpreter with strict resource limits, and returns the value of the last expression as the tool output. Anything the script writes with `print` is captured separately as `stdout`.

<Frame>
  <img src="https://mintcdn.com/maadify/K7CT7HZ-nt5ok2U5/images/code_execution/code_execution_test.png?fit=max&auto=format&n=K7CT7HZ-nt5ok2U5&q=85&s=0d45e9341bbbab8c2e13a55af491adb3" alt="Diagram of agent input, default inputs, linked tools, attached files, sandbox execution, and returned output" width="734" height="963" data-path="images/code_execution/code_execution_test.png" />
</Frame>

## Create a code execution tool

<Steps>
  <Step title="Open the code execution connector">
    Go to **Connectors** and open the **Code execution** connector.
  </Step>

  <Step title="Create a Code execution tool">
    Add a tool with the type **Code execution tool**. The tool configuration opens with the script editor and the default inputs JSON field.
  </Step>

  <Step title="Describe the tool">
    Add a clear name and description that explains when an agent should use this code execution tool. The description shows up in the tool picker and helps the agent decide when to call it.
  </Step>

  <Step title="Add default code (optional)">
    Use the **Default code** editor to provide a script that runs when the agent calls the tool without supplying its own code. Leave it empty to require the agent to write the code each time.
  </Step>

  <Step title="Add default inputs (optional)">
    In **Default inputs**, paste a JSON object whose keys become variables in the script. Use this to share constants, lookup tables, or shared configuration with every run.
  </Step>

  <Step title="Enable attached files (optional)">
    Turn on **Include attached files** if the script should be able to read files the user attaches in chat. See [Attached files in the sandbox](#attached-files-in-the-sandbox).
  </Step>

  <Step title="Link other tools (optional)">
    Link other Maadify tools so the script can call them. See [Calling other tools from a script](#calling-other-tools-from-a-script).
  </Step>

  <Step title="Save the tool">
    Save the configuration. Maadify validates the default code, stores the inputs, and updates the tool input schema.
  </Step>
</Steps>

## Use code execution in an agent

<Steps>
  <Step title="Open the agent configuration">
    Open the parent agent or sub-agent that should run scripts.
  </Step>

  <Step title="Add the code execution tool">
    Choose the code execution tool you created from the connector.
  </Step>

  <Step title="Choose how the agent can use it">
    For assistant sub-agents, register the tool so the agent can call it. For execution-focused parent agent steps, configure the tool as an executable action with a fixed payload.
  </Step>

  <Step title="Update the system prompt">
    Add a section to the sub-agent prompt that explains when to use code execution and what the sandbox supports. Reference the `code_execution_stubs` and `input.files` template variables so the prompt always reflects the current linked tools and attached files. See [Prompt template variables](#prompt-template-variables).
  </Step>

  <Step title="Test in chat">
    Open [https://app.maadify.com/chat](https://app.maadify.com/chat), choose the agent, and send a request that should trigger a script run.
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/maadify/K7CT7HZ-nt5ok2U5/images/code_execution/add_code_execution_tool.png?fit=max&auto=format&n=K7CT7HZ-nt5ok2U5&q=85&s=8f887f0551122b5d4189300fe5a72dbd" alt="Adding a Code execution tool to a sub-agent" width="506" height="314" data-path="images/code_execution/add_code_execution_tool.png" />
</Frame>

<Tip>
  If you are adding a code execution tool to a sub-agent, you will need to add the tool to the sub-agent's system prompt. Use the system **Code Execution Template** as it provides all necessary stubs and limitations.
</Tip>

## What the agent sends to the tool

When the agent calls the code execution tool, it sends:

* **`code`**: a complete Python script. The value of the last expression is returned as the tool output.
* **`inputs`**: an optional JSON object. Each key is exposed as a top-level variable in the script.

Maadify merges any default inputs you saved with the agent's inputs before the script runs. The agent can also read context from the workflow, such as previous tool outputs or runtime inputs, by populating those values into the `inputs` object.

## What the tool returns

A successful run returns:

* **`success`**: `true`.
* **`output`**: the value of the last expression in the script.
* **`stdout`**: anything the script printed.
* **`available_functions`**: the list of linked tools the script could have called.

A failed run returns:

* **`success`**: `false`.
* **`error`**: an object with `type` (such as `SyntaxError`, `TypingError`, `RuntimeError`, or `ToolValidationError`) and a `message`.
* **`stdout`**: any output captured before the failure.

<Tip>
  Encourage the agent to read the `error.message` and rewrite the script before retrying. Retrying the same code unchanged will fail the same way.
</Tip>

## Sandbox capabilities and limits

Scripts run inside a restricted Python interpreter, not a full Python process. Many features and libraries are intentionally unavailable.

### Importable modules

Only the following standard library modules are available. Importing anything else raises `ModuleNotFoundError`.

* `json`
* `math`
* `re`
* `pathlib`
* `typing`
* `os`
* `sys`
* `asyncio`
* `datetime` *(the module imports, but `datetime.now()` and `date.today()` are not available — pass the current time in through `inputs` if you need it)*

There is no `requests`, `numpy`, `pandas`, `pydantic`, `csv`, `collections`, `itertools`, `functools`, `dataclasses`, `enum`, `decimal`, `uuid`, `hashlib`, `base64`, `random`, `time`, `urllib`, `subprocess`, `socket`, or `threading` in the sandbox.

### Available builtins

`print`, `len`, `range`, `min`, `max`, `sum`, `sorted`, `reversed`, `zip`, `enumerate`, `map`, `filter`, `any`, `all`, `round`, `abs`, `divmod`, `ord`, `chr`, `hex`, `bin`, `oct`, `bool`, `int`, `float`, `str`, `list`, `tuple`, `dict`, `set`, `repr`, `iter`, `next`, `id`, `hash`, `isinstance`, `hasattr`, and `type`.

### Builtins that are not available

`open`, `exec`, `eval`, `compile`, `__import__`, `globals`, `locals`, `dir`, `vars`, `input`, `format`, `callable`, `frozenset`, `bytearray`, and the `bytes([...])` constructor.

### Syntax that is not supported

The sandbox parser rejects:

* `class` definitions of any kind, including dataclasses, `TypedDict`, `NamedTuple`, and custom exception subclasses. Re-raise built-in exceptions such as `ValueError` instead.
* `with` statements and context managers. Read and write files directly with `pathlib.Path` methods.
* `yield` and generators. Use list comprehensions, `map`, or `filter`.
* `match` and `case` statements. Use `if` and `elif`.
* `%` string formatting and `str.format()`. Use f-strings.

### Syntax that works

f-strings, list and dict and set comprehensions, lambdas, decorators, type hints, `async` and `await`, `try` and `except` and `raise` and `raise from`, the walrus operator `:=`, ternary expressions, and `*` and `**` unpacking.

### Resource limits

Each run is bounded by per-call limits. Defaults are roughly:

* **Wall time**: 5 seconds.
* **Memory**: 128 MB.
* **Allocations**: 100,000.
* **Recursion depth**: 50.

Platform caps allow up to 15 seconds, 256 MB, 250,000 allocations, and recursion depth 100. Scripts that exceed any limit are killed with a runtime error.

<Warning>
  Code execution is not a place for long-running jobs, web scraping, or work that needs network access. Use a connector or a browser tool for those tasks.
</Warning>

## Attached files in the sandbox

When **Include attached files** is enabled, files the user attaches in chat are mounted into the sandbox under `/files/` and a list named `files` is automatically added to the script's variables. Each entry has the shape:

```python theme={null}
{"path": "/files/<name>", "file_name": str, "file_type": str, "element_id": str}
```

Read the files with `pathlib.Path`. The `open(...)` builtin is not available.

```python theme={null}
from pathlib import Path

results = []
for f in files:
    file_type = f.get("file_type") or ""
    if file_type.startswith("text/") or file_type == "application/json":
        content = Path(f["path"]).read_text()
    else:
        content = Path(f["path"]).read_bytes()
    results.append({"name": f["file_name"], "size": len(content)})

results
```

<Tip>
  Always iterate the runtime `files` list and read `f["path"]` instead of hard-coding `/files/<file_name>`. When two attachments share the same name, Maadify suffixes the path automatically and the display name will not match.
</Tip>

<Note>
  Files exist only in the sandbox's in-memory virtual filesystem. The script cannot reach the host filesystem, and nothing the script writes is persisted after the run.
</Note>

## Calling other tools from a script

A code execution tool can link to other Maadify tools so the script can call them as Python functions. Use this to combine tool outputs without making the agent call each tool one at a time.

<Steps>
  <Step title="Link tools">
    In the code execution tool configuration, link the Maadify tools the script should be able to call.
  </Step>

  <Step title="Call them with await">
    Inside the script, call each tool with a single dict as `input_data` and await the result.
  </Step>
</Steps>

```python theme={null}
result = await some_linked_tool({"field": "value"})
```

Linked tools are exposed under their original tool name. When several linked tools share the same base name, Maadify also exposes the unique `<tool_name>_<id>` form so the script can target a specific instance.

<Warning>
  Always pass the payload as a single dict. Do not spread it with `**payload` — the sandbox rejects that pattern and you will see a `TypingError`.
</Warning>

<Note>
  Code execution tools cannot link to other code execution tools. This prevents recursive sandbox runs.
</Note>

## Prompt template variables

Two prompt template variables help an agent use the code execution tool well. Reference both from the sub-agent system prompt so the prompt always reflects what is actually wired into the tool.

### `code_execution_stubs`

`code_execution_stubs` is a dictionary keyed by the unique tool name (for example `code_execution_tool_42`). Each value is a Python stub block that lists every linked tool's signature. Loop over the dictionary and render each stub:

````jinja theme={null}
{% if code_execution_stubs %}
{% for tool_key, stub in code_execution_stubs.items() %}
### Stubs for `{{ tool_key }}`
```python
{{ stub }}
```
{% endfor %}
{% endif %}
````

This keeps the prompt accurate as you link or unlink tools. The agent learns the exact function names, expected payload fields, and return types without you maintaining a parallel description by hand.

### `input.files`

`input.files` is the list of files attached to the current chat turn. Each entry has `element_id`, `file_name`, and `file_type`. The list is populated whether or not **Include attached files** is enabled, so the prompt can describe what was attached even when the script does not have read access.

```jinja theme={null}
{% if input.files %}
The user attached {{ input.files|length }} file{{ 's' if input.files|length != 1 else '' }}:

{% for f in input.files %}
- `{{ f.file_name or f.element_id }}`{% if f.file_type %} — {{ f.file_type }}{% endif %}
{% endfor %}
{% endif %}
```

<Tip>
  Use both variables together. The `code_execution_stubs` block teaches the agent which linked tools it can call. The `input.files` block tells the agent which files are available to read in the sandbox.
</Tip>

## Best practices

* Pass data into the script through `inputs` instead of hard-coding it in `code`. Hard-coded data wastes tokens and breaks when the input shape changes.
* Make sure the last line of the script is the value you want returned. An assignment on the last line returns nothing.
* Prefer returning structured data, such as dicts or lists of primitives, over pretty-printed strings. Later workflow steps can map structured fields directly.
* Use `print` for diagnostic logs only. The structured `output` is what the rest of the workflow consumes.
* Keep loops bounded and avoid deep recursion so the script stays within the resource limits.
* When an attached file is large, read its size with `Path(...).stat().st_size` before reading the bytes.
* Test the tool in chat with realistic inputs before adding it to a production workflow.

## Troubleshooting

<AccordionGroup>
  <Accordion title="The script fails with ModuleNotFoundError">
    * The sandbox only ships the modules listed in [Importable modules](#importable-modules).
    * Replace third-party libraries with built-in equivalents — for example, use `re` instead of regex helpers, or use `json` instead of a parser library.
    * If you need a network call, use a different connector instead of code execution.
  </Accordion>

  <Accordion title="The script fails with NotImplementedError on class or with">
    * The sandbox parser does not support `class`, `with`, `yield`, or `match` statements.
    * Refactor classes into plain functions and dictionaries. Replace `with open(...)` with `pathlib.Path` reads. Replace generators with list comprehensions. Replace `match` with `if` and `elif`.
  </Accordion>

  <Accordion title="The tool returns no output">
    * Check that the last line of the script is the value you want returned, not an assignment.
    * The tool returns the value of the last expression. `result = compute()` returns nothing because it is a statement, not an expression. Add a final line `result` to return it.
  </Accordion>

  <Accordion title="A linked tool call returns a coroutine instead of data">
    * The linked tool was called without `await`. Add `await` to the call.
    * Linked tools are async and must be awaited from within the script.
  </Accordion>

  <Accordion title="A linked tool call fails with TypingError or invalid-argument-type">
    * Pass the payload as a single dict, for example `await tool_name({"field": "value"})`.
    * Do not spread the payload with `**payload` — the sandbox rejects this pattern.
  </Accordion>

  <Accordion title="The script cannot read an attached file">
    * Confirm **Include attached files** is enabled on the code execution tool.
    * Use `pathlib.Path` rather than `open(...)`, which is not available in the sandbox.
    * Iterate the runtime `files` list and read `f["path"]` rather than guessing `/files/<file_name>`.
  </Accordion>

  <Accordion title="The script is killed mid-run">
    * The run hit a resource limit. Check the error message for details on which limit was exceeded.
    * Reduce the size of the data you pass in, bound any loops, or move heavy work into a different connector.
  </Accordion>
</AccordionGroup>
