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_tool in needle/__init__.py before falling back to build_schema from needle/agent/tools.py.
  • Automatic generation: The @tool decorator introspects functions and stores generated schemas on the _needle_tool attribute.
  • Manual control: Assign a raw JSON schema dictionary directly to _needle_tool to override automatic generation.
  • Flexible mixing: Both auto-generated and manually defined tools can coexist in the same Needle instance.
  • Schema structure: Raw schemas require name, description, and parameters keys 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:

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 →