Skip to main content

3tears-tool-schema

Dependency-free helpers for the JSON Schema a tool advertises for its arguments.

A tool's arguments reach a model as a JSON Schema: pydantic's model_json_schema(), an MCP server's inputSchema, a LangChain tool's args_schema. Pydantic writes every nested model as a $ref into the schema's $defs, and every optional field as anyOf: [X, {"type": "null"}]. Anything that reads only a property's own type gets both wrong. A model is shown a string where the tool wants a list of objects, and an input normaliser leaves "[]" as a string where the tool wants a list.

This package has no dependencies, so any tool host can take it without inheriting a framework: a LangGraph app, an MCP server, a model adapter, a validator.

self_contained_input_schema

One schema per tool, with no references left in it.

from pydantic import BaseModel
from threetears.tool_schema import self_contained_input_schema


class Shot(BaseModel):
    prompt: str
    seconds: int | None = None


class Storyboard(BaseModel):
    shots: list[Shot]


schema = self_contained_input_schema(Storyboard.model_json_schema(), tool_name="storyboard")
# {"type": "object",
#  "properties": {"shots": {"type": "array",
#                           "items": {"type": "object",
#                                     "properties": {"prompt": {"type": "string"},
#                                                    "seconds": {"type": "integer"}},
#                                     "required": ["prompt"]}}},
#  "required": ["shots"]}

What it does:

  • Inlines every $ref into the schema's own $defs or definitions, through items, unions and nested properties. Each level keeps its required list and descriptions, and a field's own description wins over its model's.
  • Collapses an optional union (anyOf: [X, null]) to X at every depth, dropping the default: null that would contradict X.
  • Keeps a union of two or more real members whole, rather than choosing one.
  • Leaves an untyped (Any) field untyped.
  • Expands a recursive model until it recurs. The point of recursion keeps the definition's type and reads "A Node: the same shape as the Node that contains it.", rather than being cut to {}.
  • Drops titles and the root description; the tool's description travels beside its schema.
  • Always returns type: "object", properties and required.

A $ref to anything but the schema's own definitions raises ValueError naming the tool and the reference. A reference left in place would point at nothing the reader has.

In an MCP server

List a tool whose arguments are a pydantic model, with an inputSchema any client can read:

from mcp.types import Tool
from threetears.tool_schema import self_contained_input_schema

tool = Tool(
    name="storyboard",
    description="Plan the shots for a scene.",
    inputSchema=self_contained_input_schema(Storyboard.model_json_schema(), tool_name="storyboard"),
)

In a LangGraph app

Hand a model provider that takes raw tool specs the same self-contained shape:

from langchain_core.tools import StructuredTool
from threetears.tool_schema import self_contained_input_schema

tool = StructuredTool.from_function(plan, name="storyboard", args_schema=Storyboard)
spec = {
    "name": tool.name,
    "description": tool.description,
    "input_schema": self_contained_input_schema(Storyboard.model_json_schema(), tool_name=tool.name),
}

declared_type

The one JSON type a property declares, read through the shapes pydantic writes. Use it to coerce loose input toward the declared type.

from threetears.tool_schema import declared_type

schema = Storyboard.model_json_schema()
declared_type(schema["properties"]["shots"], schema)  # "array"

It reads through:

  • a nullable type list, such as ["array", "null"];
  • an optional union;
  • a $ref into the schema's definitions.

It answers None for a union of two or more real types, and for a reference it cannot follow. It never raises, so code that must not fail on a schema it only half understands can call it.

In 3tears, 3tears-models shows every bound tool to a Claude subscription model through self_contained_input_schema, and 3tears-agent-tools coerces tool input with declared_type.

Metadata

Release files for 3tears-tool-schema 0.57.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for 3tears-tool-schema 0.57.0
File Size Uploaded
3tears_tool_schema-0.57.0.tar.gz 12.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for 3tears-tool-schema 0.57.0
File Interpreter ABI Platform
3tears_tool_schema-0.57.0-py3-none-any.whl Python 3 none any Details

Total release size: 21.7 kB

Release files / 3tears_tool_schema-0.57.0.tar.gz

Download URL 3tears_tool_schema-0.57.0.tar.gz
Size 12.6 kB
Tags Source
SHA-256 checksum
How to use checksums
fe0c9bfc7419df9becdc2731235f8b62f782f5e4407c9742cc8de674de64aab0
BLAKE2b-256 checksum
How to use checksums
ebfe6b9e2bc7313b13cc3ee38ce040bdc944afe98baeda7750812c2eb1eb7044
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / 3tears_tool_schema-0.57.0-py3-none-any.whl

Download URL 3tears_tool_schema-0.57.0-py3-none-any.whl
Size 9.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cb6a992de7508569cb87901b02b30911f5c6e14af3dbde3f71ffd65cf3ddb364
BLAKE2b-256 checksum
How to use checksums
7d50276a62f4d6664b9359b802b351958211a1ef661e54a9663250039e0e901f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

0.69.0

2 release files

0.68.0

2 release files

0.67.0

2 release files

0.66.0

2 release files

This release

0.57.0 This release

2 release files

0.56.3

2 release files

0.56.2

2 release files

0.56.1

2 release files

0.56.0

2 release files

0.55.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page