THE TOOL SCHEMA CARD · the schema seat · every tool's input, checked before the agent ships
WAKES WHEN tool definitions are written or reviewed, a tool call arrives with a missing or malformed field, a handler needs test inputs, or tools move to strict mode
THE METHOD
lint TOOLS.json [--strict-all] — the verdict, every finding with its rule id, path and fix, the size table, and the strict counts.
samples TOOLS.json --out SAMPLES.jsonl — inputs for the handler tests.
calls TOOLS.json CALLS.jsonl — the logged calls, every failure, the causes.
rules — the facts used, with pages and dates. Edit assets/api-rules.json when the docs move.
REFUSES
Answering the naming rule or strict-mode limits from memory
Typing an exact token count it did not measure
Calling a FAIL a pass
Presenting generated samples as real calls
Inventing a log
IT MAKES lint — every tool and parameter against the rules; exit 1 on any ERROR; samples — valid and invalid inputs for each tool's handler (JSONL); calls — logged tool calls checked against the schemas; exit 1 when any fails; rules — the platform facts the lint uses, with their sources and dates.
THE LAW Every finding, size and count comes from the machine. Nothing is typed from memory. The platform's rules are data with a date and a page (assets/api-rules.json). When the docs change, the file changes; the machine says which date it used. ERRORs come first, each with its fix. A FAIL is never reported as a pass. Token counts are estimates and are labelled so; exact counts come from the API's token counting endpoint. Generated samples are test inputs. A log of calls is never invented. The tools file is read, not changed. Judgement is marked: the enum hints and the size thresholds are house heuristics, not platform limits.
IN THE BOX scripts/toolschema.py · assets/api-rules.json · the lint rules · the formats · the method by hand · the sources · ten tests.
PROOF selftest 28/28; a lint of an eight-tool support agent and the trigger test on the record (WITNESS.md) · witnessed 2026-10-13 · EDITION 1.
THE SEAL 96 of 100 · LEGENDARY · WITNESSED · Trigger 5 · Machinery 5 · Law 5 · Portability 5 · Proof 4
IT CAN BE WRONG an AI reading a text file: it can be wrong and can invent a number, a rule, a name or a date; check what matters against your own records and the source; the risk is yours. LICENSE-NOTE.md rides in the packet and is the licence.
THE PUNCH · SHA-256 OF SKILL.MD · CDA19DC0435DD9427A3A6805
CITE THIS CARDTHE TOOL SCHEMA CARD, Edition 1. Lund Studio LLC, Westminster, Colorado. https://lundr.tech/?view=tool-schema-card. Punch CDA19DC0435DD9427A3A6805. Retrieved 2026-10-13.
What it is.
The schema half of an agent's tools: the input_schema of every tool in a Claude Messages API tools list, checked in four parts.
Use it whenever tool definitions are written or reviewed, a tool call arrives with a missing or malformed field, a handler needs test inputs, or tools move to strict mode. Not for a tool's description wording or which tool is picked (THE TOOL WORDS), tool permissions (THE TOOL MANNERS CARD), stale docs (THE DRIFT CARD).
It carries a machine that shows its working: lint — every tool and parameter against the rules; exit 1 on any ERROR; samples — valid and invalid inputs for each tool's handler (JSONL); calls — logged tool calls checked against the schemas; exit 1 when any fails; rules — the platform facts the lint uses, with their sources and dates — 28 checks in its selftest.
It answers requests such as “lint our tools.json before we ship”, “Claude keeps leaving out the currency field and the handler crashes”, “will these schemas work with strict: true?”, “is this a legal tool name?”, “give me valid and invalid inputs to test each handler”, “which of these logged tool calls were malformed?”.
Seven laws, written into the card.
They are in the card itself, so the AI follows them.
EVERY FINDINGEvery finding, size and count comes from the machine. Nothing is typed from memory.
THE PLATFORM'S RULES ARE DATA WITH…The platform's rules are data with a date and a page (assets/api-rules.json). When the docs change, the file changes; the machine says which date it used.
ERRORS COME FIRSTERRORs come first, each with its fix. A FAIL is never reported as a pass.
TOKEN COUNTS ARE ESTIMATES AND ARE LABELLED SOToken counts are estimates and are labelled so; exact counts come from the API's token counting endpoint.
GENERATED SAMPLES ARE TEST INPUTSGenerated samples are test inputs. A log of calls is never invented.
THE TOOLS FILE IS READThe tools file is read, not changed.
JUDGEMENT IS MARKEDJudgement is marked: the enum hints and the size thresholds are house heuristics, not platform limits.
The method.
lint TOOLS.json [--strict-all] — the verdict, every finding with its rule id, path and fix, the size table, and the strict counts.
samples TOOLS.json --out SAMPLES.jsonl — inputs for the handler tests.
calls TOOLS.json CALLS.jsonl — the logged calls, every failure, the causes.
rules — the facts used, with pages and dates. Edit assets/api-rules.json when the docs move.
What rides in the card.
In the card’s own voice, with a machine that shows its working.
THE REFERENCESreferences/FORMATS.md · LINT-RULES.md · SOURCES.md · THE-METHOD.md
THE MACHINEscripts/toolschema.py · four commandslint — every tool and parameter against the rules; exit 1 on any ERROR; samples — valid and invalid inputs for each tool's handler (JSONL); calls — logged tool calls checked against the schemas; exit 1 when any fails; rules — the platform facts the lint uses, with their sources and dates. 28 checks in its selftest. Needs: Python 3; a tools file in the Messages API format.
What it does once your AI has it.
lint Every tool and parameter against the rules; exit 1 on any ERROR
samples Valid and invalid inputs for each tool's handler (JSONL)
calls Logged tool calls checked against the schemas; exit 1 when any fails
rules The platform facts the lint uses, with their sources and dates
On the record Example Co's eight-tool support agent linted (6 ERROR, then PASS), its refund handler given 22 checked inputs, a week of calls checked (4 of 7 malformed); 41 of 48 in the trigger test after the rewrite
What it refuses, and where it hands off.
It refuses answering the naming rule or strict-mode limits from memory; typing an exact token count it did not measure; calling a FAIL a pass; presenting generated samples as real calls; inventing a log.
a tool's description wording, and which tool the model picks → THE TOOL WORDS
what a tool may do without asking (send, spend, delete) → THE TOOL MANNERS CARD
docs and instructions that no longer match the code → THE DRIFT CARD
the shape of the model's own answer to a downstream system → THE OUTPUT CONTRACT CARD
security sign-off on what a tool does → the organisation's own security lead
Reads definitions, not handler code; the platform's rules are dated and change: edit assets/api-rules.json when the docs do. It is an AI reading a text file: it can be wrong, so check what matters against your own records and the source.
What’s in the buy.
CARD.md — the whole card: its boot, law, method, machine, seals, refusals and hand-offs, ledger, score and what it carries · SKILL.md — the door your AI reads first · START-HERE.md — the first five minutes, for you
FORMATS.md + LINT-RULES.md + SOURCES.md + THE-METHOD.md — in references/, in the card's own voice
scripts/toolschema.py — the machine: lint · samples · calls · rules; see NEEDS below; selftest 28/28
WITNESS.md — the run on a realistic case as printed, and the trigger test on Haiku, Sonnet and Opus · SELFTEST.md — the machine's own test, as run at the cut · AUDIT.md, the twelve checks · MANIFEST.md, every file hashed
tests/evals.json — the cases it must take, hand off and refuse
MY-LEDGER.md — the card's memory, on your own machine · TIN.md, the nine lines on the tin · LICENSE-NOTE.md · HOSTS.md, where it runs
system/short-prompt.md and system/system-prompt.md — the doors for any AI without skills
PROOF · SELFTEST 28/28 · WITNESSED ON A RUN · TRIGGER TEST 31 OF 48 ON ITS OWN PROMPTS · HAIKU 3/16 · SONNET 12/16 · OPUS 16/16 · 0 WRONG PICKS · EDITION 1 · PUNCH CDA19DC0435DD9427A3A6805
ON EVERY DOOR · A CARD FOR YOUR AI, A FILE FOR YOUR COMPUTER
In Claude: turn on code execution under Settings › Capabilities, then Customize › Skills › + › Create skill, and upload the zip as it is — START-HERE.md in the box walks you through it, and tells you how to keep the ledger. In ChatGPT, Gemini and the rest: a Project, a GPT or a Gem with the card’s files attached and the short door in the box pasted as its instructions. The words run anywhere an AI reads Markdown; the machines run wherever the host runs Python. Witnessed in Claude only. The host marks →
Take it.
Nº 965 — LAUNCHING SOON
Sold as is under the term printed. A card instructs an AI you operate; results depend on your model, your files and your judgement. Not medical, legal, tax or financial advice, never the person’s boss, and not a licence to practise. The terms →
One payment. The file is yours to keep and keeps working at its edition; new editions issued in the year after you buy reach you by email as replacement files. Tax is added at checkout where it applies. Nothing auto-renews.
A TERM, NOT A SUBSCRIPTION: NOTHING RENEWS ITSELF AND NOBODY IS CHARGED AGAIN. WHEN A TERM ENDS THE FILES STAY ON YOUR COMPUTER AND STAY READABLE — FREEZE, NOT LOSS.
THE MACHINE · IN THE OPEN · FROM THE CARD’S OWN WITNESS.MD AND SELFTEST.MD
toolschema.py, every command showing its working.
A small Python program. Your AI runs it; you can too. The blocks below are pasted from the card’s own record, as printed.
lint
every tool and parameter against the rules; exit 1 on any ERROR
samples
valid and invalid inputs for each tool's handler (JSONL)
calls
logged tool calls checked against the schemas; exit 1 when any fails
rules
the platform facts the lint uses, with their sources and dates
selftest
28 checks that prove the machine; it must print 28/28
python3 scripts/toolschema.py lint tools.json
TOOL SCHEMA LINT · tools.json · 8 tools · rules as read 2026-10-12
issue-refund
ERROR S2 /tools/1/input_schema/properties/amount/type
"float" is not a JSON Schema type
fix: use number
ERROR S3 /tools/1/input_schema/required
'reason' is required but not in properties: the model gets no type or description for it
fix: add 'reason' to properties, or drop it from required
WARN S6 /tools/1/input_schema/properties/currency
an open string where an enum belongs: the description lists USD, EUR, GBP
fix: add "enum": ["USD", "EUR", "GBP"]
update shipping address
ERROR T1 /tools/2/name
the name breaks the API's naming rule ^[a-zA-Z0-9_-]{1,128}$ (holds space)
fix: rename it, for example update_shipping_address
search_kb
WARN S8 /tools/3/input_schema/properties/tags
an array with no items schema: any items pass
fix: say what each item is with "items"
NOTE S9 /tools/3/input_schema/properties/filters
a free-form object: the handler receives whatever arrives
fix: list its properties, or say in the description what it holds
escalate_ticket
ERROR S12 /tools/4/input_examples/1
example 2 fails its own schema (the docs: an invalid example returns a 400 error): /priority "urgent" is not one of ["low", "medium", "high", "High"]
fix: fix the example or the schema
NOTE S7 /tools/4/input_schema/properties/priority/enum
values that differ only in case: ["high", "High"]
fix: keep one spelling; compare case-insensitively in the handler
send_email
ERROR X1 /tools/5/input_schema/properties/body/maxLength
strict mode does not support maxLength (structured outputs, JSON Schema limitations)
fix: drop it, say the bound in the description, and check it in the handler
WARN X2 /tools/5/input_schema
an object without additionalProperties: false; the docs say it must be set to false for objects
fix: add "additionalProperties": false
create_ticket
ERROR T3 /tools/6/parameters
the schema sits under 'parameters'; the Messages API reads input_schema
fix: rename the key to input_schema
get_customer
WARN T4 /tools/7/description
the tool has no description (the API reference: optional, but strongly recommended)
fix: say what it does, when to use it and when not; the wording is THE TOOL WORDS' job
WARN S5 /tools/7/input_schema/properties/customer_id
the parameter 'customer_id' has no description
fix: say what it means, its format and an example value
SIZE · characters of each definition as sent (compact JSON), tokens estimated at 4 characters per token
lookup_order 357 chars ~90 tokens
issue-refund 450 chars ~113 tokens
update shipping address 534 chars ~134 tokens
search_kb 388 chars ~97 tokens
escalate_ticket 577 chars ~145 tokens
send_email 436 chars ~109 tokens
create_ticket 299 chars ~75 tokens
get_customer 215 chars ~54 tokens
all tools 3256 chars ~814 tokens
[… 6 more lines, trimmed here]
STRICT · 1 tool checked as strict · optional parameters 0 of 24 · union-type parameters 0 of 16 · strict tools 1 of 20
VERDICT · PASS · 0 ERROR · 0 WARN · 0 NOTE
FORMULA · ERROR = the API refuses it, or the handler cannot rely on the field; WARN = likely to cost a malformed call; NOTE = worth a look. Any ERROR fails.
SOURCE · the rules as read 2026-10-12 · the name rule: platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools · strict mode: platform.claude.com/docs/en/build-with-claude/structured-outputs (JSON Schema limitations) · the size and nesting thresholds are house heuristics.
LIMIT · it reads the definitions, not the handler's code; the enum hints and size thresholds are judgement, marked as such; Python's re stands in for the platform's regex engine; count tokens exactly with the API's token counting endpoint.
THE SELFTEST, AS PRINTED
PASS a sound tool lints with no ERROR and no WARN
PASS a name with spaces breaks the naming rule, with a fix offered
PASS a name of 128 characters passes and 129 breaks the rule as read
PASS two tools with one name are an ERROR
PASS a schema under inputSchema is caught and the key named
PASS a required field missing from properties is an ERROR naming it
PASS type 'float' is an ERROR with 'number' offered
PASS a parameter with no type is a WARN; one with no description is a WARN
PASS an open string listing 'One of USD, EUR or GBP' is flagged with the enum to add
PASS an array with no items is a WARN
PASS an input example that fails its own schema is an ERROR (the API answers 400), the good one passes
PASS impossible bounds and an unresolved $ref are ERRORs
PASS strict mode: minimum and maxLength are refused, a lookaround is refused, additionalProperties true is refused
PASS strict limits as read: 21 strict tools and 25 optional parameters are ERRORs
PASS a recursive schema is refused in strict mode
PASS an oversized definition is a WARN with its characters and estimated tokens
PASS samples: every accept passes the schema and every reject fails it, nothing skipped
PASS samples: the minimal input holds only the required fields
PASS samples: the pattern ^[A-Z]{3}-\d{4}$ gets a matching value
PASS samples: an enum gets a value not listed and a value that differs only in case
PASS calls: a good call passes; a wrong type, an enum case slip and an unknown tool are caught and grouped
PASS the validator: true is not 1, 1.0 is an integer, a pattern searches unanchored
PASS the validator: anyOf, oneOf, allOf, not and $defs refs
PASS the validator: dates and emails are checked as formats
PASS the same tools give the same report, byte for byte
PASS the command line: lint exits 0 on a sound file and 1 on an ERROR; --help shows usage; samples writes its file
PASS assets/api-rules.json matches the rules built into this machine
PASS nothing in this machine opens a network connection
selftest 28/28
THE TRIGGER TEST. The card's line was tried on Haiku, Sonnet and Opus beside 14 neighbouring skills, in a clean environment: 31/48 on its own prompts (Haiku 3/16 · Sonnet 12/16 · Opus 16/16), 0 wrong picks; no skill (answered directly) 17.
THE RUNS ARE PASTED FROM THE CARD’S OWN RECORD AS PRINTED. THE CASES ARE THE STUDIO’S OWN EXAMPLES (EXAMPLE CO IS FICTION); NO PRIVATE PERSON IS NAMED.
QUESTIONS AT THE TABLE
Asked before the wire.
What does it need from me?
Python 3; a tools file in the Messages API format.
Does it send anything anywhere?
Its machine reads what you point it at, and HOSTS.md says what it needs; python 3; a tools file in the Messages API format. The words run wherever an AI reads Markdown.
What is on the record?
The selftest (28/28), a run on a realistic case with every command and verdict pasted into WITNESS.md as printed (Example Co's eight-tool support agent linted (6 ERROR, then PASS), its refund handler given 22 checked inputs, a week of calls checked (4 of 7 malformed); 41 of 48 in the trigger test after the rewrite); and the trigger test, the card's line tried on Haiku, Sonnet and Opus beside 14 neighbouring skills: 31/48 on its own prompts, 0 wrong picks; the twelve-check audit (AUDIT.md). The punch on the face is the SHA-256 of SKILL.md, so a downloaded copy can be checked against this room.
Can it be wrong?
Yes — it is an AI reading a text file. Reads definitions, not handler code; the platform's rules are dated and change: edit assets/api-rules.json when the docs do.
What will it never do?
It refuses answering the naming rule or strict-mode limits from memory; typing an exact token count it did not measure; calling a FAIL a pass; presenting generated samples as real calls; inventing a log.
Who may use it?
One person, any number of their own AIs and computers; a team needs a company licence.