Convert LLM tool/function schemas between OpenAI, Anthropic, and Gemini formats — CLI and Node.js library, zero dependencies.
Every major LLM provider stores tool/function schemas differently:
| Provider | Field name | Type casing |
|---|---|---|
| OpenAI | parameters |
"object" (lowercase) |
| Anthropic | input_schema |
"object" (lowercase) |
| Gemini | parameters |
"OBJECT" (UPPERCASE) |
schema-morph handles all 6 conversion directions automatically, including deeply nested objects, arrays, enum, required, and all scalar types.
npm install -g schema-morph
# or use directly
npx schema-morph --help# Convert a single tool from OpenAI → Anthropic format
schema-morph --from openai --to anthropic tool.json
# Pipe JSON directly
echo '{"type":"function","function":{"name":"ping","parameters":{"type":"object","properties":{}}}}' \
| schema-morph --from openai --to anthropic
# Anthropic → Gemini (note: Gemini types become UPPERCASE)
schema-morph --from anthropic --to gemini tool.json
# Gemini → OpenAI
schema-morph --from gemini --to openai tool.json
# Batch mode: one schema per line (JSONL), output is also JSONL
schema-morph --from openai --to anthropic --batch tools.jsonl
# Validate a schema against a format (exit 0 = valid, exit 1 = invalid)
schema-morph --validate --format gemini tool.json| Token | Description |
|---|---|
openai |
OpenAI function/tool schema — both legacy {name, parameters} and new {type:"function", function:{...}} forms |
anthropic |
Anthropic tool schema with input_schema |
gemini |
Gemini function declaration with UPPERCASE type names |
import { convert, convertBatch, validateFormat } from 'schema-morph';
// Convert a single schema
const anthropicTool = convert(openaiTool, { from: 'openai', to: 'anthropic' });
// Convert an array of schemas
const geminiTools = convertBatch(openaiTools, { from: 'openai', to: 'gemini' });
// Validate a schema
const { valid, errors } = validateFormat(tool, 'gemini');
if (!valid) console.error(errors);| Parameter | Type | Description |
|---|---|---|
schema |
object | Source schema object |
from |
string | Source format: 'openai', 'anthropic', or 'gemini' |
to |
string | Target format |
Returns the converted schema object.
Same as convert but takes and returns an array.
Returns { valid: boolean, errors: string[] }.
// Input (OpenAI)
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"]
}
}
}
// Output (Anthropic)
{
"name": "get_weather",
"description": "Get current weather",
"input_schema": {
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"]
}
}// Input (Anthropic)
{
"name": "search",
"input_schema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"limit": { "type": "integer" }
}
}
}
// Output (Gemini — note UPPERCASE types)
{
"name": "search",
"parameters": {
"type": "OBJECT",
"properties": {
"query": { "type": "STRING" },
"limit": { "type": "INTEGER" }
}
}
}| JSON Schema | Gemini |
|---|---|
"string" |
"STRING" |
"number" |
"NUMBER" |
"integer" |
"INTEGER" |
"boolean" |
"BOOLEAN" |
"array" |
"ARRAY" |
"object" |
"OBJECT" |
"null" |
"NULL" |
Nested schemas (objects within objects, arrays of objects) are converted recursively.
When building apps that target multiple LLM providers, you typically define tools once and need them in each provider's format. Doing this manually is tedious and error-prone — especially the Gemini uppercase types, which silently produce wrong schemas if forgotten.
- Node.js ≥ 18
- Zero runtime dependencies
MIT © 2026 Ayubjon
If this tool saved you time, an optional crypto tip is always welcome:
USDT (Ethereum ERC-20): 0xad39bdf2df0b8dd6991150fcea0a156150ed19b8
Verify on Etherscan — send only on the Ethereum (ERC-20) network.