How to Declare a Tool Using the `@needle.tool` Decorator in Needle
Use the @needle.tool decorator on any Python function with type hints to register it as an LLM-exposed tool that includes automatic JSON-schema generation.
The Needle framework provides a lightweight decorator-based system for turning ordinary Python functions into tools that LLM agents can invoke. By applying @needle.tool to your function, you enable automatic schema generation, runtime discovery, and first-class integration with the agent's tool-calling API—all without manual boilerplate.
How the @needle.tool Decorator Works
The decorator performs three key operations at decoration time, as implemented in needle/agent/tools.py (line 169 and surrounding code):
-
Wraps the function – creates a thin wrapper that forwards all arguments unchanged to the original callable.
-
Generates a JSON schema – the internal
build_schema(fn)function inspects Python type hints and the docstring to produce a structured schema describing parameters, return types, and descriptions. -
Stores metadata – attaches the schema to the function as
_needle_tool, a hidden attribute that the runtime uses for discovery.
At agent startup, needle/__init__.py (line 109) scans for all callables with the _needle_tool attribute and registers them with the LLM's tool-calling interface.
Declaring Your First Tool
Basic Function Decoration
Any function with type-annotated parameters and a docstring can become a tool:
import needle
@needle.tool
def greet(name: str) -> str:
"""Return a friendly greeting."""
return f"Hello, {name}!"
The decorator immediately attaches the generated schema:
>>> greet._needle_tool
{
"name": "greet",
"description": "Return a friendly greeting.",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Name of the person to greet"}
},
"required": ["name"]
}
}
Tools with Optional Parameters
Default values are automatically captured in the schema:
@needle.tool
def get_weather(city: str, unit: str = "celsius") -> dict:
"""Fetch current weather for *city*."""
# API implementation here
return {"city": city, "temp": 22, "unit": unit}
The generated schema marks unit as non-required and includes its default, letting the LLM understand when parameters can be omitted.
Real-World Usage in Environments
Built-in Needle environments demonstrate @needle.tool in practice. In needle/environments/smart_home.py, tools expose device control capabilities:
# needle/environments/smart_home.py
@needle.tool
def turn_on_light(room: str) -> str:
"""Turn on the light in the specified room."""
return f"Light in {room} turned on."
These environment tools are automatically collected into the TOOLS list at import time (verified in tests/test_environments.py, line 17), making them available to any agent running in that environment context.
Schema Generation Details
The build_schema function (in needle/agent/tools.py, line 169) handles:
- Type mapping – converts Python types (
str,int,float,bool,list,dict) to JSON Schema types. - Docstring parsing – extracts parameter descriptions from Google-style or plain docstrings.
- Return type annotation – captures the return type for LLM context, though currently informational.
The decorator returns the original function object, so decorated tools remain fully callable in standard Python code:
# Direct Python call works normally
result = greet("Alice") # "Hello, Alice!"
# LLM call uses the same function via tool-calling API
Key Source Files
| File | Purpose |
|---|---|
needle/agent/tools.py |
Core @needle.tool decorator and build_schema implementation (line 169) |
needle/__init__.py |
Runtime tool discovery via _needle_tool attribute scanning (line 109) |
needle/environments/*.py |
Production examples: smart_home.py, wearable.py |
tests/test_tools.py |
Unit tests validating schema generation (line 20) |
Summary
- Apply
@needle.toolto any type-annotated function to register it as an LLM tool. - The decorator automatically generates JSON schemas from type hints and docstrings, storing them in
_needle_tool. - Registered tools are discovered at runtime in
needle/__init__.pyand exposed through the agent's tool-calling API. - Decorated functions remain ordinary Python callables—no runtime overhead or calling convention changes.
Frequently Asked Questions
What happens if I forget type hints?
The build_schema function in needle/agent/tools.py relies on inspect.get_annotations to extract type information. Without hints, parameter types default to any in the JSON schema, reducing the LLM's ability to generate correct call arguments. Always include type hints for reliable tool behavior.
Can I use @needle.tool on class methods?
Currently, the decorator is designed for module-level functions. For class-based tools, define them as static methods or module functions that accept the instance state as an explicit parameter. The test suite in tests/test_tools.py focuses on function-based usage.
How do I inspect a tool's schema after decoration?
Access the _needle_tool attribute directly on the decorated function: my_function._needle_tool. This returns the complete JSON schema dictionary that the LLM receives during tool-calling negotiations.
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 →