| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Function → JSON Schema, once, everywhere.
Python's answer to the gap TypeScript solved with Zod + Standard Schema. Write a typed function. Export one schema. Use it in OpenAI, Anthropic, Gemini, MCP, LangChain, FastMCP, and Pydantic AI — without rewriting.
Every agent framework generates tool JSON differently. FastMCP is MCP-only. LangChain infers its own schema. OpenAI strict mode wants every field required. Claude Desktop breaks on $ref. toolschema is Layer 1 only: introspect a function once, adapt at the edge.
pip install toolschemaExtras:
pip install toolschema[fastmcp] # FastMCP
pip install toolschema[langchain] # LangChain StructuredTool
pip install toolschema[openai-agents] # OpenAI Agents SDK
pip install toolschema[pydantic-ai] # Pydantic AI Tool.from_schema
pip install toolschema[all] # all integrationsRequires Python 3.10+. Core depends on stdlib only (typing_extensions on 3.10).
Documentation: https://toolschema.readthedocs.io
from typing import Annotated
from toolschema import tool, schema, Field
@tool
def get_weather(
city: Annotated[str, Field(description="City name")],
units: str = "celsius",
) -> dict:
"""Get current weather for a city."""
return {"city": city, "temp": 22, "units": units}
t = schema(get_weather)
t.to_openai()
t.to_mcp()
t.to_anthropic()Works without @tool on any typed function with a docstring:
def add(a: int, b: int = 1) -> int:
"""Add two integers."""
return a + b
t = schema(add)from fastmcp import FastMCP
from toolschema import schema
from toolschema.integrations.fastmcp import register_tool
from myapp.tools import greet, add
mcp = FastMCP("my-server")
register_tool(mcp, schema(greet), greet)
register_tool(mcp, schema(add), add)
mcp.run()from toolschema import schema
from toolschema.integrations.langchain import from_toolschema
from myapp.tools import search
definition = schema(search)
tool = from_toolschema(definition, search)
result = tool.invoke({"query": "laptop", "limit": 5})from toolschema import schema
from toolschema.integrations.openai_agents import to_agents_function_tool
from myapp.tools import add
definition = schema(add)
agents_tool = to_agents_function_tool(definition, add)from pydantic_ai import Agent
from toolschema import schema
from toolschema.integrations.pydantic_ai import from_toolschema
from myapp.tools import add
definition = schema(add)
tool = from_toolschema(definition, add)
agent = Agent("openai:gpt-4o", tools=[tool])toolschema inspect myapp.tools:search --format mcp
toolschema inspect myapp.tools:search --format openai,mcp,anthropic
toolschema diff myapp.tools:search --targets openai,mcp
toolschema export myapp.tools
toolschema init my-mcp-serverBuild the canonical tool IR from a function signature, type hints, defaults, and docstring.
Required
Type: Callable
Any typed callable, or @tool-decorated function.
from toolschema import schema
t = schema(my_function)
t.name
t.description
t.parameters # JSON Schema 2020-12 object
t.output # return type schema, or NoneOptional decorator. Overrides name or description; does not change call semantics.
@tool(name="web_search", description="Search the web.")
def search(query: str) -> list[dict]: ...Metadata for Annotated parameters. Maps to JSON Schema constraints.
from typing import Annotated
from toolschema import Field
city: Annotated[str, Field(description="City name", min_length=1)]Plain string in Annotated sets description only: Annotated[str, "City name"].
OpenAI function-calling payload: {"type": "function", "function": {...}}.
Type: boolean
Default: false
When true, sets additionalProperties: false and lists every property in required.
Anthropic Messages API shape: {"name", "description", "input_schema"}.
Numeric and string constraints (minLength, pattern, etc.) are folded into property description text.
MCP tools/list shape: name, description, inputSchema, optional outputSchema.
Type: boolean
Default: true
When true, flatten $ref / $defs before return. Required for Claude Desktop and VS Code Copilot.
Google Gemini FunctionDeclaration shape. JSON Schema type values are uppercased (STRING, INTEGER, …). Parameters only; no output schema in v1.0.
Thin argument check against parameters. Returns ValidationSuccess with defaults applied, or ValidationFailure with ValidationIssue list.
from toolschema import ValidationSuccess
result = t.validate({"city": "London"})
if isinstance(result, ValidationSuccess):
get_weather(**result.value)Register a pre-built ToolDefinition on a FastMCP server without regenerating schema inside FastMCP.
Required
Type: fastmcp.FastMCP
Required
Type: ToolDefinition
Required
Type: Callable
Build a StructuredTool with infer_schema=False and args_schema=tool.parameters.
Build an OpenAI Agents SDK FunctionTool with on_invoke_tool wired to fn.
Also: function_tool_kwargs(), to_openai_agent_tool(), invoke_agents_tool(), invoke_agents_tool_sync().
Build a Pydantic AI Tool via Tool.from_schema() and the pre-built JSON Schema.
Also: to_pydantic_ai_tool(), prepare_toolset().
Scaffold an MCP server project from the packaged template. Runs slugify_package_name() on the directory name for the Python package.
Internal dialect: JSON Schema 2020-12. Parameters object always has additionalProperties: false.
{
"name": "add",
"description": "Add two integers.",
"parameters": {
"type": "object",
"properties": {
"a": { "type": "integer" },
"b": { "type": "integer", "default": 1 }
},
"required": ["a"],
"additionalProperties": false
}
}| Python | JSON Schema |
|---|---|
| str, int, float, bool | string, integer, number, boolean |
| T | None | anyOf: [schema(T), {"type": "null"}] |
| list[T], dict[str, T] | array, object + additionalProperties |
| Literal, Enum | enum |
| Annotated[T, Field(...)] | constraints on property |
| TypedDict, @dataclass, Union, tuple | object, anyOf, prefixItems |
| default value | "default" key; omitted from required |
| return annotation | output / MCP outputSchema |
All adapters read from ToolDefinition only. Schema is not generated twice inside an adapter.
Function FastMCP LangChain OpenAI -------- ------- --------- ------ @mcp.tool() → MCP JSON rewrite rewrite LC @tool → rewrite LC schema rewrite hand-written → drift drift drift
| Solution | OpenAI | Anthropic | MCP | LangChain | FastMCP | Lock-in free |
|---|---|---|---|---|---|---|
| Framework @tool | partial | partial | partial | partial | partial | no |
| Hand-written JSON Schema | manual | manual | manual | manual | manual | yes |
| toolschema | yes | yes | yes | yes | yes | yes |
toolschema export and toolschema inspect import user-specified modules. Only point them at code you trust.
validate() checks arguments against generated schema; it does not sandbox tool execution. Your agent framework still runs the callable.
| Path | Description |
|---|---|
| examples/01_basic.py | @tool, schema(), adapter output |
| examples/02_mcp_server.py | FastMCP stdio server + --check smoke test |
| examples/03_langchain.py | LangChain from_toolschema + invoke |
| examples/04_multi_provider.py | One function, all provider formats |
| examples/demo_tools.py | Reusable tools module |
| examples/verify_package.py | PyPI install verification |
| examples/deep_agents_demo.py | Cross-framework integration smoke test |
MIT. See LICENSE.
| Back | FazBrowse Home | New Git URL |