# How to Implement Custom Field Validation in Needle Tool Schemas

> Learn to implement custom field validation in Needle tool schemas using the Field class with parameters like ge, le, enum, pattern, or min_length for robust data handling.

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

---

**Use the `Field` class from `needle.agent.tools` with validation parameters like `ge`, `le`, `enum`, `pattern`, or `min_length`, then attach it to function parameters as a default value or inside `typing.Annotated`.**

Needle's tool-schema generation automatically converts Python function signatures into JSON-Schema definitions for LLM tool calls. Implementing **custom field validation in Needle tool schemas** lets you enforce constraints directly at the schema level, eliminating the need for manual runtime checks.

---

## Understanding Needle's Validation Architecture

The validation system centers on three components in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py):

- **`Field` class** (lines 17-31): Stores validation metadata as instance attributes
- **`Field.apply` method** (lines 35-48): Injects JSON-Schema keys like `"minimum"`, `"maximum"`, `"pattern"`, and `"enum"`
- **`_field_of` helper** (lines 84-91): Extracts `Field` instances from parameters during schema building

When you decorate a function with `@tool`, the `build_schema` routine inspects each parameter, locates any attached `Field` objects, and merges their constraints into the final schema. The LLM then receives these constraints and must generate compliant inputs.

---

## Attaching Fields to Parameters

Needle supports two patterns for associating `Field` instances with function parameters.

### Pattern 1: Direct Field as Default Value

Pass a `Field` directly where a default value would appear:

```python
from needle.agent.tools import Field, tool

@tool
def set_mode(mode: Field(default="auto", enum=["auto", "manual"])):
    """
    Switch the system mode.
    """
    return f"Mode set to {mode}"

```

The generated schema contains:
- `"enum": ["auto", "manual"]`
- `"default": "auto"`

### Pattern 2: Annotated with Field

Use `typing.Annotated` to combine a type annotation with validation rules:

```python
from typing import Annotated
from needle.agent.tools import Field, tool

@tool
def send_email(
    address: Annotated[str, Field(pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$")],
    subject: Annotated[str, Field(min_length=5, max_length=100)],
    body: str,
):
    """Send an email with validated address and subject."""
    return "Email queued"

```

The schema enforces:
- **Regex pattern** for `address` (valid email format)
- **Length limits** for `subject` (5-100 characters)

---

## Available Validation Parameters

The `Field` class accepts these constraint parameters, each mapping to a JSON-Schema property:

| Parameter | JSON-Schema Key | Use Case |
|-----------|---------------|----------|
| `ge` | `"minimum"` | Numeric lower bound (≥) |
| `le` | `"maximum"` | Numeric upper bound (≤) |
| `gt` | `"exclusiveMinimum"` | Strict numeric lower bound (>) |
| `lt` | `"exclusiveMaximum"` | Strict numeric upper bound (<) |
| `enum` | `"enum"` | Whitelist of allowed values |
| `pattern` | `"pattern"` | Regex string validation |
| `min_length` | `"minLength"` | String minimum length |
| `max_length` | `"maxLength"` | String maximum length |
| `default` | `"default"` | Default value in schema |

### Numeric Range Validation Example

```python
from needle.agent.tools import Field, tool

@tool
def scale_image(
    size: Field(default=256, ge=64, le=1024),
    keep_aspect: bool = True,
):
    """Resize an image to a given square size."""
    return f"Image resized to {size}px"

```

The resulting schema includes:
- `"minimum": 64`
- `"maximum": 1024`
- `"default": 256`

---

## How Field Defaults Are Handled

The `Field.has_default` property (lines 32-33) detects whether a default value was explicitly provided. This ensures:

- **Direct `Field` usage**: The `default` parameter is always captured
- **`Annotated` usage**: The type annotation and `Field` are separate from any Python default

Needle correctly merges these cases so the JSON-Schema always reflects the intended default behavior.

---

## Complete Validation Workflow

1. **Import** `Field` and `tool` from `needle.agent.tools`
2. **Define constraints** using `Field` parameters relevant to your data type
3. **Attach to parameter** via direct default or `Annotated[type, Field(...)]`
4. **Decorate with `@tool`** to trigger schema generation
5. **Verify** the output schema contains your constraints

The LLM receives the constrained schema and generates compliant tool calls, with validation occurring before your Python function ever executes.

---

## Summary

- **Custom field validation in Needle** uses the `Field` class in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) to inject JSON-Schema constraints
- **Two attachment patterns**: direct `Field` as default, or `Annotated[type, Field(...)]` for complex types
- **Validation parameters** map directly to JSON-Schema: `ge`/`le` for ranges, `pattern` for regex, `enum` for whitelists, `min_length`/`max_length` for strings
- **Default values** are captured via `Field.has_default` and reflected in schema output
- The `@tool` decorator's `build_schema` routine processes these fields through `_field_of` and `Field.apply`

---

## Frequently Asked Questions

### What is the difference between using `Field` directly versus `Annotated`?

Using `Field` directly replaces the default value and works when you don't need an explicit type annotation. `Annotated` preserves the type hint and separates concerns—use it when you need both strict typing and validation rules, or when the parameter has a separate runtime default.

### Can I combine multiple validation constraints on one field?

Yes. The `Field` constructor accepts multiple parameters simultaneously. For example: `Field(ge=0, le=100, default=50)` creates a bounded integer with a default. The `apply` method merges all provided constraints into the schema.

### Does Needle validate inputs at runtime or only in the schema?

Needle generates the JSON-Schema for LLM consumption; the LLM is expected to produce compliant inputs. For additional runtime safety, you can use Pydantic models or manual checks inside your tool function, though the schema-level validation handles most cases.

### Where are the core validation classes defined?

All validation logic resides in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py). Key locations: `Field` class (lines 17-31), `Field.apply` method (lines 35-48), and `_field_of` extraction helper (lines 84-91). The test suite at [`tests/test_tools.py`](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py) contains practical examples.