How to Pass a Raw JSON Schema for Tool Definition in Needle 2
You can pass a raw JSON schema to Needle 2 either by attaching it to a function’s _needle_tool attribute or by providing a dictionary directly to the tools= argument in Agent or needle.run().
Needle 2 automatically constructs tool definitions from Python type hints and docstrings via the build_schema utility. When you need granular control over parameter validation, descriptions, or complex nested types, you can pass a raw JSON schema for tool definition in Needle 2 to override this automatic generation.
Method 1: Attach a Raw Schema to a Function
The most common approach is to manually set the _needle_tool attribute on your function object. This attribute expects a dictionary that follows the OpenAI function-calling specification.
According to the source code in needle/agent/tools.py, the standard @tool decorator normally executes fn._needle_tool = build_schema(fn) at line 169 to store the generated schema on the function object. By setting this attribute yourself before the decorator runs—or by overriding it afterward—you force Needle to use your custom schema verbatim instead of building one from type hints.
When Needle discovers tools via env.TOOLS or the run API, it checks for this attribute first. If _needle_tool exists, Needle transmits that schema directly to the LLM without calling build_schema.
# Attach a raw schema to a function
def greet(name: str) -> str:
"""Return a friendly greeting."""
return f"Hello, {name}!"
# Manually override the automatically generated schema
greet._needle_tool = {
"name": "greet",
"description": "Greet a user by name.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The user's name"
}
},
"required": ["name"]
}
}
# The function can now be used like any other Needle tool
from needle import Agent
agent = Agent(tools=[greet])
response = agent.run("Say hi to Alice.")
print(response) # → "Hello, Alice!"
Method 2: Pass a Raw Schema Directly to the Runtime
Alternatively, you can skip the function-attachment step entirely and pass raw schema dictionaries directly to the runtime via the tools= parameter. Both needle.run() and the Agent class accept a list that may contain a mix of callable functions and plain dictionaries.
When Needle encounters a dictionary in the tools list, it bypasses the build_schema step and forwards the JSON-Schema straight to the LLM. This logic is handled in needle/__init__.py, where the entry point checks for the _needle_tool attribute (line 109) and falls back to build_schema only if the attribute is missing. Since raw dictionaries have no such attribute, they flow through unchanged.
# Supply a raw schema dict directly to the runtime
raw_schema = {
"name": "add_numbers",
"description": "Add two integers and return the sum.",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "integer", "description": "First addend"},
"b": {"type": "integer", "description": "Second addend"}
},
"required": ["a", "b"]
}
}
def add_numbers(a: int, b: int) -> int:
return a + b
from needle import Agent
agent = Agent(tools=[raw_schema]) # no need for a Python function here
response = agent.run("What is 3 plus 7?")
print(response) # → "10"
How Needle Resolves Tool Definitions
Understanding the resolution order helps you decide which method to use:
- Dictionary check: If an item in the
toolslist is adict, Needle uses it verbatim. - Attribute check: If a callable has the
_needle_toolattribute (as set inneedle/agent/tools.pyat line 169), Needle uses that value. - Fallback generation: Otherwise, Needle calls
build_schema(fn), defined inneedle/agent/tools.py(lines 15–46), to infer the schema from type hints and docstrings.
The build_schema function relies on _json_type to map Python types to JSON Schema types. By supplying a raw schema, you circumvent this entire inference chain.
Summary
- Attach to function: Set
func._needle_tool = {...}to override auto-generation for that specific callable. Ideal when you want to keep the Python function signature but customize the LLM-facing schema. - Pass directly: Include raw dictionaries in the
tools=list when you don’t need a corresponding Python function or when defining tools dynamically. - Source locations: The schema storage logic resides in
needle/agent/tools.py(line 169), while fallback behavior is handled inneedle/__init__.py(line 109). - Validation: Needle does not validate raw schemas; ensure your dictionary conforms to the OpenAI function-calling specification before runtime.
Frequently Asked Questions
What format must the raw JSON schema follow?
The dictionary must conform to the OpenAI function-calling schema specification. It requires a name (string), description (string), and a parameters object containing type, properties, and required arrays. Needle passes this structure directly to the LLM without modification.
Can I mix raw schemas and Python functions in the same agent?
Yes. The tools= argument accepts a heterogeneous list. You can include functions with auto-generated schemas, functions with manually attached _needle_tool attributes, and raw dictionaries side by side. Needle processes each item according to the resolution order documented above.
Does Needle validate custom schemas before sending them to the LLM?
No. When you provide a raw schema, Needle forwards it verbatim to the underlying LLM provider. Validation against the JSON Schema specification or the provider’s requirements is your responsibility. The test suite in tests/test_tools.py (line 20) demonstrates how the _needle_tool attribute is accessed, but does not enforce schema compliance.
Where is the automatic schema generation implemented?
The build_schema function and the _json_type helper are implemented in needle/agent/tools.py between lines 15 and 46. This module also contains the @tool decorator logic that normally populates the _needle_tool attribute at line 169.
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 →