Ashita Orbis

Tool Calling Semantics: Cross-Provider Comparison

Tool calling looks like a simple loop: the model emits a request, the client executes, the result returns, the model continues. The wire formats from OpenAI, Anthropic, and Google can be mapped onto one another in a few hours of adapter code. The semantics — strict-mode contracts, replay obligations, parallel-result formatting, hosted-tool ownership, reasoning continuity, and compatibility-layer leaks — cannot. This article catalogues those differences as they stand in 2026 and identifies the specific axes on which provider choice materially constrains how a tool-calling agent must be architected.

Coverage note: verified through May 2026. Tool-calling semantics are among the most volatile API surfaces in commercial LLM platforms; treat any specific behavior described here as a snapshot rather than a stable contract.

The thesis: tool calling is an execution protocol, not a JSON dialect

Most cross-provider tool-calling comparisons stop at field names. They observe that OpenAI emits function_call items, Anthropic emits tool_use content blocks, and Google emits functionCall parts; that all three accept JSON Schema in some form; that all three define a way to return results back to the model. From there they conclude the differences are cosmetic and that any of these can be wrapped behind a thin adapter.

This conclusion is partially correct and architecturally dangerous in equal measure. It is correct because the conceptual loop — declare tools, receive a tool-call request, execute, return results, continue — is genuinely portable, and was framed this way long before any commercial provider's API existed. ReAct described interleaved reasoning, action, and observation as a model behavior independent of API shape (Yao et al. 2022). Toolformer treated tools as simple APIs the model learns to call (Schick et al. 2023). Gorilla's analysis of API-call failures emphasized that incorrect arguments and hallucinated APIs were the dominant failure modes — not JSON syntax errors (Patil et al. 2023).

It is dangerous because tool calling is not just a request/response shape. It is a protocol with state, replay, ordering, parallelism, and execution-ownership semantics. Choosing a provider commits you to its conventions for each of these unless you build a normalization layer above them. And because providers are evolving these semantics in ways that diverge — OpenAI shifting weight toward the Responses API and hosted tools, Anthropic adding strict tool use and server tools, Google requiring thought signatures for Gemini 3 reasoning continuity — the cost of staying portable is rising.

The practical question this article answers is: when does the choice of provider materially constrain how you architect tool-calling agents? The short answer is that it constrains you most when you depend on hosted tools, reasoning continuity, strict schema guarantees in production, parallel fan-out at scale, multimodal tool results, durable transcript replay, or SDK-managed orchestration. It constrains you least when your tools are simple, single-turn, client-executed, internally validated, and your runtime owns the loop.

Terminology

Cross-provider discussion is hampered by terminology drift. The article uses these terms consistently:

  • Tool declaration: the schema you provide telling the model what tools exist. Each provider calls these something slightly different (tools array of function objects in OpenAI; tools with input_schema in Anthropic; tools containing functionDeclarations in Gemini).
  • Tool call: the model's request to invoke a tool, as it appears in the model output.
  • Tool result: the data returned to the model after executing the tool.
  • Continuation: the next turn of model output that consumes the tool result.
  • Client tool: a tool whose execution happens in your code; you receive the call, run it, and return the result.
  • Hosted tool (also "built-in tool" or "server tool"): a tool whose execution happens inside the provider's infrastructure; you opt in by enabling it but do not write the executor.
  • Adapter quirks: provider-specific shape requirements that affect transport but not architectural behavior.
  • Lock-in points: provider-specific behaviors that change what your runtime must do, not just how it serializes messages.

These distinctions matter because the comparison table below — and the architectural argument that follows — depends on separating syntax from semantics from execution model.

Comparison summary

The table below summarizes the major semantic axes across the three providers. Cells are deliberately qualified rather than yes/no; precision in this domain creates a false impression of equivalence.

Axis OpenAI (Responses API) OpenAI (Chat Completions) Anthropic Messages Google Gemini API
Tool-call representation function_call items in output, paired with function_call_output items containing call_id tool_calls array on assistant message; results returned as messages with role: "tool" and tool_call_id tool_use content blocks within assistant message; results as tool_result blocks in next user message functionCall parts within model Content; results as functionResponse parts
Strict schema mode Strict mode by default for tool schemas in many cases; can be disabled Strict mode opt-in via strict: true; default is lenient Native strict: true with grammar-constrained sampling; separate from OpenAI-compat path Function-calling modes (AUTO, ANY, VALIDATED, NONE) plus responseSchema for output
JSON Schema subset Documented subset; many advanced features unsupported in strict mode Same subset as Responses for strict; broader for non-strict Documented subset for strict mode Documented subset; differs from OpenAI's
Schema conformance scope Argument shape only — values can still be semantically wrong Same Same Same
Result content shape Typically strings; image/file objects supported in some return types Strings via tool role messages String, text/image/document content blocks; is_error flag Structured JSON in functionResponse.response; multimodal parts (Gemini 3)
Parallel calls Multiple function_call items per turn; controllable via parallel_tool_calls Same Multiple tool_use blocks per turn by default; disable_parallel_tool_use flag Parallel and compositional calls supported; formatting affects continuation
Parallel result delivery All function_call_output items returned before continuing Multiple tool messages, each keyed by tool_call_id All tool_result blocks must be in a single user message, before any text All functionResponse parts in single user Content
Identifier scheme call_id per function call; reasoning items also have IDs that must be preserved tool_call_id on tool message must match assistant tool_calls[].id tool_use_id on result must match assistant block id on functionCall matched by functionResponse.id; thought signatures for Gemini 3
Replay obligations Preserve full output items including reasoning items for stateful continuation Preserve assistant message and tool messages in conversation history Preserve tool_use blocks and matching tool_result blocks in their original turns Preserve thought signatures for thinking models; mismatch causes 400
Streaming partial arguments Partial deltas of arguments field Partial deltas of arguments field Fine-grained tool streaming opt-in; partial JSON may be invalid mid-stream partialArgs and willContinue flags on streamed function calls
Hosted tools Web search, file search, code interpreter, image generation, computer use, remote MCP Limited; primarily client tools Web search, code execution, computer use; emit server_tool_use blocks; may require pause_turn continuation Google Search grounding, code execution, URL context; some complete in single call
Mixing hosted and client tools Supported; client tools coexist with hosted tools in the same turn N/A for most hosted tools Supported with caveats around pause_turn Supported in Gemini 3; previously restricted
State persistence Server-side conversation state via previous_response_id None; client maintains history None; client maintains history None for Gemini API; server-side in Vertex Agent Builder
OpenAI compatibility offered N/A (this is OpenAI) N/A OpenAI SDK compatibility endpoint; explicitly for testing, not most production use OpenAI compatibility endpoint; beta; uses extra_body for native features

