Skip to content

Type mapping

How Python type hints become JSON Schema 2020-12 in ToolDefinition.parameters.

Implemented in toolschema._types.type_to_schema().

Primitives

Python JSON Schema
str {"type": "string"}
int {"type": "integer"}
float {"type": "number"}
bool {"type": "boolean"}
Any {} (unconstrained)
None / type(None) {"type": "null"}

Containers

Python JSON Schema
list[T] {"type": "array", "items": schema(T)}
dict[str, T] {"type": "object", "additionalProperties": schema(T)}
dict (bare) {"type": "object"}
tuple[A, B, C] {"type": "array", "prefixItems": [...], "minItems": 3, "maxItems": 3}
tuple[T, ...] {"type": "array", "items": schema(T)}

Unions and optionals

Python JSON Schema
A \| B {"anyOf": [schema(A), schema(B)]}
T \| None {"anyOf": [schema(T), {"type": "null"}]}
Optional[T] with = None same + "default": null

Literals and enums

Python JSON Schema
Literal["a", "b"] {"enum": ["a", "b"]}
Literal[1, 2] {"enum": [1, 2]}
class Color(str, Enum) {"enum": ["red", "green", ...]}

Structured types

Python JSON Schema
TypedDict object with properties, required from __total__
@dataclass object with fields, defaults, required
Pydantic BaseModel model_json_schema() normalized

Annotated

Annotated[str, Field(description="City", min_length=1)]

{"type": "string", "description": "City", "minLength": 1}

Annotated[str, "City name"]

{"type": "string", "description": "City name"}

Defaults

Function parameter defaults become schema "default" keys. Parameters with defaults are not in required.

def f(a: int, b: int = 1): ...
# required: ["a"]
# properties.b.default: 1

Return types

Return annotations map to ToolDefinition.output via the same type_to_schema():

def f() -> list[dict[str, str]]: ...
# output: {"type": "array", "items": {"type": "object", ...}}

Unsupported (v1.0)

Type Status
*args, **kwargs skipped / not supported
Generics list[T] unbound use concrete types
ParamSpec, TypeVar deferred
dict[int, T] only dict[str, T]
Callable types not supported

Raises TypeError: Unsupported type annotation: ...

Example — complex function

from typing import Annotated, Literal
from toolschema import Field, schema

def search(
    query: Annotated[str, Field(min_length=1)],
    tags: list[str] | None = None,
    mode: Literal["fuzzy", "exact"] = "fuzzy",
) -> list[dict]:
    """Search products."""
    ...

print(schema(search).parameters)

See tests/complex_fixtures.py and tests/test_types_extended.py for golden examples.