How the `@needle.tool` Decorator Works: Registering Python Functions as LLM Tools

The @needle.tool decorator transforms regular Python functions into LLM-callable tools by registering them in a global registry, generating JSON-serializable schemas, and wrapping them for runtime execution within the Needle agent framework.

The @needle.tool decorator serves as the primary integration point for the cactus-compute/needle agent architecture. When applied to any Python callable, it marks that function as an available operation that large language models can invoke during multi-step reasoning, automatically handling serialization, validation, and registry management.

Core Mechanisms of the @needle.tool Decorator

The decorator performs three critical actions at import time to bridge the gap between Python functions and LLM tool-use protocols.

Global Tool Registration

When @needle.tool is applied to a function, the decorator immediately adds the callable to Needle’s internal tools dictionary (exposed as TOOL_REGISTRY in the public API). This global registry maps function names to their corresponding callables, allowing the Agent class to look up and execute tools by name when parsing model outputs.

JSON Schema Generation

The decorator introspects the function signature—extracting parameter names, type hints, and docstrings—to construct a standardized JSON schema. This schema is sent to the LLM as part of the system prompt, informing the model exactly what operations are available and how to structure its tool-call arguments.

Runtime Function Wrapping

The original function is wrapped in a lightweight shim that performs minimal pre-flight checks and normalizes return values. This ensures that whether the tool returns a string, integer, or complex object, the output is safely serializable for injection back into the LLM's context window.

Implementation in needle/agent/tools.py

The core logic resides in needle/agent/tools.py between lines 168 and 210. Here, the tool function acts as both the decorator and the registration engine:


# Conceptual structure based on needle/agent/tools.py#L168-L210

from typing import Callable
import functools

def tool(func: Callable) -> Callable:
    """Decorator to register a function as a Needle agent tool."""
    # Register in global lookup

    TOOL_REGISTRY[func.__name__] = func
    
    # Generate and store JSON schema from type hints/docstring

    _schemas[func.__name__] = _build_schema(func)
    
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # Runtime validation and execution logic

        result = func(*args, **kwargs)
        return _normalize_output(result)
    
    return wrapper

According to the cactus-compute/needle source code, this implementation ensures that decorated functions are available for automatic discovery by the Agent class without requiring manual registration steps.

Practical Usage Examples

Basic Tool Definition

Define a tool by importing the decorator and applying it to any function with type hints:

from needle.agent.tools import tool

@tool
def search_web(query: str) -> str:
    """Perform a web search and return the top result."""
    # Implementation details omitted

    return f"Results for: {query}"

@tool
def calculate_sum(a: int, b: int) -> int:
    """Return the sum of two integers."""
    return a + b

Integrating with an Agent

Once decorated, tools can be passed to an Agent instance or automatically discovered if the module is imported:

from needle.agent import Agent

agent = Agent()
agent.add_tool(search_web)  # Explicit addition

agent.add_tool(calculate_sum)

# The LLM may output: {"name": "calculate_sum", "arguments": {"a": 5, "b": 3}}

# The agent executes the function and feeds "8" back into the context

Inspecting the Tool Registry

You can view all registered tools by accessing the global registry directly:

from needle.agent.tools import TOOL_REGISTRY

print(TOOL_REGISTRY.keys())

# Output: dict_keys(['search_web', 'calculate_sum', ...])

# Access the raw function

sum_func = TOOL_REGISTRY['calculate_sum']
result = sum_func(10, 20)  # Returns 30

Summary

  • The @needle.tool decorator is defined in needle/agent/tools.py (lines 168–210) and serves as the entry point for tool registration.
  • Global registration occurs at decoration time, populating the TOOL_REGISTRY dictionary used by the Agent to resolve tool calls.
  • Schema generation automatically creates LLM-compatible descriptions from Python type hints and docstrings.
  • Runtime wrapping ensures consistent output formatting and basic validation when the LLM invokes the tool.
  • No manual registration is required beyond applying the decorator; functions become immediately available to any Needle agent instance.

Frequently Asked Questions

Where is the @needle.tool decorator implemented?

The decorator is implemented in needle/agent/tools.py starting at line 168. This file contains the tool function definition, the global TOOL_REGISTRY dictionary, and the schema generation logic used to expose Python functions to the LLM.

How does the decorator generate the tool schema?

The decorator introspects the function’s __annotations__ and __doc__ attributes to build a JSON-serializable description. As implemented in cactus-compute/needle, it extracts parameter types from type hints and the description from the docstring, then stores this metadata in an internal schemas dictionary for later inclusion in system prompts.

Can I register a tool manually without using the decorator?

Yes. While the decorator is the idiomatic approach, you can manually add functions to the registry by importing TOOL_REGISTRY from needle.agent.tools and assigning the function: TOOL_REGISTRY['my_tool'] = my_function. However, this bypasses automatic schema generation and wrapping, requiring you to manually ensure the function conforms to the expected interface.

What happens when the LLM calls a registered tool?

When the agent parses a tool-use request from the model’s output, it looks up the function name in TOOL_REGISTRY, validates the provided arguments against the stored schema, executes the wrapped function, and serializes the return value for re-injection into the LLM’s context window.

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 →