How to Declare Tools in Needle 2: 3 Methods Explained

In Needle 2, you declare tools using the @needle.tool decorator on Python functions, passing Pydantic models, or providing raw JSON schemas, then supply them to the Needle constructor via the tools parameter.

The cactus-compute/needle repository provides a lightweight framework for building conversational AI agents. Declaring tools in Needle 2 follows a two-step pattern where you first define the tool schema and then register it with the agent.

Using the @needle.tool Decorator

The primary method for declaring tools involves applying the @needle.tool decorator to Python functions. According to the source code in needle/agent/tools.py (lines 68-71), this decorator introspects function signatures, type hints, and docstrings to automatically generate JSON Schema definitions.

Converting Functions to Tool Schemas

When you decorate a function with @needle.tool, the framework inspects the callable and stores the generated schema as fn._needle_tool. The decorator extracts parameter types from annotations and descriptions from the docstring's Args section.

@needle.tool
def set_thermostat(temperature: int,
                   mode: Literal["heat", "cool", "auto"] = "auto"):
    """Set the thermostat.

    Args:
        temperature: target temperature in Celsius
        mode: heating strategy to use
    """
    return {"temperature": temperature, "mode": mode}

Registering Tools with the Agent

After decorating your functions, pass them to the Needle constructor using the tools parameter. As documented in doc/apis.md (line 5), the constructor accepts a list of tool definitions.

agent = needle.Needle(tools=[set_thermostat])
agent.run("make it 21 and cool the room")

Alternative Declaration Methods

Needle 2 supports three alternative approaches for declaring tools beyond the standard decorator pattern.

Direct JSON Schema

You can bypass Python function definitions entirely by constructing raw JSON Schema dictionaries. This method is documented in doc/apis.md (lines 51-68) and passed directly to the tools parameter.

tools = [{
    "name": "set_lights",
    "description": "Turn a room's lights on/off and set brightness",
    "parameters": {
        "type": "object",
        "properties": {
            "room": {"type": "string", "description": "room name"},
            "on": {"type": "boolean"},
            "brightness": {"type": "integer", "minimum": 0, "maximum": 100},
        },
        "required": ["room", "on"],
    },
}]

agent = needle.Needle(tools=tools)
agent.run("Dim the living room lights to 30%")

Pydantic Models

For complex validation scenarios, define tools using Pydantic models. The framework extracts JSON schemas from BaseModel subclasses via the pydantic_schema function in needle/agent/tools.py.

Field Constraints with typing.Annotated

Use typing.Annotated combined with needle.Field to specify per-argument validation rules such as numeric ranges, regex patterns, and string lengths. This approach is detailed in doc/apis.md (lines 34-48).

from typing import Annotated

@needle.tool
def send_money(
    amount: Annotated[float, needle.Field(gt=0, le=10000,
                                         description="USD, up to 10,000")],
    to:     Annotated[str,   needle.Field(pattern=r"^@[a-z0-9_]+$",
                                         description="recipient handle")],
    memo:   Annotated[str,   needle.Field(max_length=80)] = "",
):
    """Send money to a handle."""
    return {"sent": amount, "to": to}

agent = needle.Needle(tools=[send_money])
agent.run("Send $25 to @bob")

Complete Working Examples

Here are runnable patterns demonstrating each declaration style:

Simple Function Tool:

@needle.tool
def greet(name: str):
    """Say hello to someone."""
    return f"Hello, {name}!"

agent = needle.Needle(tools=[greet])
response = agent.run("Hey, can you greet Alice?")

Summary

  • The @needle.tool decorator (implemented in needle/agent/tools.py) automatically generates JSON schemas from Python function signatures and docstrings
  • Tool registration requires passing decorated functions to the Needle constructor via the tools parameter
  • Three declaration methods are supported: decorated functions, raw JSON schemas, and Pydantic models
  • Validation constraints can be added using typing.Annotated with needle.Field for granular control over argument validation

Frequently Asked Questions

What file contains the tool decorator implementation?

The @needle.tool decorator is implemented in needle/agent/tools.py (lines 68-71). This module handles function introspection, schema generation, and the Field constraint helper used with typing.Annotated.

Can I use Pydantic models instead of functions to declare tools?

Yes. Needle 2 accepts Pydantic BaseModel classes as tools. The framework uses the pydantic_schema function in needle/agent/tools.py to extract JSON schemas from model definitions, providing an alternative to the decorator-based approach for complex data structures.

How do I add validation constraints to tool arguments?

Use typing.Annotated combined with needle.Field to specify constraints like numeric ranges (gt, le), regex patterns (pattern), or length limits (max_length). As shown in doc/apis.md (lines 34-48), these annotations integrate directly with the @needle.tool decorator to enforce validation at the schema level.

Does Needle 2 support raw JSON schema definitions?

Yes. You can pass raw JSON Schema dictionaries directly to the Needle constructor via the tools parameter. This approach, documented in doc/apis.md (lines 51-68), allows integration with external schema definitions or languages other than Python.

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 →