to_anthropic
Python · package toolnexus · SPEC §4 · python/src/toolnexus/adapters.py
def to_anthropic(tools: list[Tool]) -> list[dict[str, Any]]Turns a list[Tool] into the tools array the Anthropic Messages API expects. It is the flattest
of the three adapters: Anthropic’s tool schema is almost exactly toolnexus’ own Tool, so the
mapping copies three fields and renames nothing.
When to use it
Section titled “When to use it”When you are calling POST /v1/messages yourself — with the anthropic SDK, over httpx, or
through a Bedrock/Vertex gateway that speaks the Anthropic wire format — and need the value to put
in the request’s tools field.
Why this and not the alternative
Section titled “Why this and not the alternative”tk.to_anthropic() on a Toolkit is this same function applied to
that toolkit’s tools — use the method when you have a toolkit, the free function when you have a
bare list (a filtered subset, a hand-built list, one tool).
Examples
Section titled “Examples”1. One tool to Anthropic schema
Section titled “1. One tool to Anthropic schema”Note what does not happen: no {"type": "function"} wrapper, and input_schema keeps its name.
from toolnexus import to_anthropic, define_tool
def get_weather(city: str) -> str: """Current weather for a city.""" return f"sunny in {city}"
weather = define_tool(get_weather, name="get_weather", description="Current weather for a city")
schema = to_anthropic([weather])
assert len(schema) == 1# Three keys, flat — nothing nested under "function".assert sorted(schema[0].keys()) == ["description", "input_schema", "name"]assert schema[0]["name"] == "get_weather"assert schema[0]["description"] == "Current weather for a city"# The key stays `input_schema`, exactly as on Tool — unlike OpenAI's `parameters`.assert schema[0]["input_schema"]["properties"]["city"] == {"type": "string"}
print("ok:", schema[0]["name"])2. Feeding it straight into a Messages request body
Section titled “2. Feeding it straight into a Messages request body”The output is plain dicts, designed to drop into tools verbatim alongside system and messages.
import jsonfrom toolnexus import to_anthropic, define_tool
def search(q: str) -> str: """Search the docs.""" return f"results for {q}"
def ping() -> str: """Health check.""" return "pong"
tools = [ define_tool(search, name="search", description="Search the docs"), define_tool(ping, name="ping", description="Health check"),]
body = { "model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "search for adapters"}], "tools": to_anthropic(tools),}
# Order is preserved, one entry per tool.assert [t["name"] for t in body["tools"]] == ["search", "ping"]# A no-argument tool still carries a valid object schema — Anthropic rejects a bare {}.assert body["tools"][1]["input_schema"]["type"] == "object"assert body["tools"][1]["input_schema"]["properties"] == {}# Plain JSON — serializes with no custom encoder.assert len(json.dumps(body)) > 0
print("ok:", ", ".join(t["name"] for t in body["tools"]))3. Round-tripping a tool_use block back to the tool
Section titled “3. Round-tripping a tool_use block back to the tool”Schema out, tool_use in. Anthropic sends input as a real object, so unlike OpenAI there is
nothing to json.loads — hand it to execute as-is.
import asynciofrom toolnexus import to_anthropic, define_tool
def get_weather(city: str) -> str: """Current weather for a city.""" return f"sunny in {city}"
tools = [define_tool(get_weather, name="get_weather", description="Current weather for a city")]schema = to_anthropic(tools)
# What a model would send back for that schema.block = { "type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": {"city": "Chennai"},}
async def main(): called = next(t for t in tools if t.name == block["name"]) # `input` is already a dict — no parsing step. res = await called.execute(block["input"]) assert res.is_error is False
# What you send back on the next turn. tool_result = { "type": "tool_result", "tool_use_id": block["id"], "content": res.output, "is_error": res.is_error, } assert tool_result["content"] == "sunny in Chennai" assert tool_result["is_error"] is False
# An empty tool list is valid — it just means "no tools this turn". assert to_anthropic([]) == []
print("ok:", schema[0]["name"], "->", tool_result["content"])
asyncio.run(main())| Path | From | Notes |
|---|---|---|
[]["name"] |
Tool.name |
What the model calls back with, in tool_use.name. |
[]["description"] |
Tool.description |
What the model reads to decide whether to call it. |
[]["input_schema"] |
Tool.input_schema |
Copied under the same key — no rename. |
See also
Section titled “See also”to_openai·to_geminicreate_toolkit—tk.to_anthropic()is this, applied to the toolkitcreate_client— calls the adapter for you