# How to Handle Multi-Step Tool Calling Chains with Nested Function Execution in Needle

> Master multi-step tool calling chains with nested function execution in Needle. Learn to orchestrate complex workflows using our lightweight framework for recursive Python function calls.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-08-18

---

**Needle implements a lightweight tool-calling framework that enables recursive execution of Python functions decorated with `@tool`, allowing language models to orchestrate complex workflows through schema-validated nested chains.**

The Needle framework provides a deterministic mechanism for building AI agents that execute multi-step tool calling chains through nested function execution. By leveraging runtime schema generation and Python type hints, Needle transforms ordinary functions into discoverable tools that can recursively invoke one another while maintaining strict validation contracts according to the `cactus-compute/needle` source code.

## Understanding the Tool Registration System

### Schema Generation via the `@tool` Decorator

Needle's tool registration centers on the `@tool` decorator defined in **[`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)**. When applied to a function, the decorator immediately invokes `build_schema()` to generate a JSON-Schema description based on the function's signature, type hints, and docstring. This schema is stored on the function's `_needle_tool` attribute (see lines `163‑165`).

The `build_schema()` function (lines `111‑141`) inspects parameters using the `inspect` module, mapping Python types to JSON Schema types through internal helpers. It handles complex annotations including unions and optional types, ensuring the resulting schema accurately represents the function's contract for language model consumption.

### Type Mapping and Validation Helpers

The framework includes sophisticated type resolution utilities:

- **`_json_type`** (lines `57‑82`): Maps Python types (`str`, `int`, `float`, `bool`, `list`, `dict`) to corresponding JSON Schema types
- **`_is_optional`** (lines `52‑55`): Detects `Optional[T]` and `Union[T, None]` annotations to mark parameters as non-required in the schema
- **Default value handling**: Extracts defaults from `Field` definitions and standard function signatures to populate `default` keys in the schema

Because schema generation occurs once at decoration time and results are cached on the function object, runtime overhead during multi-step chain execution remains minimal.

## Implementing Nested Tool Execution

### Registering Individual Tools

Define atomic tools using the `@tool` decorator with complete type annotations. Each tool becomes a node in potential execution chains:

```python

# needle/agent/tools.py

from needle.agent.tools import tool

@tool
def fetch(url: str) -> str:
    """Download the content of *url* and return it as text."""
    import httpx
    return httpx.get(url).text

@tool
def extract_title(html: str) -> str:
    """Extract the <title> from a block of HTML."""
    import re
    m = re.search(r"<title>(.*?)</title>", html, re.I)
    return m.group(1).strip() if m else "No title"

```

The decorator automatically attaches JSON-Schema metadata to both functions, enabling the agent to validate arguments before invocation.

### Composing Multi-Step Chains

Create complex workflows by calling decorated tools from within other tool functions. Needle's dispatcher recognizes nested tool calls and recursively executes them with full schema validation:

```python
@tool
def fetch_and_title(url: str) -> str:
    """Fetch a page and return its title."""
    # First tool call - invokes the fetch tool

    html = fetch(url)
    # Second tool call - consumes previous result

    title = extract_title(html)
    return title

```

When the agent invokes `fetch_and_title`, the framework executes `fetch` first, validates its string output against `extract_title`'s schema, then proceeds to the second invocation. This creates a **nested function execution chain** where each link is independently validated.

### Agent Orchestration and Auto-Discovery

The `Needle` class automatically discovers available tools and handles chain execution:

```python
from needle import Needle

agent = Needle()
result = agent.run(
    "Please give me the title of https://example.com")

```

As implemented in **[`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)** (lines `102‑103`), the framework auto-discovers callables in the agent's namespace and attaches schemas even to undecorated functions by falling back to `build_schema(entry)`. The model determines the sequence of tool calls (`fetch` → `extract_title`), while Needle orchestrates the nested execution and manages data flow between steps.

## Schema Validation and Error Handling

**Pure Function Requirement**: All tool functions should be deterministic and free of side effects that could confuse the language model's reasoning process. The framework relies on consistent return values for reliable chain execution.

**Exception Safety**: If a tool in the chain raises an exception, Needle captures the error and returns a structured error payload to the model. This allows the language model to decide whether to retry the specific step or abort the entire chain.

**Optional Arguments**: The framework respects optional parameters and default values (detected via `_is_optional` and `Field.has_default`), allowing callers to safely omit parameters when invoking tools in a chain.

## Summary

- Needle uses the `@tool` decorator in **[`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)** to register functions as JSON-Schema-described tools that support multi-step tool calling chains.
- The `build_schema()` function (lines `111‑141`) generates validation schemas from type hints and docstrings, caching them on the function object to minimize runtime overhead.
- Nested function execution occurs naturally when tools call other tools; the dispatcher recursively validates arguments at each step using the type mapping utilities (`_json_type`, lines `57‑82`).
- The `Needle` agent auto-discovers tools ( **[`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)**, lines `102‑103`) and handles error propagation, allowing models to recover from failed steps in complex chains.

## Frequently Asked Questions

### How does Needle validate arguments in a nested tool chain?

Needle validates arguments at every invocation step using the JSON-Schema generated by `build_schema()`. When a parent tool calls a child tool, the dispatcher inspects the child tool's schema (stored on its `_needle_tool` attribute) and validates the provided arguments against required types and constraints before executing the function.

### Can undecorated functions participate in tool chains?

Yes. As implemented in **[`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)** (lines `102‑103`), the framework falls back to `build_schema(entry)` for callables discovered in the agent's namespace that lack the `@tool` decorator. This ensures any function can participate in chains, though explicit decoration is recommended for complex type signatures.

### What happens if a tool in the middle of a chain fails?

Needle catches exceptions raised during tool execution and returns a structured error payload to the language model rather than crashing the chain. The model can then analyze the error and decide whether to retry the failed step with different arguments or terminate the workflow.

### Does Needle support recursive tool calls?

Yes, recursion depth is limited only by Python's call stack and any agent-level safeguards (such as timeouts). The framework treats recursive tool calls identically to standard nested execution, applying schema validation at each recursive level according to the definitions in **[`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)**.