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

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. 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:


# 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:

@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:

from needle import Needle

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

As implemented in 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 (fetchextract_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 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, 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 (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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →