One bad schema 400s the entire tools array, so every turn of your agent dies on
the worst tool in the belt. Toolwright walks every node against eleven real targets —
OpenAI strict and non-strict, Anthropic strict and non-strict, Gemini's two schema fields,
Vertex, and llama.cpp GBNF — and hands you the repaired files.
1 · Your tool belt
Drop a file anywhere on this box. Python dicts, TypeScript literals and trailing commas
are repaired — you will see the diff before anything is parsed.
Repaired paste
2 · Where it breaks
3 · Every finding
4 · The local-model path
Key order is locked by the grammar
A GBNF grammar hard-locks the order of object keys, and the order is the
declaration order of properties — not the order of your
required array. Reordering keys in a schema silently changes what the
model is allowed to emit.
Dropped by the grammar
These constraints are in your schema and not in the grammar. This list and
the generated post-parse-checks.ts are a matched pair — every keyword
here is re-asserted there.
5 · Take the files
Every file is generated here, from what you pasted. Nothing was uploaded to produce them.
What this cannot tell you
1 · Paste, don't upload
Paste an MCP tools/list result, an OpenAI tools array in either the
flat Responses shape or the nested Chat Completions shape, an Anthropic array with
input_schema, or a Gemini functionDeclarations block. Toolwright works
out which it is. A Python dict, a TypeScript literal or a paste with trailing commas is
repaired first — and you are shown the diff before anything is parsed.
2 · It walks into the namespaces
The 2026 Responses tools array is no longer flat: a
type: "namespace" entry carries its own nested tools, each with its own
parameters and strict, and the API validates inside them. A linter
that iterates only the top level reports a clean bill of health on a belt that 400s.
3 · Eleven targets, not three
“OpenAI, Anthropic, Gemini” is a fiction. The tool shape and the accepted keyword
subset vary independently, so a belt can be legal on one API of a provider and rejected by the
other. Toolwright treats OpenAI strict, OpenAI non-strict on Responses, OpenAI non-strict on
Chat, Anthropic strict, Anthropic non-strict, the Anthropic SDK path, Gemini
parameters, Gemini parametersJsonSchema, Vertex, llama.cpp and
xgrammar as separate targets — because they are.
4 · The conflict is the point
additionalProperties: false is required by OpenAI strict, required
by Anthropic strict, and does not exist in Gemini's parameters schema. The
single most-Googled repair in this problem space is itself a Gemini-breaking change. That is why
this is a matrix and not a list of errors.
5 · The grammar and the runtime checks are a matched pair
Toolwright compiles your belt to a llama.cpp GBNF grammar with a first-party port of
json-schema-to-grammar.cpp, and prints exactly which of your constraints the
grammar .
The generated post-parse-checks.ts re-asserts that same list at runtime. They are
computed from one walk, so they cannot drift apart.
6 · You leave with files
Repaired tool definitions for each target, the two grammars, the runtime checks, an audit CSV,
and a name-mapping table whenever a rename was forced.
Protected
Not protected
Trust surface
Toolwright lints, repairs and transpiles an agent tool belt across the
providers, and generates the grammar and the runtime checks for the local-model path. It runs
entirely in your browser.
It exists because the fix for one provider is the break for another, and because the file you
need to check is the one you least want to upload — a tool belt's property names, enum values
and descriptions are a map of your system.
No live provider API call was made to build the rules in this tool. Every rule
carries a provenance badge: DOC is quotable from the vendor's own
documentation, SDK was read from the vendor's own SDK source,
SRC was read from llama.cpp or xgrammar source, and FIELD is a
third-party capture of a real 400. Where the evidence genuinely does not settle a question, the
cell reads unknown and the Limits panel says why, rather than showing a green check.
Two libraries ship in the bundle and no more.
@cfworker/json-schema
validates schemas, chosen because it compiles no code — the usual choice, ajv,
builds its validators with the Function constructor and cannot run under this
page's content policy at all.
jsonrepair
salvages a malformed paste, behind a first-party truncation guard. The dialect detection, the
linter, the transpilers, the GBNF port and the check generator are first-party and unit-tested.
Counting tokens was deliberately left out. Two of the three providers have no client-side
tokenizer at all, and shipping one would have meant a megabyte of vocabulary and a network
request — which is the only reason this page can promise that nothing leaves the tab. For a
real token budget, rendered through the model's own chat template, use
chatprint.