The table is the cheap part. The rest of the article is the part that determines whether the wrapper around it survives contact with production.

Schema and strict-mode semantics

The most over-claimed part of cross-provider tool calling is "all three providers support strict JSON output." This is true at a sufficiently abstract level and false at the level where decisions actually live.

OpenAI: strict tool schemas, Structured Outputs, and JSON mode

OpenAI distinguishes three schema-related guarantees:

  • JSON mode: the model will produce syntactically valid JSON, but no schema is enforced. Valid JSON can still violate your shape entirely.
  • Structured Outputs (response format): a response_format of type json_schema with strict: true enforces schema adherence in the model's primary output via constrained decoding.
  • Strict tool/function schemas: for tool calling, strict: true on the function definition enforces argument adherence to the declared schema.

The official Structured Outputs announcement is explicit that schema adherence is a syntactic guarantee, not a semantic one (OpenAI Engineering):

Structured outputs do not prevent all kinds of model mistakes. For instance, the model could still make a mistake within the values of the JSON object (e.g. getting the steps in a math problem wrong). Developers should validate the response of the model based on whether they are confident in the model's accuracy and the use case.

Strict mode also has known constraints. Refusals, length-limited truncation, and some advanced JSON Schema features (such as oneOf with overlapping types or unconstrained anyOf) may not be supported in strict mode (OpenAI function calling docs). The Responses API and Chat Completions also differ in their defaults: Responses tends to normalize tool schemas into strict mode unless explicitly opted out, while Chat Completions remains non-strict by default. Treating "OpenAI strict mode" as one uniform behavior across both API surfaces is a documentation-summary trap.

Anthropic: native strict tool use and grammar-constrained sampling

Anthropic's native API now exposes strict tool use via strict: true on the tool declaration. The provider documentation describes this as backed by grammar-constrained sampling, not just post-hoc validation (Anthropic — Strict tool use). The supported JSON Schema subset is documented and is not identical to OpenAI's; nested optional structures, certain allOf/oneOf constructs, and pattern-constrained strings can be supported on one provider and rejected on another.

A practical consequence: if you generate provider-specific schemas from a single source of truth, you will hit cases where the schema accepted by Anthropic is rejected by OpenAI's strict mode or vice versa. The fix is usually a per-provider "schema flattener" that lowers expressive features to the intersection of supported subsets, or per-provider hand-tuning of the strictest schema variant.

Google Gemini: function-calling modes

Gemini exposes schema constraint differently. The tool_config.functionCallingConfig.mode field accepts:

  • AUTO: model decides whether and which function to call.
  • ANY: model must call one of the declared functions.
  • VALIDATED: function calls are constrained to declared schemas (constrained decoding).
  • NONE: function calling is disabled.

Output schemas for non-tool-calling structured generation are configured separately via responseSchema (Gemini structured output). The interaction between VALIDATED mode, ANY mode, and the supported JSON Schema subset is documented per-feature rather than collapsed into a single "strict" knob, which makes it more expressive but more error-prone for adapter code.

What strict actually buys you

The Empiricist position deserves reproducing in plain language: strict mode reduces the rate of malformed-argument errors substantially but does not eliminate semantic errors. Tool-calling benchmarks like τ-bench show pass-at-1 rates under 50% for state-of-the-art function-calling models, with pass^8 (consistency across eight repetitions) under 25% in retail tasks (Yao et al. 2024). Live API-Bench reports task completion rates of 7–47% depending on configuration, improving to roughly 50% under interactive agent settings (Tang et al. 2024). The Berkeley Function-Calling Leaderboard (BFCL) V4 distinguishes native function-calling pass rates from prompt-based workarounds and is updated periodically; live-leaderboard scores remain well below 100% for production-grade tasks (BFCL leaderboard).

The implication: strict schemas are necessary but not sufficient. Architectures that assume strict mode is "safe enough to skip validation" are betting against measured base rates. A robust runtime validates argument values, not just shapes — checking that account_id exists, that amount is positive, that until_date is in the future — and treats an argument-validation failure as a tool error to be returned to the model, not as an exception to be raised at the caller.

Call representation: messages, content blocks, parts

The wire-level shape of a tool call differs across providers in ways that matter for transcript persistence and ordering.

OpenAI Responses API: items in an output array

The Responses API returns an output array containing typed items: message, function_call, reasoning, and others (OpenAI function calling). A turn that calls a tool produces a function_call item with a call_id, name, and arguments (JSON-string-encoded). To continue, the client sends back function_call_output items keyed by the same call_id. For reasoning models (o-series and successors), reasoning items must also be preserved across turns to maintain state.

OpenAI Chat Completions: tool_calls on assistant messages

Chat Completions uses a different structure: the assistant message has a tool_calls array, and tool results return as messages with role: "tool" and a tool_call_id matching one of the assistant's tool_calls[].id values. Multiple tool messages can follow a single assistant message that emitted multiple tool calls. This is the format most "OpenAI-compatible" gateways implement.

