`programmatic_tool_caller` lets the model use its other tools by writing a short program, instead of calling them one at a time and waiting for each result. It is on by default.

## How it works

The model calls the tool with a `code` string of Python. That code runs in isolation with every other registered tool exposed as an async function, so the model can chain calls, loop over them, filter results, and run independent calls in parallel, all in a single turn. Only text the code sends to `print` returns to the model; a tool’s return value stays in the code’s local scope unless the code prints it. That is the point: a step that touches many tools produces one concise result rather than filling the conversation with every intermediate payload.

For example, the model can read several files, keep only the matches it cares about, and print a summary, without the full contents of each file ever entering the context window.

## Interventions still apply

The tool calls the code makes run through the agent’s normal executor, so an autonomous [intervention](/docs/user-guide/harness/configure/interventions/index.md) policy (a Cedar policy or a natural-language risk policy) that denies a call blocks it, and the code sees a catchable error. Interrupt-based human approval cannot prompt from inside the code, so a call gated that way raises a catchable error rather than pausing for input.

## Isolated code execution

The code runs in [Monty](https://github.com/pydantic/monty), a Python interpreter that isolates the code it runs. Filesystem, environment, network, and process access do not exist in it, so the only thing model-authored code can reach is the set of tool functions handed in. Monty runs in a separate worker process with memory, time, and recursion limits.

Isolation for the code, not the tools

Monty bounds the code, not the tools it calls: `shell` still runs commands on the host. For an OS boundary around the tools themselves, run the agent inside a Strands Harness SDK [sandbox](/docs/user-guide/sdk/sandbox/index.md).

Output is capped (a runaway print cannot blow up the context window), and awaited code is bounded by a default timeout.

## Selecting it

The tool is selected by name through `builtin_tools`. Drop `programmatic_tool_caller` from the list to turn it off; see [add tools and instructions](/docs/user-guide/harness/configure/tools-and-instructions/index.md) for how selection works.

## Implementation

### Python

- [harness-sdk/harness-py/src/strands_harness/tools/programmatic_tool_caller.py](https://github.com/strands-agents/harness-sdk/blob/main/harness-py/src/strands_harness/tools/programmatic_tool_caller.py)

### TypeScript

- [harness-sdk/harness-ts/src/tools/programmatic-tool-caller.ts](https://github.com/strands-agents/harness-sdk/blob/main/harness-ts/src/tools/programmatic-tool-caller.ts)
