# How to Use the @needle.tool Decorator for Basic Tool Calling in Needle

> Learn to use the @needle.tool decorator to transform Python functions into JSON-schema tools for automatic invocation by Needle agents. Simplify tool calling.

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

---

**The `@needle.tool` decorator transforms a Python function into a JSON-schema tool definition that Needle agents automatically invoke by attaching the schema to the function's `_needle_tool` attribute.**

The `@needle.tool` decorator is the primary mechanism for registering Python functions as callable tools in the Needle framework. Located in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py), this decorator automatically generates JSON schemas from type hints and docstrings, enabling language models to discover and execute your code during agentic workflows.

## How the Decorator Generates Tool Schemas

When you apply `@needle.tool` to a function, the decorator internally calls `build_schema` to parse the function's signature and docstring. According to the source code in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py), it attaches the resulting JSON schema to the function object as the `_needle_tool` attribute (lines 62-65). This metadata includes parameter types, default values, and descriptions extracted from the docstring.

## Registering Tools with a Needle Agent

To make tools available to an agent, pass them in a list to the `tools` parameter when instantiating the `Needle` class. During initialization, defined in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 84-94), the framework walks the supplied list and extracts the attached schema from each function's `_needle_tool` attribute. It stores a mapping from the tool name to the callable for later execution.

```python
import needle

@needle.tool
def set_thermostat(temperature: int, mode: str = "auto"):
    """Set the thermostat.

    Args:
        temperature: Desired temperature in Celsius.
        mode: Heating mode – one of "heat", "cool", or "auto".
    """
    # In a real app this would issue a command to a device.

    return {"temperature": temperature, "mode": mode}

# Create an agent bound to the tool.

agent = needle.Needle(tools=[set_thermostat])

# Run a query; the model will decide to call the tool.

result = agent.run("Make it 21 °C and set cooling mode")
print(result["results"])

# → [{'temperature': 21, 'mode': 'cool'}]

```

## Runtime Tool Execution Flow

When the agent runs a query, the model receives the declared schemas and decides which tool to call. The model returns a response with a `function_calls` field containing the chosen tool name and arguments. As implemented in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 107-126), Needle looks up the callable via its internal mapping, executes the function with the provided arguments, feeds the result back to the model, and returns the aggregated results.

## Manual Tool Calling with complete()

For finer control over the execution loop, you can use the `complete()` method to handle tool calls manually. This approach lets you inspect the model's decision before invoking the function.

```python
response = agent.complete("Dim the living room lights to 30%")
if response["type"] == "call":
    # The model chose a tool; extract the call details.

    call = response["function_calls"][0]
    tool_fn = agent._functions[call["name"]]
    tool_result = tool_fn(**call["arguments"])
    # Feed the result back to continue the dialogue.

    next_resp = agent.complete(json.dumps([tool_result]))

```

## Summary

- **Schema Generation**: The `@needle.tool` decorator uses `build_schema` in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) to create JSON schemas from type hints and docstrings, storing them in the `_needle_tool` attribute.
- **Tool Registration**: The `Needle` class scans the `tools` list during initialization ([`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), lines 84-94) to build an internal mapping of callable functions.
- **Automatic Execution**: During runs, the framework handles the full loop: parsing `function_calls` responses, executing matched functions, and returning results to the model (lines 107-126).
- **Flexibility**: While the decorator provides a type-safe interface using Python-native syntax, you can also supply raw JSON schemas directly if needed.

## Frequently Asked Questions

### How does @needle.tool convert functions to schemas?

The decorator calls the internal `build_schema` function in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) to parse Python type hints and Google-style docstrings. It stores the resulting JSON schema in the function's `_needle_tool` attribute (lines 62-65), making the function self-describing for the Needle framework.

### Can I register tools without using the decorator?

Yes. While `@needle.tool` offers a concise, type-safe approach, you can supply raw JSON schemas directly to the agent. The decorator is a convenience wrapper that leverages Python's native type system and documentation conventions to generate these schemas automatically.

### How does Needle determine which tool to execute?

During initialization in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (lines 84-94), the framework creates a mapping from tool names to callables using the extracted schemas. When the model returns a `function_calls` response during execution (lines 107-126), Needle looks up the function by name in this mapping and invokes it with the model-supplied arguments.

### What happens to the return value of a tool function?

Needle captures the return value, serializes it, and automatically feeds it back into the conversation context. This allows the model to observe the result and either request additional tool calls or generate a final response based on the execution output.