The two surfaces are not interchangeable. Migrating from Chat Completions to Responses is documented as a non-trivial change (OpenAI — Migrate to Responses) precisely because the item-based representation, the handling of reasoning items, the default strictness, and the hosted-tool integration all differ.

Anthropic Messages: content blocks

Anthropic's representation is content-block-based. An assistant message contains a list of content blocks, which may include text and tool_use. The tool_use block carries an id, name, and input (already a parsed JSON object, not a string). The client returns a user message containing one or more tool_result blocks, each carrying a tool_use_id matching the original tool_use.id, plus content (string or structured blocks) and an optional is_error flag (Anthropic — Handle tool calls).

Two ordering constraints matter:

  1. The user message containing tool_result blocks must immediately follow the assistant message that emitted the corresponding tool_use blocks. Inserting other turns between them is disallowed.
  2. Within that user message, all tool_result blocks must come before any text block.

Anthropic's documentation describes this contrast explicitly: where some APIs use separate tool or function roles, Claude integrates tool interactions directly into the existing user and assistant roles. This is not a cosmetic difference — it changes what your conversation store has to model.

Gemini: parts within Content

Gemini structures messages as Content objects containing a list of Parts. A model Content may include text, functionCall, and thoughtSignature parts. The client responds with a user Content containing functionResponse parts. Each functionCall carries a name, args (object), and an id; each functionResponse carries a matching id, the name, and a response object containing the result (Gemini function calling).

For Gemini 3 thinking models, thoughtSignature parts encode reasoning state and must be preserved across turns when continuing function-call interactions. Omitting required signatures is documented to produce 400 errors. This is structurally similar to OpenAI's reasoning items but uses opaque signatures attached to specific parts rather than separate items in an output array.

Why representation matters

A naive event normalization layer might collapse all three into:

AssistantText(text)
ToolCallRequested(provider_id, name, args, metadata)
ToolResultProvided(provider_id, payload, error?)

This works for the loop. It does not work for replay or persistence unless metadata is large enough to round-trip provider-specific structure: reasoning items, thought signatures, content-block ordering, and the assistant-message-vs-output-item distinction. Most teams who attempt full normalization end up either (a) storing the raw provider response alongside the normalized event, or (b) re-rendering provider-shaped messages from the event log on each continuation. Both patterns work; the failure mode is teams who normalize aggressively, lose provider-specific state, and discover at the second turn that they cannot continue the conversation.

Continuation and replay

The most consequential semantic difference across providers is what continuation requires. This is also where folk wisdom — "just keep the conversation history and re-send it" — is most often wrong.

Correlation IDs are not transaction IDs

Every provider attaches an identifier to tool calls: call_id (OpenAI), tool_use.id (Anthropic), functionCall.id (Gemini). These are correlation tokens for matching results to calls within a turn. They are not durable transaction IDs, not exactly-once tokens, and not safe primitives for replay across distinct invocations. If your tool has side effects — sending email, charging a card, writing to a database — the model can and will replay the call under retry or branching scenarios. The provider IDs do not protect you. Your runtime needs:

  • Its own idempotency keys passed to side-effecting tools.
  • An effect ledger that records "for transcript X at turn N, tool Y was invoked with arguments Z, producing result R."
  • Replay logic that consults the ledger before re-executing.

The failure mode here is subtle. A team uses OpenAI call_id as a deduplication key for a send_invoice tool, observes that within a single turn it works correctly, then fails to handle a case where the user retries the agent run from scratch — generating new call_id values for what should be the same logical operation. The provider ID is local to one model invocation; idempotency is a property of your domain model.

Reasoning items and thought signatures

OpenAI reasoning models emit reasoning items in the Responses API output. These represent intermediate thinking that the model produces between user input and final output. For continuation across turns — particularly turns that include tool use — these items must be preserved and replayed back to the model (OpenAI function calling). Discarding them changes what the model sees and can degrade subsequent reasoning quality.

Gemini 3 thinking models use a different mechanism: thoughtSignature parts attached to model output. Omitting required signatures from a continuation request produces 400 errors. This is documented as a hard contract (Vertex AI function calling). Anthropic Claude's extended thinking (Anthropic — Extended Thinking, where applicable) similarly produces thinking blocks that should be preserved for continuity.

These mechanisms are conceptually parallel but not interoperable. There is no "reasoning items" abstraction that round-trips across all three providers. A portable abstraction layer that elides reasoning state may work for the next turn but will degrade reasoning continuity over multi-turn agent runs. The pragmatic pattern is to store the provider-native raw turn alongside any normalized representation, and replay the raw turn when continuing within the same provider session.

Anthropic's ordering constraints

Anthropic's requirement that tool_result blocks immediately follow the tool_use turn — and that they precede any text in the user message — is sometimes treated as a parsing curiosity. It is more than that. It encodes a state-machine constraint: an assistant turn that emits tool-use blocks is incomplete until the matching results arrive. Anthropic's documentation indicates that incorrect formatting can degrade subsequent tool-use behavior, including reduced parallel-tool-call propensity in later turns (Anthropic — Parallel tool use).

This means your transcript store cannot freely interleave turns. If you want to model "user said something during tool execution," you cannot insert that as a user turn between the tool-use assistant turn and the tool-result user turn. The right pattern is either to buffer the user input until tool execution completes, or to treat the user input as a new conversation branch (not a continuation of the in-progress tool turn).

What "replay" actually means

Replay has three distinct meanings that are often conflated:

  1. Within-turn correlation: matching tool results back to the calls that requested them. Correlation IDs handle this.
  2. Across-turn continuation: providing the model enough context to continue reasoning. This requires preserving provider-specific items (reasoning, thought signatures, content-block structure) — not just the user-facing message log.
  3. Cross-session replay (resuming after a crash, debugging, or retrying with a different model): reconstructing the full state of an agent run. This requires your own event log and effect ledger; provider mechanisms are not designed for it.

