Install
$ agentstack add mcp-np-compete-toolglot ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
toolglot Define tools once. Use them with any model.
Quick Start • Why ToolGlot • Format Zoo • 30+ Models • LangGraph • Provider Cookbook • Contributing • Production Hardening • Security
You wrote 15 MCP tools. They work perfectly with GPT-4o. Then your boss says "make it work with Claude, Gemini, Llama, and that 2B model running on a Raspberry Pi."
Every model speaks a different tool dialect. OpenAI wants functions. Anthropic wants tools with input_schema. Gemini wants function_declarations. Cohere wants flat parameters. Ollama needs it in the chat template. That 2B model? It needs tools injected into the system prompt and responses parsed from freeform text.
ToolGlot translates between all of them.
Your Tools ──→ [ ToolGlot ] ──→ Any Model, Any Provider, Any Format
↑ │
MCP / OpenAPI └──→ OpenAI, Anthropic, Gemini, Mistral, Cohere,
/ JSON Schema Bedrock, Ollama, vLLM, or plain system prompt
Quick Start
pip install toolglot
Translate tool definitions from the CLI:
# MCP → OpenAI format
toolglot translate --from mcp --to openai --input tools.json --output openai_tools.json
# OpenAPI spec → Anthropic format
toolglot translate --from openapi --to anthropic --input petstore.yaml
# Inspect what a model supports
toolglot capabilities --model gpt-4o
# Validate a tool definition
toolglot validate --format openai --input tools.json
# Compare two schemas and detect lossy changes
toolglot schema-diff --left-input a.json --left-format mcp --right-input b.json --right-format openai --output json
Scaffold a provider plugin:
toolglot plugin init --name acme --output-dir ./plugins
Automation-friendly CLI modes:
# Machine-readable output
toolglot capabilities --model gpt-4o --output json
toolglot validate --format mcp --input tools.json --output json
toolglot import-mcp --config ./mcp_config.json --output json
# Logging control for scripts
toolglot inspect --format mcp --input tools.json --quiet
toolglot translate --from mcp --to openai --input tools.json --verbose
Or in Python — define once, export everywhere:
from toolglot import ToolKit
# Load from any source
toolkit = ToolKit.from_mcp("./my_tools.json")
# Export to any target — one line each
openai_tools = toolkit.to_openai()
anthropic_tools = toolkit.to_anthropic()
gemini_tools = toolkit.to_gemini()
mistral_tools = toolkit.to_mistral()
cohere_tools = toolkit.to_cohere()
bedrock_tools = toolkit.to_bedrock()
ollama_tools = toolkit.to_ollama(model="llama3.2")
prompt_text = toolkit.to_system_prompt() # any model, zero native support needed
# Parse tool calls back from any provider
from toolglot import parse_tool_calls
calls = parse_tool_calls(response, provider="anthropic")
for call in calls:
print(call.name, call.arguments) # unified format, always
Why ToolGlot
The problem exists in pieces. Nobody assembled the solution.
| What You Need | Existing Solutions | The Problem | |---|---|---| | Provider-agnostic tool definitions | LiteLLM | Full proxy. Couples you to their runtime. You wanted a library, not a service. | | Tool format translation | LangChain tool abstraction | Locked into the LangChain ecosystem. Not standalone. | | Multi-provider tool calling | Vercel AI SDK | TypeScript only. Frontend-focused. | | Schema optimization for small models | Nothing | Nobody downgrades complex schemas for weaker models. | | Tool calling for non-native models | Nothing | If the model doesn't support tools natively, you're on your own. |
ToolGlot is the missing primitive. A standalone Python library that translates tool definitions between formats, optimizes schemas per model, parses responses back to a unified format, and makes tool calling work on models that don't natively support it.
┌─────────────┐ ┌───────────┐ ┌───────────┐ ┌──────────┐
│ Import │────▶│ Canonical │────▶│ Transform │────▶│ Export │
│ MCP/OpenAPI │ │ IR │ │ per model │ │ per prov │
└─────────────┘ └───────────┘ └───────────┘ └──────────┘
│ │ │ │
Your tool defs Pydantic models Flatten, deref, OpenAI, Claude,
in any format (the truth) simplify, validate Gemini, Ollama...
The Format Zoo
Here's the same tool — get_weather — in six different formats. This is what ToolGlot handles for you.
Your MCP tool definition (input):
{
"name": "get_weather",
"description": "Get current weather for a city",
"inputSchema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" },
"units": { "type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius" }
},
"required": ["city"]
}
}
OpenAI format — wraps in type: "function", nests under function.parameters:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" },
"units": { "type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius" }
},
"required": ["city"]
}
}
}
Anthropic format — uses input_schema instead of parameters:
{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" },
"units": { "type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius" }
},
"required": ["city"]
}
}
Gemini format — uppercase types, no default, nested under function_declarations:
{
"function_declarations": [{
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "OBJECT",
"properties": {
"city": { "type": "STRING", "description": "City name" },
"units": { "type": "STRING", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}]
}
Cohere format — flat parameter_definitions, Python types, no nesting:
{
"name": "get_weather",
"description": "Get current weather for a city",
"parameter_definitions": {
"city": { "type": "str", "description": "City name", "required": true },
"units": { "type": "str", "description": "Temperature units: celsius or fahrenheit. Default: celsius", "required": false }
}
}
System prompt fallback — for models with zero native tool support:
You have access to the following tools:
## get_weather
Get current weather for a city
Parameters:
- city (string, required): City name
- units (string, optional): Temperature units. One of: celsius, fahrenheit. Default: celsius
When you want to call a tool, respond with:
{"name": "get_weather", "arguments": {"city": "Tokyo"}}
Six formats. Same tool. ToolGlot handles all of this with one line of code.
Features
Import From Anywhere
from toolglot import ToolKit
# MCP tool definitions (JSON)
toolkit = ToolKit.from_mcp("./mcp_tools.json")
# MCP server config (connects and lists tools)
toolkit = ToolKit.from_mcp_server("./mcp_server_config.json")
# OpenAPI / Swagger spec
toolkit = ToolKit.from_openapi("./petstore.yaml")
# OpenAI function format
toolkit = ToolKit.from_openai([{"type": "function", "function": {...}}])
# Raw JSON Schema
toolkit = ToolKit.from_json_schema({"get_weather": {...}})
# LangChain tools (requires toolglot[langchain])
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""Get weather for a city."""
...
toolkit = ToolKit.from_langchain([get_weather])
Supported input formats:
| Source | Method | Notes | |---|---|---| | MCP tool definitions | ToolKit.from_mcp("tools.json") | JSON file with MCP tool array | | MCP server config | ToolKit.from_mcp_server("config.json") | Connects to server, lists tools | | OpenAPI 3.x spec | ToolKit.from_openapi("spec.yaml") | Extracts operations as tools | | OpenAI function format | ToolKit.from_openai(tools_list) | List of {"type": "function", ...} | | JSON Schema | ToolKit.from_json_schema(schemas) | Dict of name → schema | | LangChain tools | ToolKit.from_langchain(tools) | List of BaseTool |
Export To Any Model
toolkit = ToolKit.from_mcp("./tools.json")
# Cloud providers
openai_tools = toolkit.to_openai() # GPT-4o, GPT-4.1, o1, o3-mini
anthropic_tools = toolkit.to_anthropic() # Claude Sonnet 4, Claude 3.5 Haiku
gemini_tools = toolkit.to_gemini() # Gemini 2.5 Pro, Gemini 2.0 Flash
mistral_tools = toolkit.to_mistral() # Mistral Large, Codestral
cohere_tools = toolkit.to_cohere() # Command R+, Command A
# Cloud platforms
bedrock_tools = toolkit.to_bedrock() # Any model on AWS Bedrock
# Azure OpenAI and Vertex AI use the same format as OpenAI/Gemini respectively
# Local / self-hosted
ollama_tools = toolkit.to_ollama(model="llama3.2") # Chat-template-aware
vllm_tools = toolkit.to_vllm() # Guided generation format
# Universal fallback
prompt_text = toolkit.to_system_prompt() # Works with ANY model
Parse Responses Back
Every provider returns tool calls differently. ToolGlot normalizes them.
from toolglot import parse_tool_calls, ToolCall
# OpenAI: tool_calls[].function.arguments (JSON string)
calls = parse_tool_calls(openai_response, provider="openai")
# Anthropic: content[].type=="tool_use", input (dict)
calls = parse_tool_calls(claude_response, provider="anthropic")
# Gemini: candidates[].content.parts[].function_call
calls = parse_tool_calls(gemini_response, provider="gemini")
# Mistral: tool_calls[].function (similar to OpenAI, subtly different)
calls = parse_tool_calls(mistral_response, provider="mistral")
# Cohere: tool_calls[].name + parameters
calls = parse_tool_calls(cohere_response, provider="cohere")
# Freeform text (for system prompt fallback)
calls = parse_tool_calls(raw_text, provider="freeform")
# Every call is the same type regardless of source
for call in calls:
assert isinstance(call, ToolCall)
print(call.id, call.name, call.arguments)
For streaming chunks/events:
from toolglot import parse_stream_tool_calls
calls = parse_stream_tool_calls(openai_stream_chunks, provider="openai")
calls = parse_stream_tool_calls(anthropic_stream_events, provider="anthropic")
Schema Transforms
Not all models handle complex schemas. ToolGlot downgrades intelligently.
from toolglot.transforms import flatten, simplify, deref, validate
# Flatten nested objects (required for Cohere)
# {passengers: {adults: int, children: int}} → {passengers_adults: int, passengers_children: int}
flat_toolkit = flatten(toolkit)
# Resolve $ref pointers (required for Gemini)
resolved_toolkit = deref(toolkit)
# Simplify for small models (remove anyOf, simplify enums, add descriptions)
simple_toolkit = simplify(toolkit, target_model="phi4-mini")
# Validate a tool call against the schema
result = validate(tool_call, toolkit)
if not result.valid:
print(result.errors)
When transforms are applied automatically:
| Transform | Auto-applied For | Why | |---|---|---| | flatten | Cohere | Only supports flat parameter_definitions | | deref | Gemini | No $ref support | | simplify | Edge models via mode="simplified" | Complex schemas confuse small models | | All three | mode="auto" | ToolGlot picks based on capability matrix |
Capability Matrix
ToolGlot knows what every model can do. Query it programmatically or from the CLI.
from toolglot import capabilities
caps = capabilities("gpt-4o")
print(caps.native_tools) # True
print(caps.parallel_calls) # True
print(caps.streaming) # True
print(caps.strict_mode) # True
print(caps.max_tools) # 128
toolglot capabilities --model claude-sonnet-4
# native_tools: true
# parallel_calls: true
# streaming: true
# strict_mode: false
# max_tools: 500+
# recommended_mode: native
Full matrix (30+ models):
| Model | Provider | Native Tools | Parallel | Streaming | Strict | Max Tools | Recommended Mode | |---|---|---|---|---|---|---|---| | GPT-4o | OpenAI | Yes | Yes | Yes | Yes | 128 | native | | GPT-4o-mini | OpenAI | Yes | Yes | Yes | Yes | 128 | native | | GPT-4.1 | OpenAI | Yes | Yes | Yes | Yes | 128 | native | | GPT-4.1-mini | OpenAI | Yes | Yes | Yes | Yes | 128 | native | | GPT-4.1-nano | OpenAI | Yes | Yes | Yes | Yes | 128 | native | | o1 | OpenAI | Yes | Yes | No | Yes | 128 | native | | o3-mini | OpenAI | Yes | Yes | No | Yes | 128 | native | | o4-mini | OpenAI | Yes | Yes | No | Yes | 128 | native | | Claude Sonnet 4 | Anthropic | Yes | Yes | Yes | No | 500+ | native | | Claude 3.5 Haiku | Anthropic | Yes | Yes | Yes | No | 500+ | native | | Gemini 2.5 Pro | Google | Yes | Yes | Yes | No | 128 | native | | Gemini 2.0 Flash | Google | Yes | Yes | Yes | No | 128 | native | | Mistral Large | Mistral | Yes | Yes | Yes | No | 64 | native | | Mistral Small | Mistral | Yes | Yes | Yes | No | 64 | native | | Codestral | Mistral | Yes | Yes | Yes | No | 64 | native | | Command R+ | Cohere | Yes | No | No | No | 40 | native (flat) | | Command A | Cohere | Yes | No | No | No | 40 | native (flat) | | DeepSeek V3 | DeepSeek | Yes | Yes | Yes | No | 128 | native | | DeepSeek R1 | DeepSeek | No | No | No | No | -- | system_prompt | | Grok 3 | xAI | Yes | Yes | Yes | No | 128 | native | | Grok 3 Mini | xAI | Yes | Yes | Yes | No | 128 | native | | Llama 4 Maverick | Meta | Yes | Yes | Yes | No | 64 | native | | Llama 4 Scout | Meta | Yes | Yes | Yes | No | 64 | native | | Llama 3.3 70B | Groq / Together | Yes | Yes | Yes | No | 64 | native | | Llama 3.2 3B | Ollama | Template | No | No | No | ~10 | simplified | | Llama 3.2 1B | Ollama | Template | No | No | No | ~5 | system_prompt | | Phi-4-mini | Ollama | Template | No | No | No | ~10 | simplified | | Qwen 2.5 7B | Ollama | Template | No | No | No | ~15 | native | | Qwen 2.5 3B | Ollama | Template | No | No | No | ~10 | simplified | | Mistral 7B | Ollama | Template | No | No | No | ~10 | native | | Gemma 2 9B | Ollama | No | No | No | No | -- | system_prompt | | Gemma 2 2B | Ollama | No | No | No | No | -- | system_prompt | | DeepSeek R1 8B | Ollama | No | No | No | No | -- | system_prompt | | QwQ 32B | Ollama | No | No | No | No | -- | system_prompt | | SmolLM2 1.7B | Ollama | No | No | No | No | -- | system_prompt |
Key:
- Native: Model has built-in tool calling API
- Template: Tools injected via chat template (Ollama/vLLM)
- No: No native support — use
system_promptmode - Max Tools: Approximate practical limit before quality degrades
LangGraph Integration
This is where ToolGlot becomes essential. create_react_agent breaks when you switch models. ToolGlot fixes that.
pip install "toolglot[langchain]"
Same Agent, Any Model
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_mistralai import ChatMistralAI
from langchain_cohere imp
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [NP-compete](https://github.com/NP-compete)
- **Source:** [NP-compete/toolglot](https://github.com/NP-compete/toolglot)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.