How to Define Tools Using Raw JSON Schema in Needle
Attach a correctly structured dictionary to your function's _needle_tool attribute to supply a raw JSON schema, or use the @tool decorator to auto-generate one from type hints.
Needle, an open-source LLM orchestration framework from cactus-compute/needle, enables developers to expose Python functions as callable tools for language models. While the library automatically generates JSON schemas from function signatures and docstrings, you can also define tools using raw JSON schema when you need precise control over parameter definitions or want to support complex validation rules that automatic introspection cannot capture.
How Needle Discovers Tool Schemas
When initializing a Needle instance with a list of tools, the engine inspects each callable for a _needle_tool attribute. According to the source code in needle/__init__.py (around line 210), the initialization logic first attempts to load entry._needle_tool. If the attribute exists, Needle uses the dictionary verbatim as the tool's JSON schema definition. If absent, the engine falls back to build_schema(entry), which is implemented in needle/agent/tools.py to automatically introspect the function.
This dual-path architecture allows you to choose between convenience (automatic generation) and precision (manual schema definition) on a per-tool basis.
Approaches to Schema Definition
Automatic Generation with @tool
The @tool decorator, defined in needle/agent/tools.py, wraps Python functions and populates the _needle_tool attribute automatically. It inspects parameter types via typing hints, parses docstrings for descriptions, and constructs a complete JSON schema object. This approach is ideal when your functions use standard Python types and clear documentation.
Raw JSON Schema Definition
To bypass automatic introspection entirely, manually assign a dictionary to the _needle_tool attribute before passing the function to the Needle class. This dictionary must conform to the JSON Schema specification, containing keys such as name, description, and parameters with proper type definitions and required field arrays.
Implementation Examples
The following examples demonstrate both automatic and manual schema definition patterns.
Example 1: Automatic Schema with @tool
This approach uses the decorator to generate the schema from type hints and docstrings.
from needle.agent.tools import tool
from needle import Needle
@tool # Automatically creates _needle_tool attribute
def greet(name: str, *, polite: bool = False) -> str:
"""Say hello to *name*.
Parameters
----------
name: The person to greet.
polite: Whether to use a formal greeting.
"""
return f"{'Good day' if polite else 'Hey'} {name}!"
# Pass the function to the engine
engine = Needle(tools=[greet])
Example 2: Raw JSON Schema Definition
Here we manually construct the schema dictionary and attach it to the function.
from needle import Needle
def add(a: int, b: int) -> int:
"""Return the sum of *a* and *b*."""
return a + b
# Attach a raw schema that matches the function signature
add._needle_tool = {
"name": "add",
"description": "Add two integers and return the result.",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "integer", "description": "First operand"},
"b": {"type": "integer", "description": "Second operand"}
},
"required": ["a", "b"]
}
}
# The engine will use this schema verbatim
engine = Needle(tools=[add])
Example 3: Mixing Auto-Generated and Manual Schemas
You can combine both approaches in a single tool list.
from needle.agent.tools import tool
from needle import Needle
def echo(message: str) -> str:
"""Return the received *message* unchanged."""
return message
# Use automatic schema generation for echo
echo = tool(echo)
# Provide custom schema for multiply
def multiply(x: int, y: int) -> int:
return x * y
multiply._needle_tool = {
"name": "multiply",
"description": "Multiply two integers.",
"parameters": {
"type": "object",
"properties": {
"x": {"type": "integer"},
"y": {"type": "integer"}
},
"required": ["x", "y"]
}
}
engine = Needle(tools=[echo, multiply])
Summary
- Dual registration paths: Needle checks for
_needle_toolinneedle/__init__.pybefore falling back tobuild_schemafromneedle/agent/tools.py. - Automatic generation: The
@tooldecorator introspects functions and stores generated schemas on the_needle_toolattribute. - Manual control: Assign a raw JSON schema dictionary directly to
_needle_toolto override automatic generation. - Flexible mixing: Both auto-generated and manually defined tools can coexist in the same
Needleinstance. - Schema structure: Raw schemas require
name,description, andparameterskeys conforming to JSON Schema standards.
Frequently Asked Questions
What is the _needle_tool attribute in Needle?
The _needle_tool attribute is a dictionary attached to callable objects that contains the raw JSON schema definition. When present, Needle uses this schema directly instead of calling build_schema to introspect the function. According to the source code in needle/__init__.py, this check occurs during engine initialization to determine whether to use the provided schema or generate one automatically.
Can I mix auto-generated and manual schemas in the same Needle instance?
Yes. The Needle class accepts a list of tools where some functions use the @tool decorator for auto-generated schemas while others have manually assigned _needle_tool attributes. The engine handles both types seamlessly during initialization, making it possible to combine convenience and precision in the same application.
Where does Needle check for custom tool schemas?
The check occurs in needle/__init__.py at approximately line 210, where the initialization code attempts to access entry._needle_tool. If the attribute exists, the engine uses it verbatim; otherwise, it invokes build_schema(entry) from needle/agent/tools.py to generate the schema from the function's signature and type hints.
What structure must a raw JSON schema follow in Needle?
The _needle_tool dictionary must include name (string identifier), description (string explanation), and parameters (object containing JSON Schema definitions). The parameters object should specify type (typically "object"), properties (mapping parameter names to their schemas), and required (array of mandatory parameter names) according to the JSON Schema specification.
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 →