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.60.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.60.0
File Size Uploaded
3tears_tool_schema-0.60.0.tar.gz 12.6 kB Details

Built distribution (wheel)

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

Total release size: 21.7 kB

Release files / 3tears_tool_schema-0.60.0.tar.gz

Download URL 3tears_tool_schema-0.60.0.tar.gz
Size 12.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e3ddd4d471bbf9c9d3922a6c3f9650e54858f458aba3d1523ed8b4d1ab459d05
BLAKE2b-256 checksum
How to use checksums
7c4fffefe4bebcabd2870ed9993004ff6630a08f2d50709b75450e2b46eec138
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 Oct 3, 2026.

Transparency log

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

Download URL 3tears_tool_schema-0.60.0-py3-none-any.whl
Size 9.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc4c438bcffcba9974a0fa64a75db7a9d86a4568e74787b273326b9fdbb448a4
BLAKE2b-256 checksum
How to use checksums
30bd358f2d761c1ee1d37d8696b5d8ba37322b72b29714e4801a54465e8d5738
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 Oct 3, 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.60.0 This release

2 release files

0.57.0

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