A common architecture pattern is to use the provider's stateful conversation feature — OpenAI's previous_response_id, Vertex Agent Builder's session APIs — for within-session continuation, while maintaining your own durable log for cross-session replay. Mixing the two is fine; relying on either alone is not. Provider-side state is typically tied to retention windows and is not a substitute for your own audit log.

Parallelism

Parallel tool calls are widely advertised and easily over-promised. The careful framing separates capability, behavior, and orchestration.

Capability: can the API return multiple calls per turn?

All three providers support emitting multiple tool calls in a single model turn:

  • OpenAI returns multiple function_call items in one output, controllable via parallel_tool_calls (OpenAI function calling).
  • Anthropic returns multiple tool_use blocks in one assistant message; disable_parallel_tool_use opts out (Anthropic — Parallel tool use).
  • Gemini supports parallel function calling and "compositional" calling, where one model turn emits multiple functionCall parts (Gemini function calling).

Behavior: how often does the model use this capability?

Capability is not behavior. Anthropic's parallel-tool documentation explicitly notes that incorrectly formatted past parallel results can reduce future parallel-tool propensity. OpenAI documents that parallel function calls are not available with built-in tools in some configurations. Models also vary in how aggressively they parallelize: smaller or older models tend to chain calls sequentially even when independent.

This means parallelism is partly a transcript-shaping problem. If your past turns return parallel results in the wrong order, in separate user messages instead of one, or with mismatched IDs, the model may degrade to sequential calling in subsequent turns. Architectures that depend on aggressive parallelism for latency need to verify, at the transcript level, that they are returning results in the format the provider expects.

Orchestration: who is the scheduler of record?

Parallel tool calls expose an architectural question that is easy to defer and expensive to defer too long: when the model emits three independent calls, who runs them?

The naive answer — "execute them in parallel" — works until you encounter:

  • Tools with conflicting effects (two writes to the same resource).
  • Tools with rate-limited external dependencies.
  • Tools that should fail-fast (cancel the other two if one errors).
  • Tools with priority (run the cheap one first; if it fails, skip the expensive ones).
  • Effect classification: read tools are safely parallelizable; write tools may not be.

The model is not a scheduler. It does not know your rate limits, your cost model, or your conflict graph. The scheduler should live in your runtime, with explicit dependency analysis, effect classification, retries, idempotency, and concurrency limits. Provider parallelism is a hint to your scheduler, not a delegation of scheduling. This is true regardless of provider — but it is more visible on Anthropic, where the formatting requirements force you to confront the orchestration question; less visible on OpenAI Chat Completions, where independent tool messages can be returned in any order; and least visible on managed orchestration features like OpenAI's Tool Runner where the SDK handles the loop for you.

Parallel result delivery

Whatever your scheduler does, the result-delivery format is provider-specific:

  • Anthropic: all tool_result blocks must be in a single user message, before any text. Splitting them across multiple turns or interleaving text is disallowed.
  • Gemini: functionResponse parts go in a single user Content.
  • OpenAI Responses: all function_call_output items are returned together before continuing.
  • OpenAI Chat Completions: each result is a separate tool message; ordering is more flexible but each must reference its tool_call_id.

If you build your runtime to return results one at a time as they complete (e.g., to minimize latency), you will have to buffer until all results are ready before sending the continuation to Anthropic or Gemini. OpenAI Chat Completions tolerates incremental delivery via separate messages. This is one of the cleaner cases where the wire format directly constrains your dispatcher's flush policy.

Streaming partial arguments

Streaming tool-call arguments is a subtler differentiator than the binary "supports streaming" question suggests.

OpenAI's streaming API returns partial deltas of the arguments field as the model generates them. By default, the JSON is only guaranteed to be valid once the call finishes. Both Responses and Chat Completions stream this way.

Anthropic offers fine-grained tool streaming as an opt-in mode. The provider documentation is direct that partial arguments may be invalid JSON mid-stream and that consuming partial arguments requires a parser that handles incremental, possibly malformed input (Anthropic — Fine-grained tool streaming):

When using fine-grained tool streaming, partial JSON may be invalid or malformed, and may not parse until streaming is complete.

Google Gemini exposes streaming function-call arguments with partialArgs and willContinue flags, indicating when a part is mid-stream and when it will continue (Vertex AI function calling).

Why this matters architecturally: there are tools where you genuinely want to begin execution before the full argument object exists. A common case is starting a long-running search or remote API call as soon as the query string is known, before the model finishes generating the (unrelated) max_results field. Doing this requires:

  1. A schema that lets you know which arguments are "execution-triggering" vs "configuration-only."
  2. A streaming parser that emits structured partial events as fields complete.
  3. A cancellation path if the model later changes course or the final arguments are invalid.

Few teams need this. The teams that do — voice agents with tight latency budgets, exploratory search agents, real-time orchestration — find that provider differences in partial-argument streaming directly determine whether the optimization is buildable. OpenAI partial deltas without explicit field-completion semantics are harder to work with than Gemini's partialArgs/willContinue markers; Anthropic's fine-grained streaming is closer to OpenAI's model. None of them should be assumed equivalent.

Result rendering: strings, blocks, parts, multimodal

Tool results are not all the same shape.

  • OpenAI: tool outputs are typically strings (often JSON-stringified). Some return types accept image or file objects, but the canonical form is a string. The model interprets stringified JSON as structured if the surrounding prompt makes it clear.
  • Anthropic: tool_result content can be a string, or an array of content blocks including text, image, and document. The is_error: true flag explicitly marks a result as an error, and the model is documented to treat error results differently from successful ones (Anthropic — Handle tool calls).
  • Gemini: functionResponse.response is a structured object, not a string. For Gemini 3, functionResponse parts can include multimodal content alongside the structured response.

