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.56.2

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.56.2
File Size Uploaded
3tears_tool_schema-0.56.2.tar.gz 12.6 kB Details

Built distribution (wheel)

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

Total release size: 21.7 kB

Release files / 3tears_tool_schema-0.56.2.tar.gz

Download URL 3tears_tool_schema-0.56.2.tar.gz
Size 12.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c36da5b5fdb038f894274b2ae53f45775d94e9210aab69b02913ff271dab1130
BLAKE2b-256 checksum
How to use checksums
0faee62a95523c74ebcb4c32663fdc9db30553dabce85fb9b82e871ac1fe989b
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 28, 2026.

Transparency log

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

Download URL 3tears_tool_schema-0.56.2-py3-none-any.whl
Size 9.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
642c9cf21be6b80b019e3e4d8660d9015ed1a270846190bc38ea27e57750db9b
BLAKE2b-256 checksum
How to use checksums
7e9bc7650802491711a3e7a39034b17ff6adb6fb599ab09ccd9939c96899d127
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 28, 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

0.57.0

2 release files

0.56.3

2 release files

This release

0.56.2 This release

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