# How to Implement Custom Field Validation Constraints in Needle: A Complete Guide

> Learn to implement custom field validation constraints in Needle. Use the Field class to declare validation rules directly on function parameters and generate OpenAI-compatible JSON schemas effortlessly.

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

---

**Use the `Field` class from `needle.agent.tools` to declare validation rules directly on function parameters, and Needle automatically translates them into OpenAI-compatible JSON schemas.**

Needle is a lightweight Python framework for building agent tools with structured validation. This guide shows you how to implement custom field validation constraints in Needle using the `Field` class, which is defined in [[`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L18).

## Understanding Needle's Validation Architecture

Needle's validation system centers on three core components working together:

- **`Field` class** – Container for constraint metadata
- **`build_schema` function** – Schema generation engine ([`build_schema`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L11))
- **`@tool` decorator** – Registration mechanism that attaches schemas to functions ([`tool`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L64))

When you decorate a function with `@tool`, Needle inspects the signature through `_field_of` ([`_field_of`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L85)), extracts any `Field` objects, and merges their constraints into a JSON schema via the `apply` method ([`apply`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L36)).

## Declaring Field Validation Constraints

The `Field` constructor accepts multiple constraint types that map directly to JSON Schema keywords:

### Numeric Bounds

- `ge` / `le` – inclusive minimum/maximum
- `gt` / `lt` – exclusive minimum/maximum

### String Validation

- `min_length` / `max_length` – character limits
- `pattern` – regex pattern matching
- `format` – semantic format (email, uri, etc.)

### Collection Constraints

- `min_items` / `max_items` – length limits for lists
- `unique_items` – enforce uniqueness

### Value Restrictions

- `enum` – restrict to specific values
- `const` – require exact value

## Basic Usage: Declaring Fields as Default Values

The simplest pattern attaches a `Field` as a parameter's default value:

```python
from needle import tool, Field, build_schema

@tool
def set_environment(
    temp: int = Field(description="temperature in °C", ge=0, le=100),
    mode: str = Field(default="auto", pattern="^(auto|manual)$")
):
    """Configure the environment."""
    pass

# Inspect the generated schema

print(build_schema(set_environment))

```

This produces a schema with validated constraints:

```json
{
  "name": "set_environment",
  "parameters": {
    "type": "object",
    "properties": {
      "temp": {
        "type": "integer",
        "description": "temperature in °C",
        "minimum": 0,
        "maximum": 100
      },
      "mode": {
        "type": "string",
        "default": "auto",
        "pattern": "^(auto|manual)$"
      }
    },
    "required": ["temp"]
  }
}

```

Note that `temp` is **required** (no default provided) while `mode` is **optional** (has `default="auto"`).

## Using typing.Annotated for Cleaner Defaults

When you need both a default value and validation constraints, use `typing.Annotated` to separate concerns:

```python
from typing import Annotated
from needle import tool, Field, build_schema

@tool
def upload_file(
    path: Annotated[str, Field(pattern=r"^/data/.*\.csv$", description="CSV file path")] = "/data/default.csv"
):
    """Upload a CSV file."""
    pass

print(build_schema(upload_file))

```

This pattern keeps the default value assignment clean while preserving full validation capabilities.

## Combining Multiple Validation Constraints

Real-world tools often need layered validation. Here's a comprehensive example:

```python
@tool
def create_user(
    username: str = Field(min_length=3, max_length=20, pattern=r"^\w+$"),
    age: int = Field(ge=13, le=120),
    tags: list = Field(min_items=1, max_items=10, unique_items=True),
    role: str = Field(enum=["admin", "editor", "viewer"])
):
    """Create a new user with validated attributes."""
    pass

```

This single function enforces:
- **Username**: 3-20 word characters only
- **Age**: 13-120 inclusive
- **Tags**: 1-10 unique items
- **Role**: restricted to three specific values

## How Schema Generation Works

The internal flow in [`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py) processes your declarations as follows:

1. **`_field_of`** detects `Field` objects either as default values or within `Annotated` wrappers
2. **Type inspection** determines JSON Schema types from Python annotations
3. **`apply`** merges `Field` constraints into the schema property
4. **Required/optional status** derives from `Optional` annotations or missing defaults
5. **`@tool`** stores the final schema on `fn._needle_tool`

This architecture means validation constraints travel with your function and are available whenever Needle generates tool definitions for language models.

## Summary

- **`Field`** is the primary interface for custom validation constraints in Needle
- Constraints map directly to JSON Schema validation keywords
- Use **default values** for simple cases or **`Annotated`** when you need both defaults and validation
- The **`@tool`** decorator automatically generates and attaches the complete schema
- All examples are verified in [[`tests/test_tools.py`](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py)](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py)

## Frequently Asked Questions

### What validation types does Needle's Field support?

Needle supports numeric bounds (`ge`, `le`, `gt`, `lt`), string constraints (`min_length`, `max_length`, `pattern`, `format`), collection limits (`min_items`, `max_items`, `unique_items`), and value restrictions (`enum`, `const`). The full implementation is in [[`needle/agent/tools.py`](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py#L18).

### How does Needle handle optional parameters with Field constraints?

Needle determines required status from the presence of a default value. A parameter without a default (like `temp: int = Field(...)`) becomes required, while one with a default (like `mode: str = Field(default="auto")`) becomes optional. The `build_schema` function implements this logic.

### Can I use Field without the @tool decorator?

Yes. The `build_schema` function works independently to generate JSON schemas from any function signature containing `Field` objects. However, the `@tool` decorator is required to register the function for use within Needle's agent framework.

### Where are Field validation constraints tested?

The test suite in [[`tests/test_tools.py`](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py)](https://github.com/cactus-compute/needle/blob/main/tests/test_tools.py) validates that declared constraints correctly appear in generated schemas. These tests verify constraint propagation through `build_schema` and proper handling of `Annotated` types.