Every AI framework invented its own way to define tools. Same function. Same parameters. Six different JSON schemas. Here's what that actually looks like.
Each card below defines the exact same get_weather(city, unit) tool.
Notice the highlighted differences -- every framework uses different key names for the same thing.
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The city name"},
"unit": {"type": "string", "description": "Temperature unit (celsius or fahrenheit)", "default": "celsius"}
},
"required": ["city"]
}
}
}
{
"name": "get_weather",
"description": "Get current weather for a city.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The city name"},
"unit": {"type": "string", "description": "Temperature unit (celsius or fahrenheit)", "default": "celsius"}
},
"required": ["city"]
}
}
{
"name": "get_weather",
"description": "Get current weather for a city.",
"parameters": {
"type": "OBJECT",
"properties": {
"city": {"type": "STRING", "description": "The city name"},
"unit": {"type": "STRING", "description": "Temperature unit (celsius or fahrenheit)"}
},
"required": ["city"]
}
}
{
"name": "get_weather",
"description": "Get current weather for a city.",
"inputSchema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The city name"},
"unit": {"type": "string", "description": "Temperature unit (celsius or fahrenheit)", "default": "celsius"}
},
"required": ["city"]
}
}
from langchain.tools import StructuredTool from pydantic import BaseModel, Field class GetWeatherInput(BaseModel): city: str = Field(description="The city name") unit: str = Field(default="celsius", description="Temperature unit") def get_weather(city: str, unit: str = "celsius") -> dict: return {"temp": 22, "unit": unit, "city": city} weather_tool = StructuredTool.from_function( func=get_weather, name="get_weather", description="Get current weather for a city.", args_schema=GetWeatherInput, )
from crewai.tools import tool @tool("get_weather") def get_weather(city: str, unit: str = "celsius") -> dict: """Get current weather for a city. Args: city: The city name unit: Temperature unit (celsius or fahrenheit) """ return {"temp": 22, "unit": unit, "city": city} # Note: CrewAI tools only work within CrewAI
Four names for the same thing. "OBJECT" vs "object".
Wrapped in "function" or not.
None of this is your problem to solve.
Write a normal Python function. Add one decorator. Get every format.
from agent_friend import tool @tool def get_weather(city: str, unit: str = "celsius") -> dict: """Get current weather for a city. Args: city: The city name unit: Temperature unit (celsius or fahrenheit) """ return {"temp": 22, "unit": unit, "city": city} # One definition. Every format. get_weather.to_openai() # OpenAI function calling get_weather.to_anthropic() # Claude tool use get_weather.to_google() # Gemini function declarations get_weather.to_mcp() # Model Context Protocol get_weather.to_json_schema() # Raw JSON Schema
{"type": "function", "function": {"name": ...}}
{"name": ..., "input_schema": {...}}
{"name": ..., "parameters": {"type": "OBJECT"}}
{"name": ..., "inputSchema": {...}}
The Toolkit class bundles multiple tools and exports them as a list for any framework.
from agent_friend import tool, Toolkit @tool def get_weather(city: str) -> dict: ... @tool def send_email(to: str, body: str) -> bool: ... @tool def search_docs(query: str, limit: int = 10) -> list: ... kit = Toolkit([get_weather, send_email, search_docs]) kit.to_openai() # list of 3 OpenAI tool defs kit.to_anthropic() # list of 3 Anthropic tool defs kit.to_mcp() # list of 3 MCP tool defs
Switch from OpenAI to Anthropic to Gemini without rewriting your tool definitions. Your logic stays the same. Only the export changes.
No pydantic. No langchain. No framework imports. Just Python's standard library. The @tool decorator adds nothing you wouldn't write yourself.
File I/O, HTTP, JSON, CSV, crypto, templates, metrics, and more. All tested. All exportable. Use them directly or as reference implementations.
Run python mcp_server.py and expose all 314 tools via the Model Context Protocol. Connect it to Claude Desktop or any MCP client.
Every tool. Every export format. Every edge case. The test suite is the specification. If a format changes, the tests catch it first.
Use it however you want. Fork it. Vendor it. Embed it. The code is on GitHub and it's yours.
$ pip install "git+https://github.com/0-co/agent-friend.git[all]"
v0.49.0 · Python 3.8+ · Zero external dependencies · MIT license