Tool Schema Linter & Converter
How it works
One tool, three wire shapes
The same function is written three ways. OpenAI Chat Completions nests it under a function envelope: {"type":"function","function":{"name","description","parameters"}}; the OpenAI Responses API flattens the envelope away, putting name and parameters beside type (the Responses API shape checkbox emits that variant); Anthropic uses {"name","description","input_schema"}; MCP servers return {"name","description","inputSchema"} — camelCase — inside a {"tools":[...]} result. This tool detects the shape per entry, so it accepts a bare tools array, a whole request body, the legacy OpenAI functions parameter, or an MCP tools/list response still wrapped in its JSON-RPC envelope. Converting within the same shape keeps that provider's own optional fields (an MCP tool keeps its title, annotations and outputSchema); converting across providers drops what the target shape has no place for, and each dropped field is named in a warning under the output. strict survives OpenAI ⇄ Anthropic but has no MCP equivalent. Provider built-in tools (web_search, code_execution and friends) carry no JSON schema at all and are skipped with a note.
The rules that fail silently
Anthropic requires tool names to match ^[a-zA-Z0-9_-]{1,64}$, and OpenAI documents the same rule for Chat Completions. MCP (spec 2025-11-25) is looser: up to 128 characters and dots are allowed, so admin.tools.list is a perfectly good MCP tool name that gets rejected the moment it is bridged to a direct API call — exactly the failure a converter creates. Beyond names, the linter flags what degrades quietly instead of erroring: a missing description (models choose tools by description — it is the single biggest lever on tool-calling quality), required listing keys that do not exist in properties (unsatisfiable when additionalProperties is false), enum values that contradict the declared type, and duplicate tool names. The schema root must be type "object" for every provider; omitting parameters entirely is legal on OpenAI (it means an empty parameter list) but Anthropic and MCP require the schema, so conversion synthesizes an empty object schema and says so.
Strict mode and structured outputs
OpenAI strict mode (strict: true) enforces two things everywhere in the schema: additionalProperties must be false on every object, and every key in properties must be listed in required — an optional field is expressed by adding "null" to its type instead. Its unsupported-keyword list has shrunk since 2024: pattern, format, numeric bounds (minimum/maximum/multipleOf) and minItems/maxItems are supported now (except for fine-tuned models); allOf, not, if/then/else and dependent* are still rejected. Note the Responses API now auto-normalizes schemas toward strict when you omit the flag. Anthropic's structured outputs and strict tools draw the lines differently: numeric constraints, string length constraints, array constraints beyond minItems 0/1, and recursive schemas are not supported — while OpenAI strict handles recursion fine. The linter labels all of this STRICT, names which provider each finding applies to, and upgrades a finding to an ERROR when the tool itself declares strict: true for the provider whose shape it uses.
Examples
| OpenAI tools array (TO ANTHROPIC) | [{"name":"get_weather","input_schema":{...}}] — parameters becomes input_schema |
| MCP tools/list JSON-RPC response (TO OPENAI) | function envelopes — note: dropped title, annotations |
| LINT on an MCP tool named "admin.tools.list" | WARN name valid for MCP only — OpenAI (Chat Completions) and Anthropic require ^[a-zA-Z0-9_-]{1,64}$ |
| LINT on strict:true without additionalProperties:false | ERROR strict: true is set — additionalProperties: false missing at schema root |
FAQ
Which input shapes are accepted?
OpenAI Chat Completions tools arrays ({"type":"function","function":{...}}), the flat Responses API shape, the deprecated functions parameter, Anthropic tools (input_schema), and MCP tools/list results — bare or still inside their JSON-RPC envelope. You can paste a whole request body containing any of these, or a single tool object. Detection is per entry, so a mixed list converts too.
Why did the conversion drop some fields?
The target shape has no place for them. Converting across providers drops fields like MCP's annotations or Anthropic's cache_control — each dropped field is named in the note under the output (a long list is truncated to the first few). Converting within the same shape keeps them, and strict survives OpenAI ⇄ Anthropic but not MCP. Provider built-in tools (web_search, code_execution) carry no JSON schema and are skipped entirely.
What does STRICT mean in the lint report?
A finding that only bites when the schema is used with OpenAI strict mode or Anthropic structured outputs: additionalProperties: false missing on an object, optional properties not listed in required, a keyword one of the two rejects. Each finding names the provider it applies to. Once a tool itself declares strict: true, the findings for that tool's own provider become ERRORs, because the API will reject the request.
How do I make a parameter optional under OpenAI strict mode?
You can't leave it out of required — strict mode requires every key in properties to be listed. Optionality moves into the type instead: "type": ["string", "null"]. Anthropic's strict tools do allow genuinely optional parameters, with a documented cap of 24 optional parameters across all strict schemas in a request.
Why does my MCP tool name fail the lint?
MCP tool names may be up to 128 characters and may contain dots (admin.tools.list is a spec example). OpenAI and Anthropic require ^[a-zA-Z0-9_-]{1,64}$ — no dots, 64 max. The linter warns rather than errors when the source is an MCP shape, since the name is fine where it lives; it becomes a real error the moment that tool is passed to a provider API directly.
Is this a full JSON Schema validator?
No. It checks the subset LLM providers actually enforce, plus the cross-provider traps — which is different from validating against the full JSON Schema 2020-12 spec. A schema that passes here can still be invalid JSON Schema, and a valid JSON Schema can still be rejected by a provider.
Are my tool definitions uploaded?
No. The page ships a strict Content-Security-Policy: connect-src 'none' blocks fetch, XHR, WebSockets and beacons, while default-src 'self' and form-action 'none' block every other way markup could send data off-site. Conversion and linting happen in your browser and cannot leave it.