Function Calling
Function calling is what turns a chat model into a component: the model decides WHEN your code should run and with WHAT arguments; your code does the work and reports back. This server serves the loop on every dialect, with the OpenAI shape as the reference contract, so existing agent loops, framework tool abstractions, and hand-rolled dispatchers run against local models unchanged. This guide is the client-side loop; tools the SERVER executes on the model's behalf are the agent platform's job instead.
1One loop, three dialects#
| Dialect | Declaration and calls |
|---|---|
OpenAI (/v1/chat/completions) |
tools with function entries, tool_choice, calls arrive as tool_calls with finish_reason: "tool_calls", results return as tool role messages |
Anthropic (/v1/messages) |
tools with input schemas, tool_choice (auto, any, none, or a specific tool), calls arrive as tool_use content blocks, results return as tool_result blocks |
Responses (/v1/responses) |
Flat function tools with tool_choice, calls arrive as function_call items, results return as function_call_output items (Responses and Vector Stores) |
The loop is the same everywhere: declare, generate, execute what the model called, append the result, generate again until the model answers in prose.
2The contract, precisely#
- Arguments arrive whole. A call's arguments JSON is delivered complete (streaming included), not as fragments you must assemble, so a dispatcher can parse and run on arrival.
- Arguments follow the declared parameter schema. Generation is constrained the same way structured outputs are, which is why calls parse instead of arriving as almost-JSON.
tool_choiceis honored: auto lets the model decide, required/anyforces a call, none disables calling for the turn, and naming one tool forces that tool. Forcing a tool the request does not declare is refused up front by name.- The description is the interface. The model chooses tools by reading names, descriptions, and parameter descriptions; a vague description costs you wrong calls before any code runs. Write them like documentation for a fast, literal-minded colleague.
3What makes calls reliable#
- Pick a tool-capable model. Tool calling is a trained skill: the catalog marks models with the tools capability, and choosing one without it buys disappointment. For custom imports, the server probes tool calling at import time and refuses tool-carrying requests on a model whose probe failed, with the reason, instead of letting it emit garbage calls.
- Fewer, sharper tools beat many vague ones. Every declared tool costs selection accuracy; the same discipline this server applies to its own MCP catalog applies to yours.
- Cap your loop. The model decides when it is done; your dispatcher should still bound iterations and treat an unexpected call as a signal to stop, not to obey.
- Results are model input. What your function returns becomes context: return compact, factual payloads, and treat anything a tool fetched from the outside world as untrusted content, not instructions.
4Two tool worlds, deliberately separate#
Everything above is CLIENT-dispatched: the model asks, YOUR process executes. This server also runs tools itself in two governed places, and the distinction is a security boundary:
- Server tools on chat (
server_tools, an extension field on the chat dialects): the curated built-ins executed server-side under the tools policy and egress gate. - Agent-platform tools: what a named agent may run, defined by the operator, not the request.
Client-dispatched functions need no server-side permission, because the server never executes them; it only relays the model's intent to the process that declared the tool.
5Stated plainly#
- Declare tools in your dialect's shape and the loop runs on local models unchanged; the wire behavior matches what your framework already expects.
- Arguments arrive whole and schema-constrained, so parsing is not the risky part; tool SELECTION quality is, and it follows your descriptions and your model choice.
- Custom models are probed for tool calling at import and refuse honestly when they cannot.
- Client functions are yours to execute and secure; server-executed tools are a different, operator-governed surface.