When a model calls a tool, your code runs it and writes the result back into the conversation. That write is where portability usually breaks. Each provider defines a different envelope for the result, so a tool wrapped for one API will not parse on another without translation.
The underlying loop is identical everywhere: define tools, send the request, detect the call, execute it, return the result, and repeat. The universal six-step pattern holds across vendors the ofox function calling guide describes, and the four-step version (define, call, execute, return) is the same in TypeScript and Python the digitalapplied comparison. The differences are surface syntax, and the result envelope is the part most teams get wrong.
This matters more than it used to. Multiple providers now speak variants of the same interfaces: Gemini documents an OpenAI-compatible endpoint that works by changing three lines of code the Gemini OpenAI compatibility page, while Anthropic keeps its own content-block model the Claude tool-call docs. If your tools return raw strings and your adapters guess the shape, a model swap turns into a debugging session.
The decision rule
Define the error object once, in your own code. Make every tool return that object or a success value, never a provider-specific blob. Let a thin adapter per provider translate your object into the native envelope at the edge. The rule: if any tool source file mentions a provider name, your abstraction has leaked.