The architectural consequence: if your tool returns mixed content — a query result plus an attached PDF, an image plus extracted text — the cleanest rendering differs per provider. On Anthropic, you return a multi-block tool_result. On Gemini, you return a multi-part functionResponse. On OpenAI, you typically string-encode the structured part and either embed images via a separate mechanism or pass file IDs that the model already has access to.

A portable abstraction is possible but lossy. If you flatten everything to "tool result is a string," you give up image and document attachments on Anthropic and Gemini. If you model results as "structured payload plus optional attachments," you can render this faithfully on Anthropic and Gemini and degrade to JSON-stringified payload on OpenAI. The latter is the better default: model your domain output with the most expressive provider in mind, and downgrade in the adapter rather than upgrade.

Error semantics are another provider-specific axis. Anthropic's is_error flag is direct. OpenAI and Gemini have no equivalent flag — error rendering is by convention (an {"error": "..."} object in the result) and the model relies on text content to recognize it. The convention works in practice but is provider-dependent: Anthropic's flagged errors trigger somewhat different model behavior than unflagged JSON errors on OpenAI.

Built-in tools versus client tools

This is the axis where provider choice constrains architecture most decisively, and the axis most often glossed over in cross-provider comparisons.

A client tool is a function you implement: the model emits a call, you receive it, you run code, you return the result. A built-in tool — also called a server tool or hosted tool — is executed inside the provider's infrastructure: you opt in, the model invokes it, the provider runs it, and the result flows back into the conversation without touching your code.

The portable conceptual loop ("model emits, client executes, result returns") does not apply to built-in tools. The execution boundary moves. So do trust, observability, billing, latency, custody, replay, and compliance.

OpenAI: hosted tools in the Responses API

The Responses API exposes a set of platform-hosted tools: web search, file search, code interpreter, image generation, computer use, and remote MCP servers (OpenAI — Migrate to Responses). When the model invokes one of these, OpenAI executes it server-side and inserts the result into the response output. From the client's perspective, the tool call and result happen within a single API request — no client-side execution loop is needed for the hosted tool.

Hosted tools in OpenAI also coexist with client tools in the same turn. The model can emit a hosted tool call and a client tool call simultaneously; the hosted call resolves server-side while the client call returns to your code for execution.

Anthropic: server tools and pause_turn

Anthropic exposes server tools — including web search, code execution, and computer use — that emit server_tool_use blocks in the assistant output (Anthropic — Server tools). For some long-running server tools, the response may end with a stop_reason: "pause_turn", signaling that the model has not yet completed its turn and the client should resume the request to continue execution. This is a different control flow from OpenAI's hosted tools: the client must explicitly handle pause_turn and resume, rather than receiving a single complete response.

Mixing server and client tools is supported but adds control-flow complexity: the client loop now has three exit conditions per turn (final response, client tool call, pause-turn for server tool continuation) instead of two.

Gemini: built-in tools and combination semantics

Gemini's built-in tools include Google Search grounding, code execution, and URL context. Some of these complete within a single API call and surface their results directly; others integrate via grounding metadata that is structurally distinct from functionCall/functionResponse parts (Gemini tools). Gemini 3 expanded support for combining built-in and custom function-calling tools in the same request, removing earlier restrictions.

The semantic models differ noticeably:

  • OpenAI hosted tools feel most like "client tools that run in the cloud."
  • Anthropic server tools introduce the pause_turn control flow.
  • Gemini built-ins lean toward grounding as a side-channel rather than as a tool-call-shaped artifact.

Why hosted tools are an architectural decision

Hosted tools change what your runtime owns and what it can control:

Property Client tool Hosted tool
Execution location Your infrastructure Provider infrastructure
Custody of inputs and outputs Your logs Provider logs (subject to retention policy)
Ability to mock for tests Trivial Difficult or impossible
Latency profile Your network Provider's hosted network
Billing Your costs (e.g., search API fees) Provider's metered cost
Auditability Whatever your logs capture Whatever the provider exposes
Replay across providers Portable if your tool is Not portable; behavior is provider-specific
Failure modes Whatever your code produces Provider outages, rate limits, content policy
Observability Your tracing Provider's exposed metadata
Compliance posture Yours Joint, with provider-defined boundaries

The Skeptic's framing is correct: client function calling is mostly an adapter problem above a portable internal protocol. The Architect's framing is also correct: hosted tools are not adapter-level. They are a different deployment topology with different trust and observability properties. If you depend on OpenAI's code_interpreter for sandboxed execution, you cannot trivially port to Anthropic — Anthropic's code execution server tool has a different control flow, different cost model, and (importantly) different supported language and library surface. Your dependency is not on "tool calling" but on "this specific hosted tool's behavior."

The architectural rule of thumb: hosted tools are commitments to a provider, not adapters above a provider. If you want portability, prefer client tools that wrap your own implementations of search, code execution, and retrieval. Use hosted tools when their convenience, cost, or capability is decisive — knowing you have committed to that provider's runtime for those capabilities.

Compatibility layers and what they leak

Both Anthropic and Google publish OpenAI-compatible endpoints. They are useful for migration smoke-tests, multi-provider evals, and minimum-effort fallback paths. They are not semantic equivalence layers, and treating them as such is a recurring source of production incidents.

Anthropic's OpenAI SDK compatibility

Anthropic explicitly positions its OpenAI SDK compatibility as a testing/comparison surface, not a production substrate (Anthropic — OpenAI SDK compatibility):

The OpenAI compatibility layer is designed to make it easier to compare Claude with other models. We recommend using the native Anthropic API for most production use cases.

