> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Tool calling

> Define tools, send tool-calling requests, handle tool_use blocks, return tool results, and control tool_choice on Gateway across the native API, OpenAI SDK, and AI SDK.

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`**

```bash title="cURL"
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"]
      }
    }]
  }'
```

**`Python`**

```python title="Python"
import os
from merge_gateway import MergeGateway

client = MergeGateway(api_key=os.environ["MERGE_GATEWAY_API_KEY"])

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"],
    },
}]

response = client.responses.create(
    model="openai/gpt-5.1",
    input=[{"type": "message", "role": "user", "content": "What is the weather in San Francisco?"}],
    tools=tools,
)
```

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

```json
{
  "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`**

```python title="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.

> **Note**
>
> With reasoning on, the assistant turn also carries a signed `thinking` block that you must [replay with its signature](/merge-gateway/capabilities/reasoning#replay-reasoning-in-multi-round-tool-loops).

## 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](/merge-gateway/get-started#use-another-sdk) lists base URLs.

**`AI SDK`**

```typescript title="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_choice`                                 | Behavior                                                                                                       |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Omitted                                       | The 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

#### [Structured outputs](/merge-gateway/capabilities/structured-outputs)

Return JSON that follows a schema

#### [Model catalog](/merge-gateway/models/catalog)

Check model tool-calling support