Skip to navigation

Tool calling

Let models call functions in your application

Tool calling lets a model ask your application to run a function. You send tool definitions, the model returns a tool_use block with arguments, and you run the function and send the result back.

Send a request with tools

A native tool is a flat object with type, name, description, and a JSON Schema parameters. The TypeScript SDK takes the same snake_case fields, such as tool_choice.

curl https://api-gateway.merge.dev/v1/responses \
-H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.1",
"input": [{"type": "message", "role": "user", "content": "What is the weather in San Francisco?"}],
"tools": [{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a location.",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string", "description": "City name"}},
"required": ["location"]
}
}]
}'

When the model calls a tool, the output message has finish_reason: "tool_use" and a tool_use block:

{
"output": [{
"role": "assistant",
"finish_reason": "tool_use",
"content": [
{"type": "tool_use", "id": "call_abc123", "name": "get_weather", "input": {"location": "San Francisco"}}
]
}]
}

Send the result back

Echo the assistant turn with all its blocks, then add a tool_result for each tool_use. content is a string or a list of content blocks.

Python
tool_call = response.output[0].content[0]
weather = get_weather(tool_call.input["location"]) # your function, returns a string
follow_up = client.responses.create(
model="openai/gpt-5.1",
input=[
{"type": "message", "role": "user", "content": "What is the weather in San Francisco?"},
{"type": "message", "role": "assistant", "content": [
{"type": "tool_use", "id": tool_call.id, "name": tool_call.name, "input": tool_call.input},
]},
{"type": "tool_result", "tool_use_id": tool_call.id, "content": weather},
],
tools=tools,
)
print(follow_up.output[0].content[0].text)

The model may return several tool_use blocks in one turn; send a result for each, since a missing or unmatched result fails with a 400 naming the ID. Gateway fills in a tool-call ID when a vendor omits one, and doesn’t validate arguments against your schema.

With reasoning on, the assistant turn also carries a signed thinking block that you must replay with its signature.

Other SDKs

The OpenAI SDK at /v1/openai takes the Chat Completions tool shape ({"type": "function", "function": {...}}), and the Anthropic SDK at /v1/anthropic takes Anthropic’s own. With the AI SDK, use merge-gateway-ai-sdk-provider (v5 apps import from merge-gateway-ai-sdk-provider/v5) and define tools with inputSchema. Use another SDK lists base URLs.

AI SDK
import { createMergeGateway } from "merge-gateway-ai-sdk-provider";
import { generateText, tool } from "ai";
import { z } from "zod";
const gateway = createMergeGateway({ apiKey: process.env.MERGE_GATEWAY_API_KEY });
const { text } = await generateText({
model: gateway("openai/gpt-5.1"),
prompt: "What is the weather in San Francisco?",
tools: {
getWeather: tool({
description: "Get the current weather for a location.",
inputSchema: z.object({ location: z.string().describe("City name") }),
execute: async ({ location }) => ({ location, temperature: 72, condition: "sunny" }),
}),
},
});

Reference

tool_choiceBehavior
OmittedThe vendor’s default, which is auto on mainstream vendors
"auto"The model decides whether to call a tool
"none"The model calls no tools
"required"The model must call at least one tool
{"type": "function", "name": "get_weather"}The model must call that tool. The nested {"type": "function", "function": {"name": "..."}} form also works.

parallel_tool_calls (boolean) is accepted on /v1/responses.

anthropic/claude-fable-5-1 and anthropic/claude-opus-5-5 reject forced tool use: "required" or a named tool returns 400 unsupported_params with param: tool_choice. Use "auto" and name the tool in the prompt.

Gateway checks tool support on the exact route before calling the vendor; a route without it returns 400 unsupported_params naming tools or tool_choice. Check supports_tool_calling and supports_tool_choice in vendors.<vendor>.capabilities on GET /v1/models.

Next steps