The page enumerates concrete behavioral gaps. Among them: the strict parameter on function definitions is ignored. So is response_format. Many other OpenAI-specific fields are silently accepted but not honored. This means a request that works correctly against OpenAI strict mode — and depends on strict guarantees — will appear to work against Anthropic via the compatibility endpoint, but without the strict guarantee in force. The failure is silent.

Native Anthropic features also do not surface fully through the compatibility endpoint: server tools, Anthropic-native strict mode, content-block structure, and is_error semantics are either absent or flattened. The compatibility endpoint exposes a Chat-Completions-shaped subset of Anthropic capabilities; the native shape is required to access the rest.

Gemini's OpenAI compatibility

Google's Gemini OpenAI compatibility is similarly scoped (Gemini — OpenAI compatibility). The documentation recommends calling Gemini's native API directly if you are not already on OpenAI SDKs. Native Gemini features — thought signatures, grounding metadata, function-calling modes, multimodal functionResponse parts — are exposed (where exposed at all) via extra_body parameters and Gemini-specific extensions. The endpoint is labeled beta in current documentation. Like Anthropic's surface, it accepts an OpenAI-shaped request but does not promise behavioral equivalence.

The "OpenAI-compatible" gateway problem

The pattern extends beyond Anthropic and Google. A wide ecosystem of "OpenAI-compatible" gateways — proxies, model routers, third-party hosts — accepts OpenAI-shaped JSON and forwards to whatever underlying model. The problem with treating all of these as a single architecture target is that "OpenAI-compatible" describes request shape, not behavior. Equivalence on:

  • strict-mode adherence,
  • parallel-tool-call behavior,
  • streaming partial-argument semantics,
  • tool-call ID stability across retries,
  • structured-output validation,
  • error semantics for invalid arguments

is not implied by the gateway accepting an OpenAI-shaped request. Each of these axes can — and does — diverge silently.

The pragmatic discipline is:

  1. Architect your tool-calling layer around native APIs, not compatibility endpoints.
  2. Use the compatibility endpoints only for testing or for genuinely interchangeable, low-stakes calls.
  3. Where you must use a compatibility endpoint in production, write conformance tests against the specific behaviors you depend on (strict mode, parallel results, streaming, ID stability).
  4. Treat fields silently accepted but not honored as a class of bug. They will not surface as errors; they will surface as occasional production failures attributable to "model weirdness."

Empirical reliability: what benchmarks tell us about the loop

Provider docs describe contracts. Benchmarks describe behavior. The gap between them is large enough to be architecturally relevant.

τ-bench evaluates tool-calling agents in retail and airline domains. Its headline result is that state-of-the-art function-calling models pass under 50% of tasks at pass-at-1, and pass^8 (consistency across eight independent attempts) drops below 25% in retail. The benchmark is designed to expose the difference between "the agent succeeded once" and "the agent reliably succeeds" (Yao et al. 2024).

Live API-Bench evaluates agents against real, mutating APIs over time, with task completion rates from 7% to 47% under static configurations and improving to roughly 50% under interactive agent settings (Tang et al. 2024). The benchmark's design — APIs that evolve, deprecate, and rate-limit — exposes failure modes that static benchmarks miss.

The Berkeley Function-Calling Leaderboard (BFCL V4) is a periodically updated benchmark that distinguishes native function-calling pass rates from prompt-based workarounds (BFCL leaderboard). It emphasizes parallel and multi-step function calling, with frontier models still scoring well below 100% on production-grade scenarios.

ToolBench and similar large-tool-surface benchmarks include thousands of tools (3,451 tools and 16,464 APIs across 49 categories in the original ToolBench) and demonstrate that performance degrades as the candidate tool surface grows (Qin et al. 2023). Retrieval-style tool selection becomes a separate problem from tool-calling itself once your registry exceeds a few dozen tools.

Three implications for the architecture question:

  1. Reliability is not a provider property to rank from docs. Benchmark scores depend on model version, prompt structure, schema design, and retry policy. The same provider can produce wildly different reliability on different schemas. Architecture decisions that depend on relative provider reliability must be backed by local evals on the actual workload.
  2. Strict schemas are necessary but not sufficient. Even with strict mode, semantic argument errors persist. A robust runtime validates argument values, not just shapes, and treats validation failures as tool errors returned to the model rather than exceptions raised to the caller.
  3. Multi-turn and multi-tool runs fail substantially more than single-tool single-turn demos. Provider docs typically illustrate the simple case. Production architectures need to assume and budget for retry, branching, and failure recovery as the common path, not the exception.

The Empiricist's recommendation deserves promotion to a habit: before writing strong architectural claims about a provider's tool-calling fitness for your workload, build a 30–60 minute conformance harness. Run 10–20 fixed prompts, three repeats each, across the semantic axes you depend on — strict schemas, parallel calls, sequential dependency, result rendering, replay integrity, mixed hosted/client tools where applicable. Log raw requests and responses, validation failures, 400 codes, turn counts, latency, and final-answer correctness. The harness will surface provider-specific failure modes that no documentation alone exposes.

When provider choice materially constrains architecture

This is the practical question, and it has a structured answer rather than a categorical one. Provider choice constrains your architecture along the following axes:

