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.tooldecorator is defined inneedle/agent/tools.py(lines 168–210) and serves as the entry point for tool registration. - Global registration occurs at decoration time, populating the
TOOL_REGISTRYdictionary 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →