How to Declare Tools Using the @needle.tool Decorator in Needle

The @needle.tool decorator registers Python functions as LLM-invokable tools by automatically generating OpenAI-compatible JSON schemas from type hints and docstrings.

Needle is an open-source framework for building LLM agents that interact with physical and virtual environments. Declaring tools using the @needle.tool decorator is the primary mechanism for exposing Python callables to the agent runtime, enabling automatic schema validation and function calling without manual JSON schema authoring.

How the @needle.tool Decorator Works

When you apply @needle.tool to a function, Needle performs a three-step registration process defined in needle/agent/tools.py:

  1. Attribute tagging – The decorator attaches a _needle_tool attribute to the function object at line 73.
  2. Schema analysis – The build_schema(fn) utility inspects the function signature, type hints, and docstring to construct an OpenAI-compatible JSON schema.
  3. Schema storage – The generated schema is stored within the _needle_tool attribute, making the tool discoverable by the agent runtime.

The resulting schema captures parameter types, required fields, and the function description, allowing the LLM to understand how to invoke the tool and validate arguments automatically.

Declaring Tools: Code Examples

Basic Tool with Primitive Arguments

For simple use cases, define a function with type-hinted primitive arguments and a descriptive docstring:

from needle.agent.tools import needle

@needle.tool
def turn_on_light(room: str) -> str:
    """Turn on the light in the specified room."""
    return f"Light in {room} turned on."

The decorator extracts the room parameter as a required string and uses the docstring as the tool description.

Tool with Pydantic Input Models

For complex inputs, use Pydantic models to define structured arguments:

from pydantic import BaseModel
from needle.agent.tools import needle

class LightCommand(BaseModel):
    """Command to control a light."""
    room: str
    intensity: int = 100

@needle.tool
def set_light(cmd: LightCommand) -> dict:
    """Set the light intensity for a given room."""
    return {"room": cmd.room, "intensity": cmd.intensity}

Needle detects the LightCommand model and extracts its JSON schema via Pydantic's schema generation, including default values in the tool definition.

Tool Returning Pydantic Models

You can also type-hint return values with Pydantic models to provide structured response schemas:

class LightStatus(BaseModel):
    """Current status of a light."""
    room: str
    on: bool
    intensity: int

@needle.tool
def get_light_status(room: str) -> LightStatus:
    """Retrieve the current status of a light."""
    return LightStatus(room=room, on=True, intensity=75)

The return model's schema is captured in build_schema, enabling the agent to parse and validate the tool's output structure.

Key Source Files

Understanding the implementation details requires examining these specific files in the cactus-compute/needle repository:

Summary

  • Import path: Access the decorator via from needle.agent.tools import needle and apply as @needle.tool.
  • Schema generation: The decorator automatically creates OpenAI-compatible JSON schemas by analyzing function signatures and docstrings through build_schema.
  • Registration mechanism: Tools are marked with a _needle_tool attribute containing the generated schema, making them discoverable by the Needle agent runtime.
  • Type support: The decorator handles primitive types and Pydantic models for both arguments and return values, enabling automatic validation.

Frequently Asked Questions

What types of function arguments does @needle.tool support?

The decorator supports JSON-serializable primitives (strings, integers, booleans) and Pydantic models. As implemented in needle/agent/tools.py, the build_schema function extracts type information from standard Python type hints and Pydantic BaseModel classes to construct the tool's parameter schema.

How does the decorator use function docstrings?

The docstring becomes the tool's description in the generated OpenAI-compatible schema. When build_schema analyzes the function, it extracts the docstring to populate the description field, which the LLM uses to understand when and how to invoke the tool.

Can I declare optional parameters with default values?

Yes. The schema generation logic in tools.py inspects function signatures to identify parameters with default values. When using Pydantic models, fields with default values are marked as optional in the JSON schema, while required fields must be provided by the LLM during tool invocation.

Where can I find complete tool declaration examples?

The repository includes reference implementations in needle/environments/smart_home.py and needle/environments/wearable.py. These files demonstrate production-ready tool patterns using @needle.tool with various input types and return structures.

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 →