Constrains heavily (architectural lock-in)

  • Hosted-tool dependence. If your agent depends on OpenAI's code_interpreter, Anthropic's web search server tool, or Gemini's grounding, you have committed to that provider's runtime for that capability. There is no portable abstraction. Migration requires reimplementing the capability as a client tool or accepting the loss.
  • Reasoning continuity for thinking models. OpenAI reasoning items, Anthropic extended thinking blocks, and Gemini thought signatures are not interoperable. If your agent depends on multi-turn reasoning continuity from a thinking model, switching providers requires re-architecting the continuation layer.
  • SDK-managed orchestration. OpenAI's automatic tool-runner helpers, Anthropic's tool-loop SDK conveniences, and Gemini's automatic function-calling all hide the loop from your code. Convenient when adopted; a rewrite when changed. Teams that lean on these features are committing to their provider's SDK as the orchestrator of record.
  • Server-side conversation state. OpenAI's previous_response_id and Vertex Agent Builder sessions persist conversation state on the provider's side. Migration requires either reconstructing state from your own log or accepting a discontinuity.
  • Compatibility-layer production use. Building production on Anthropic or Google's OpenAI-compatible endpoints commits you to the leakage profile of those endpoints. Strict mode silently ignored, tool-call IDs handled differently, native features inaccessible — these are durable constraints, not transient migration costs.

Constrains moderately (adapter complexity)

  • Strict schema guarantees. Using strict mode is portable in spirit but not in detail; the supported JSON Schema subsets differ enough that schema-generation logic must be provider-aware. A schema that compiles strictly on OpenAI may need to be rewritten for Anthropic and again for Gemini.
  • Parallel tool result formatting. The requirement that Anthropic and Gemini deliver all parallel results in a single user message constrains your dispatcher's flush policy; OpenAI Chat Completions tolerates incremental delivery. If your scheduler runs tools as they complete, you need provider-aware buffering.
  • Streaming partial arguments for low-latency execution. OpenAI delta-only streaming, Anthropic fine-grained streaming, and Gemini partialArgs/willContinue are not interchangeable for execution-before-completion patterns. A latency-optimized agent must adapt per provider.
  • Multimodal tool results. Anthropic content blocks, Gemini multimodal parts, and OpenAI's typically-string outputs differ in what they can faithfully render. Portable architectures degrade rather than upgrade — model the most expressive representation and downsample in the adapter for OpenAI.

Constrains lightly (mostly adapter syntax)

  • Single-turn client function calls. Translating between function_call, tool_use, and functionCall is mechanical. An internal event model with ToolCallRequested(provider_id, name, args) covers all three.
  • Simple result rendering as strings or JSON. If your tools return text or JSON-encoded payloads, all three providers accept this with minor adapter logic.
  • Sequential tool calls. Sequential invocation — one call, one result, then the next — is portable across all three providers with negligible adapter work.
  • Basic retry on transient errors. All three providers expose enough error metadata to drive your own retry layer. Provider-specific 429 and 5xx handling is well-documented.

A decision frame

If your agent depends on items from the "constrains heavily" list, your architecture is provider-shaped whether you intend it to be or not. The honest move is to accept the dependency, document it, and design your code with that provider as the orchestrator of record — including for transcript persistence, replay, and observability.

If your agent depends only on items from the "constrains lightly" list, a provider-neutral substrate is genuinely cheap to build and pays for itself the first time you run an eval that compares providers head-to-head.

If your agent depends on items from the "constrains moderately" list, the question is whether you want to invest in per-provider adapter code or accept a single-provider architecture for the affordances you depend on. There is no universal right answer; the calibration depends on how much portability you actually need and how stable each provider's behavior is in your workload.

The portable substrate

A workable provider-neutral runtime sits below the provider adapters and above your domain code. It does not try to abstract every semantic difference; it abstracts the loop and exposes provider-specific behavior through metadata. A reasonable shape:

TurnEvent
├── AssistantText(text)
├── AssistantReasoning(opaque_blob, provider)         // reasoning items, thought signatures, thinking blocks
├── ToolCallRequested(provider_id, name, args, metadata)
│       metadata: { provider, raw_block, hosted: bool, ... }
├── ToolResultProvided(provider_id, payload, attachments, error?, metadata)
│       payload: structured object
│       attachments: list of typed media blocks
└── AssistantFinal(text, citations?)

The runtime tracks:

  • A transcript log of these events, with raw provider responses preserved alongside the normalized form. The raw responses are what get replayed back to providers; the normalized events are what your domain code consumes.
  • An effect ledger indexed by your own idempotency keys, recording each side-effecting tool invocation and its result. This is consulted before re-executing.
  • A scheduler that classifies incoming ToolCallRequested events by effect type (read/write/external), applies dependency graphs and concurrency limits, and dispatches to executors.
  • An executor registry of client tools, each with a typed signature, a timeout, a retry policy, and an effect classification.
  • A provider adapter per supported provider that handles serialization (events → provider-specific request shape), parsing (provider response → events), and ordering constraints (e.g., Anthropic's tool_result-before-text requirement, Gemini's signature preservation).

Hosted tools are the load-bearing exception. Rather than abstracting them, the runtime should treat them as provider-specific capabilities exposed via opt-in flags: enable_openai_code_interpreter, enable_anthropic_web_search. The runtime knows that turning on a hosted tool commits the agent to that provider for that capability; downstream code can check this and refuse to multiplex across providers when hosted tools are in use.

This is more architecture than the simple cases warrant and less than a fully provider-agnostic agent platform. It is calibrated to the observation that 80% of tool-calling complexity is portable, 15% is per-provider adapter work, and 5% is genuine lock-in that should be acknowledged rather than abstracted. The 5% is where most architectural pain originates when teams attempt aggressive abstraction; it is also where most performance and capability advantages live for teams that commit to a provider.

Compatibility-layer leakage in detail

