Observed since 20 Oct, 15:38 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Dependency-free helpers that make a tool's JSON Schema self-contained for models and validators
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_schemaOne 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:
$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.anyOf: [X, null]) to X at every depth, dropping the
default: null that would contradict X.Any) field untyped."A Node: the same shape as the Node that contains it.", rather than being cut to {}.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.
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"),
)
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_typeThe 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:
["array", "null"];$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.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.72.0 | 20 Oct 2026 | 0 |
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Public sponsor counts have not been collected. These signals belong to the repository owner.
github · 1 observations shown. Missing days are unknown.
| Date / source period | Value | Observed at |
|---|---|---|
| 2026-10-20 | 0 | 2026-10-20T17:18:19.8928356Z |
Observed since 20 Oct, 15:38 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:e193a28e2b6caed974fce457ff8929bd682a57482ccf6ad233b664d26fccfe7d