How to Use Raw JSON Schema for Tool Definitions in Needle 2

Needle 2 accepts hand-written JSON schemas directly via the tools parameter, bypassing the @needle.tool decorator while maintaining the same validation and grammar-constrained decoding pipeline.

While Needle 2 typically discovers tools through the @needle.tool decorator—which internally calls build_schema in needle/agent/tools.py to inspect function signatures—you can supply raw JSON schema definitions directly. This is essential when integrating tools that live outside your codebase, generating schemas dynamically, or requiring explicit control over parameter descriptions and constraints.

Structure of a Valid Tool Schema

A raw JSON schema passed to needle.Needle must match the structure produced by build_schema in [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L10-L41). The schema requires three top-level keys:

  • name: The string identifier the model uses to invoke the function.
  • description: Optional human-readable text shown to the model to guide tool selection.
  • parameters: A JSON Schema object containing type: "object", a properties dictionary mapping argument names to their schemas, and an optional required array listing mandatory parameters.

Inside each property definition, you can specify standard JSON Schema types (string, integer, boolean, etc.) and validation constraints equivalent to needle.Field annotations, including description, enum, gt, lt, pattern, and minLength.

Defining Tools with Raw JSON

You can provide schemas as inline Python dictionaries, load them from external files, or mix them with decorator-generated definitions.

Inline Dictionary Definition

Define the schema directly in Python code as a list of dictionaries. This pattern appears in the official documentation at [doc/apis.md](https://github.com/cactus-compute/needle/blob/main/doc/apis.md#L51-L68):

import needle

tools = [{
    "name": "set_lights",
    "description": "Turn a room's lights on or off and set brightness",
    "parameters": {
        "type": "object",
        "properties": {
            "room": {"type": "string", "description": "which room to control"},
            "on": {"type": "boolean"},
            "brightness": {"type": "integer", "minimum": 0, "maximum": 100},
        },
        "required": ["room", "on"],
    },
}]

agent = needle.Needle(tools=tools)
agent.run("turn the kitchen lights on at 70%")

The schema is passed directly to the Needle constructor, bypassing the decorator entirely while using the same internal grammar-generation pipeline.

Loading from External JSON Files

For large tool catalogs, store schemas in a JSON file and load them at runtime:

import json
import needle

with open("tools.json") as f:
    schema = json.load(f)

agent = needle.Needle(tools=schema)
print(agent.run("dim the living room lights to 30"))

The tools.json file must contain an array of schema objects identical in structure to the inline example above.

Combining Manual and Decorator-Generated Schemas

Needle 2 allows mixing automatically generated schemas with hand-written ones. The decorator stores the generated schema in the _needle_tool attribute of the decorated function (as implemented in [needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L62-L65)):

@needle.tool
def send_money(amount: float, to: str):
    """Send money to a user."""
    return {"sent": amount, "to": to}

# Hand-written schema for a second tool

extra = [{
    "name": "set_thermostat",
    "description": "Set the thermostat temperature.",
    "parameters": {
        "type": "object",
        "properties": {
            "temperature": {"type": "integer"},
            "mode": {"type": "string", "enum": ["heat", "cool", "auto"]},
        },
        "required": ["temperature"],
    },
}]

# Merge decorator-generated and manual schemas

all_tools = [send_money._needle_tool] + extra
agent = needle.Needle(tools=all_tools)

This hybrid approach lets you extend the agent with external tool definitions while keeping native Python functions for internal logic.

Validation Constraints in Raw Schemas

When writing raw JSON schemas, you can enforce the same validation rules that needle.Field provides. The parameters.properties dictionary supports standard JSON Schema validation keywords:

  • enum: Restrict values to a specific set.
  • minimum, maximum, exclusiveMinimum, exclusiveMaximum: Numeric range constraints (equivalent to gt, lt).
  • minLength, maxLength: String length constraints.
  • pattern: Regular expression validation for strings.

These constraints are evaluated during the agent's validation phase, ensuring that tool arguments meet your specifications before execution.

Summary

  • Raw JSON schemas bypass the @needle.tool decorator while maintaining full compatibility with Needle 2's validation and grammar-constrained decoding.
  • Required structure: Each tool requires name, optional description, and parameters containing type: "object", properties, and optional required.
  • Storage location: Decorated functions store their generated schema in fn._needle_tool, allowing manual mixing with external definitions.
  • Validation support: Raw schemas support the same constraints as needle.Field, including enum, range limits, and pattern matching.
  • Source files: Core logic resides in needle/agent/tools.py (lines 10-41 and 62-65), with documentation examples in doc/apis.md (lines 51-68).

Frequently Asked Questions

What is the exact structure required for a raw JSON tool schema in Needle 2?

A valid schema must be a dictionary containing name (string), optional description (string), and parameters (object). The parameters object must declare type: "object" and contain a properties dictionary mapping argument names to their type definitions, plus an optional required array listing mandatory arguments. This structure mirrors the output of build_schema in needle/agent/tools.py.

Can I mix manually written schemas with the @needle.tool decorator?

Yes. Access the decorator-generated schema via the _needle_tool attribute on the decorated function, then combine it with manual schemas in a single list passed to needle.Needle(tools=...). The agent processes both sources through the identical validation pipeline.

Where does Needle 2 store the automatically generated schema on decorated functions?

The @needle.tool decorator attaches the generated schema to the function object under the attribute _needle_tool. You can access this attribute to retrieve the JSON schema dictionary for inspection or manual merging, as implemented in lines 62-65 of needle/agent/tools.py.

Does using a raw JSON schema affect grammar-constrained decoding or validation?

No. Needle 2 treats manually supplied schemas exactly like automatically generated ones. Both feed into the same grammar-generation and validation pipeline defined in the agent's tool processing logic, ensuring consistent confidence-gated execution regardless of how the schema was created.

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 →