Returning to compatibility layers with the architectural frame in hand: the question is not whether they accept your request shape, but whether they preserve the semantics your runtime depends on. The audit checklist:

  • Does strict: true on tool definitions enforce schema constraints? Anthropic's compatibility endpoint documents that it does not. Verify on Gemini and any third-party gateway before depending on strict guarantees.
  • Are tool-call IDs stable across streaming and non-streaming requests? This affects parallel result delivery and replay. Test by issuing the same request streamed and unstreamed; compare IDs.
  • Are parallel tool calls supported, and in what order are results expected back? Compatibility layers may flatten parallel calls to sequential or accept results in non-OpenAI orderings.
  • Is response_format honored? Anthropic's compatibility endpoint documents that it is not. Many third-party gateways silently ignore it.
  • Are tool_choice semantics preserved? "Required," "auto," and "none" are documented in OpenAI's docs but vary in compatibility layers.
  • Are streaming partial arguments emitted in a parseable, OpenAI-equivalent format? Compatibility layers often buffer until completion, breaking latency-sensitive patterns.
  • Is the n parameter (multiple completions per request) honored? Many compatibility surfaces do not implement this.
  • What happens to provider-native fields (Anthropic is_error, Gemini grounding metadata, OpenAI reasoning items)? They are typically dropped. Architectures that rely on them must use native APIs.

The pattern is consistent: compatibility layers accept an OpenAI-shaped request, route it to the underlying provider, and silently drop or reinterpret fields that do not map cleanly. Architectures that depend on those fields will fail in production in ways that look like model misbehavior. The cleanest fix is also the simplest: use native APIs for production paths, and use compatibility surfaces only for testing or low-stakes parallel calls.

What to write down before choosing a provider

The decision-relevant questions, in order of architectural weight:

  1. Will you depend on hosted tools? If yes, name them, name the provider, and accept that you have made a multi-year commitment. Reconsider whether your runtime should attempt portability for non-hosted-tool paths or just commit fully.
  2. Will your agent use a thinking/reasoning model? If yes, you must preserve provider-specific reasoning state across turns. There is no portable substitute. Plan transcript storage to keep raw provider items, not just normalized events.
  3. Do you need strict schema guarantees in production for tools? If yes, decide whose schema subset is your source of truth and accept per-provider schema rewriting for the others. Run a conformance harness on the subset you actually use.
  4. Do you need parallel tool execution at scale? If yes, build your scheduler explicitly. Do not let the model's emission rate drive your concurrency. Buffer results for Anthropic and Gemini's single-message delivery requirements.
  5. Do you need partial-argument streaming for latency? If yes, accept that this is among the most provider-specific axes. A portable abstraction is unlikely to be worth it; commit to the provider whose streaming model best matches your needs.
  6. Will tool results carry images, documents, or structured multimodal payloads? If yes, model your domain output for the most expressive provider (Anthropic content blocks or Gemini multimodal parts) and degrade in the OpenAI adapter.
  7. Will you persist transcripts for replay, audit, or evaluation? If yes, store raw provider responses alongside normalized events. Do not rely on provider-side conversation state as your durable record.
  8. Will side-effecting tools be invoked? If yes, build your own idempotency ledger keyed by your own transaction IDs. Do not use provider tool-call IDs for cross-session deduplication.

Answers to these questions determine architecture more than the choice of provider does. A team that answers them clearly can pick any provider and build a robust agent. A team that defers them will discover the answers in production, at higher cost and lower flexibility.

Closing observation

The cross-provider tool-calling story in 2026 is one of converging surface and diverging substrate. All three major providers have settled on roughly the same conceptual loop. All three offer strict-mode constraints, parallel calls, hosted tools, streaming, and OpenAI-compatible endpoints. The naive comparison says they are interchangeable.

The substrate, where engineering decisions live, is not converging. OpenAI is investing in the Responses API, hosted tools, and stateful continuation. Anthropic is investing in content-block fidelity, server tools with pause_turn, and native strict mode. Google is investing in thought signatures, grounding, and Gemini-specific multimodal function responses. The compatibility surfaces between them are explicitly scoped as testing aids, not production substrates.

The practical engineering posture is to assume the conceptual loop is portable, the wire format is adapter work, and the substrate is provider-shaped. Build a runtime that owns the loop, the schedule, the ledger, and the transcript. Adapt per provider for serialization. Acknowledge the lock-in points — hosted tools, reasoning continuity, server-side state, SDK-managed orchestration — as durable architectural commitments rather than papering over them with abstractions that will leak the first time a turn needs to continue.

Tool calling is an execution protocol with provider-specific extensions. The article that flattens it into "all three look the same with different field names" is producing technical debt for the team that takes it seriously. The article that treats every provider difference as a lock-in fault is producing premature abstraction. The honest position is the calibrated one: name the axes that matter for your workload, decide per axis whether to abstract or commit, and write conformance tests for whichever path you take.

Companion entries

Core theory:

  • ReAct Loops and Action-Observation Interleaving
  • JSON Schema Dialects in LLM APIs
  • Agent State Machines and Replay Semantics
  • Tool Use vs Tool Calling: Conceptual Lineage from Toolformer to MCP
  • Constrained Decoding and Grammar-Constrained Sampling

Practice:

  • Provider-Neutral Agent Runtime Design
  • Idempotent Tool Execution and Effect Ledgers
  • Agent Retry and Recovery Patterns
  • Tool Registry Architecture and Schema Generation
  • Transcript Persistence for LLM Agents
  • Streaming Tool Arguments for Low-Latency Agents
  • Conformance Harnesses for Multi-Provider Agent Development
  • Hosted Tools as Architectural Commitments

Counterarguments and limits:

  • The Cost of Premature Provider Abstraction
  • When SDK-Managed Orchestration Wins
  • Tool Calling Reliability: What Benchmarks Actually Measure
  • Compatibility Layers and the OpenAI-Shaped Gateway Problem

Adjacent topics:

  • Context Window Economics for Tool-Calling Agents
  • Multi-Model Agent Orchestration Patterns
  • Structured Output Enforcement Across Providers
  • MCP Protocol Semantics and Cross-Provider Adoption
  • Reasoning Models and Multi-Turn Continuity
  • Computer Use and Browser Tool Architectures

AI-researched reference article. Something wrong here? Tell us.