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

# Tools

> Function tools, AgentTool sub-agents, transfer handoffs, exit loop, long-running tools, MCP adapters, and CallContext for stateful tools.

```mermaid theme={null}
flowchart LR
    classDef agent fill:#dbeafe,stroke:#3b82f6,color:#1e3a8a
    classDef tool fill:#f3e8ff,stroke:#a855f7,color:#581c87
    classDef effect fill:#fff7ed,stroke:#f97316,color:#7c2d12
    classDef external fill:#f0fdf4,stroke:#22c55e,color:#14532d

    A([LlmAgent]):::agent

    A --> FT[function_tool]:::tool
    A --> AT[AgentTool]:::tool
    A --> TT[make_transfer_tool]:::tool
    A --> EL[exit_loop_tool]:::tool
    A --> LR[LongRunningFunctionTool]:::tool
    A --> MCP[MCPToolAdapter]:::tool

    FT -->|wraps| Fn["async def fn()"]:::external
    AT -->|invokes| SA([sub-agent]):::agent
    SA -->|derive context\nbranch isolation| IC["Context\n.derive()"]:::effect
    TT -->|sets| EA1["EventActions\n.transfer_to_agent"]:::effect
    EL -->|sets| EA2["EventActions\n.escalate = True"]:::effect
    MCP -->|fetches from| MS[("MCP Server")]:::external
```

## Function tools

Wrap any async function as a LangChain `BaseTool`:

```python theme={null}
from orxhestra import function_tool

async def search_web(query: str) -> str:
    """Search the web and return results."""
    ...

tool = function_tool(search_web)
# or: function_tool(search_web, name="web_search", description="...")
```

Or use LangChain's `@tool` decorator directly - both work with `LlmAgent`.

## AgentTool - sub-agents as tools

Wrap a `BaseAgent` so it can be called as a tool by a parent agent:

```python theme={null}
from orxhestra import LlmAgent
from orxhestra.tools.agent_tool import AgentTool

research_agent = LlmAgent(name="ResearchAgent", model=model, tools=[search_tool])

parent = LlmAgent(
    name="Coordinator",
    model=model,
    tools=[AgentTool(research_agent)],
    instructions="Delegate research to ResearchAgent.",
)
```

The tool derives a child context with branch isolation and a clean session automatically. Sub-agent events stream through the parent in real-time via `ctx.event_callback` - each event carries a `branch` field (e.g. `"ResearchAgent"`) for attribution.

### AgentTool callbacks

Hook into the child agent's event stream with `before_agent_callback` (intercept each event, optionally short-circuit) and `after_agent_callback` (run after completion):

```python theme={null}
def check_event(event, child_ctx):
    if child_ctx.state.get("needs_approval"):
        return "Paused: awaiting approval."  # short-circuits, returns this as tool result
    return None  # continue

tool = AgentTool(research_agent, before_agent_callback=check_event)
```

Any custom tool can push events the same way:

```python theme={null}
if ctx.event_callback is not None:
    ctx.event_callback(my_event)
```

## Transfer tool - explicit agent handoff

```python theme={null}
from orxhestra import make_transfer_tool

transfer = make_transfer_tool([billing_agent, support_agent, tech_agent])
# The LLM can call "transfer_to_agent" with the target agent name.
# EventActions.transfer_to_agent is set; the parent routes accordingly.
```

## Exit loop tool

Signal a `LoopAgent` to stop iterating:

```python theme={null}
from orxhestra import exit_loop_tool

loop_agent = LoopAgent(
    name="RefineLoop",
    agents=[refine_agent],
    # refine_agent has exit_loop_tool in its tools list
)
```

## CallContext

Inside a tool's `_arun()`, use `CallContext` to read/write agent state:

```python theme={null}
from orxhestra.tools import CallContext

class MyStatefulTool(BaseTool):
    _ctx: CallContext | None = None

    def inject_context(self, ctx: InvocationContext) -> None:
        self._ctx = CallContext(ctx)

    async def _arun(self, query: str) -> str:
        self._ctx.state["last_query"] = query
        return "done"
```

`LlmAgent` automatically calls `inject_context()` on any tool that exposes it before each tool execution.

## Filesystem tools

Sandboxed file operations restricted to a configurable workspace directory. All paths are validated to prevent escaping the workspace.

```python theme={null}
from orxhestra.tools import make_filesystem_tools

tools = make_filesystem_tools(workspace="/tmp/my-workspace")
# or use $AGENT_WORKSPACE env var / default /tmp/agent-workspace

agent = LlmAgent(name="coder", model=model, tools=tools)
```

| Tool         | Description                                         |
| ------------ | --------------------------------------------------- |
| `ls`         | List files and directories                          |
| `read_file`  | Read file contents (returns base64 for images)      |
| `write_file` | Write content to a file, creating parents as needed |
| `edit_file`  | Replace the first occurrence of `old` with `new`    |
| `mkdir`      | Create a directory and parents                      |
| `glob`       | Find files matching a glob pattern (e.g. `**/*.py`) |
| `grep`       | Search file contents for a text pattern             |

In Composer YAML, use the `filesystem` builtin:

```yaml theme={null}
tools:
  fs:
    builtin: filesystem
```

## Shell tools

Sandboxed shell execution with timeout, output truncation, and command filtering.

```python theme={null}
from orxhestra.tools import make_shell_tools

tools = make_shell_tools(
    workspace="/tmp/my-workspace",
    timeout=30,                          # max seconds per command
    max_output_bytes=102400,             # truncate beyond 100KB
    allowed_commands=["python", "npm"],   # whitelist (optional)
    denied_commands=["rm", "shutdown"],   # blocklist (optional)
    env={"NODE_ENV": "test"},            # extra env vars (optional)
)
```

| Tool                    | Description                                   |
| ----------------------- | --------------------------------------------- |
| `shell_exec`            | Run a command and return stdout + stderr      |
| `shell_exec_background` | Start a command in the background, return PID |

Configuration via environment variables:

| Variable                 | Default                | Description                     |
| ------------------------ | ---------------------- | ------------------------------- |
| `AGENT_WORKSPACE`        | `/tmp/agent-workspace` | Working directory               |
| `AGENT_SHELL_TIMEOUT`    | `30`                   | Max seconds per command         |
| `AGENT_SHELL_MAX_OUTPUT` | `102400`               | Output truncation limit (bytes) |

In Composer YAML, use the `shell` builtin:

```yaml theme={null}
tools:
  sh:
    builtin: shell
```
