# Needle JSON Schema Support: Using Raw JSON Schemas Instead of Python Functions

> Discover how Needle supports raw JSON schemas bypassing Python function introspection. Simply pass a JSON-encoded string to the tools parameter for direct schema usage.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Yes, Needle supports raw JSON schemas directly—pass a JSON-encoded string to the `tools` parameter and it bypasses Python function introspection entirely.**

The `cactus-compute/needle` repository provides a flexible tool-calling framework designed for LLM agents. While Python functions with type annotations are the most common input format, the architecture explicitly allows you to supply pre-built JSON schemas instead. This enables integration with external API specifications, hand-written OpenAI-compatible definitions, or schemas generated from other sources.

---

## How Needle Handles the Tools Parameter

The entry point for tool definition occurs in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py). The primary `Needle` class constructor accepts a `tools` argument that can be either:

- A **string** containing valid JSON (raw schema mode)
- A **Python object** (callable or collection of callables)

The constructor logic distinguishes between these cases:

```python
tools_json = tools if isinstance(tools, str) else json.dumps(self._resolve(tools))

```

When `tools` is already a string, Needle uses it verbatim without transformation. Otherwise, it invokes `_resolve()` to convert Python callables into equivalent JSON schema definitions.

---

## Submitting Raw JSON Schemas to Needle

### Basic Usage with a JSON String

Define your tools as a JSON array following the standard tool-calling format, then pass the serialized result:

```python
import json
from needle import Needle

raw_schema = [
    {
        "name": "search",
        "description": "Search the web for a query",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "The search query"
                }
            },
            "required": ["query"]
        }
    }
]

needle = Needle(tools=json.dumps(raw_schema))

```

The `json.dumps()` call produces the exact string format the constructor expects. Since the input is a string, `_resolve()` is skipped entirely.

### Loading Schemas from External Files

For schemas maintained in separate files—common when versioning tool definitions or sharing specifications across services:

```python
import json
from needle import Needle

with open("openapi_tools.json", "r", encoding="utf-8") as f:
    tools_json = f.read()

needle = Needle(tools=tools_json)

```

This pattern works with any JSON source: OpenAPI specifications, manually curated schemas, or exports from other framework.

---

## Mixing Raw JSON with Python Functions

Needle's `_resolve()` method handles heterogeneous collections automatically. You can combine raw JSON schemas with Python callables in the same `tools` list:

```python
def calculate_sum(a: int, b: int) -> int:
    """Add two integers together."""
    return a + b

mixed_tools = [
    {
        "name": "weather_lookup",
        "description": "Retrieve current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            },
            "required": ["city"]
        }
    },
    calculate_sum  # Python callable with type hints

]

needle = Needle(tools=mixed_tools)

```

The framework iterates through the collection, applying JSON serialization only to non-string items. Raw schemas pass through unchanged.

---

## Where Python Function Conversion Occurs

When you do provide Python callables, the transformation to JSON schema happens in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py). The `_json_type()` helper maps Python type hints to JSON Schema type definitions:

| Python Type | JSON Schema Equivalent |
|-------------|------------------------|
| `str` | `{"type": "string"}` |
| `int` / `float` | `{"type": "number"}` |
| `bool` | `{"type": "boolean"}` |
| `list[T]` | `{"type": "array", "items": ...}` |
| `dict` / `BaseModel` | `{"type": "object", "properties": ...}` |

This introspection is bypassed entirely when you supply a pre-built JSON string, avoiding any runtime overhead from type analysis.

---

## Key Source Files for JSON Schema Handling

| File | Purpose |
|------|---------|
| [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) | Main `Needle` class; implements `tools` parameter parsing and `_resolve()` dispatch |
| [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) | Python-to-JSON schema conversion utilities; `_json_type()` and related helpers |
| [`needle/model/finetune.py`](https://github.com/cactus-compute/needle/blob/main/needle/model/finetune.py) | Training data pipeline; demonstrates schema injection into JSONL formats |
| [`needle/playground/server.py`](https://github.com/cactus-compute/needle/blob/main/needle/playground/server.py) | HTTP server reference implementation; accepts raw `tools` payloads |

---

## Complete Working Example

Below is a runnable pattern showing end-to-end usage with a raw JSON schema and execution context:

```python
import json
from needle import Needle

# Schema defining a hypothetical code execution tool

executor_schema = [
    {
        "name": "execute_python",
        "description": "Run Python code in a sandboxed environment",
        "parameters": {
            "type": "object",
            "properties": {
                "code": {
                    "type": "string",
                    "description": "Python code to execute"
                },
                "timeout_seconds": {
                    "type": "integer",
                    "description": "Maximum execution time",
                    "default": 30
                }
            },
            "required": ["code"]
        }
    }
]

# Initialize with raw JSON—no Python function defined

agent = Needle(tools=json.dumps(executor_schema))

# The LLM can now invoke execute_python based on this schema

result = agent.complete(
    messages=[{
        "role": "user",
        "content": "Calculate factorial of 5 using a Python loop"
    }]
)

```

---

## Summary

- **Raw JSON schemas are first-class inputs**—pass `json.dumps(your_schema)` to the `tools` parameter and Needle accepts it without modification
- **String detection happens at initialization**—the `isinstance(tools, str)` check in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) routes execution around `_resolve()`
- **Mixed collections work transparently**—combine raw JSON objects with Python callables in the same list
- **Conversion utilities remain available**—when needed, [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) provides `_json_type()` for Python-to-JSON transformation

---

## Frequently Asked Questions

### Does Needle validate the JSON schema I provide?

Needle performs minimal validation at initialization. The schema is passed directly to the underlying LLM interface, and validation errors typically surface during model execution or tool invocation rather than at construction time. For stricter validation, pre-validate your schemas against the JSON Schema specification before passing them to Needle.

### Can I use OpenAI's function calling format directly?

Yes. Needle's JSON schema format aligns with OpenAI's function calling specification. Schemas formatted for `tools` or `functions` parameters in the OpenAI API should work without modification, making migration between services straightforward.

### What happens if my schema references Python-specific types like `datetime`?

Raw JSON schemas must use standard JSON Schema types. If you need `datetime` handling, represent it as `{"type": "string", "format": "date-time"}` in your raw schema. When using Python callables, [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) handles common type conversions automatically.

### Is there a performance difference between raw JSON and Python functions?

Raw JSON schemas eliminate the introspection overhead of `_resolve()` and `_json_type()`. For applications invoking Needle frequently or with large tool collections, pre-serialized schemas reduce startup latency marginally. The runtime performance during LLM inference remains identical.