Tools and Function Calling
Tools are how an agent stops being a text generator and starts acting. The chat API offers
two dispatch models, and the difference is WHO executes: tools declares functions YOUR
code runs, server_tools names capabilities THIS SERVER runs inside the exchange. They
compose freely with each other, with skills, and with the
agent bundle that can carry a tool set as its default.
1Client-dispatched: tools#
Standard function calling, the shape every OpenAI-compatible SDK already speaks: each entry
declares a name, a description, and a JSON Schema for the arguments. When the model decides
to call one, the exchange ends with reason tool_calls and the arguments; your code executes
whatever the function means (query your database, call your internal API), appends the result
as a tool role message, and continues the conversation. The server never executes these and
never needs to know what they do, which is exactly the point: this is the integration path
for capabilities that live in YOUR system.
tool_choice steers the decision: auto (default) lets the model decide, none forbids
calls, required demands one, and {"name": "..."} forces a specific tool, the right lever
when a turn exists only to produce one structured call.
2Server-executed: server_tools#
server_tools names tools by string (web_search, calc_arithmetic, document tools), and
the server runs them inside the exchange: the model calls, the server executes, the result
returns to the model, and the loop continues without a round-trip to your code. What makes
this operable in production is that the catalog is CURATED and GOVERNED:
- The catalog is deliberate. It carries safe computation, web search, allowlisted HTTP, and the server's own document engine: the same tools its MCP endpoint serves, so one hardened implementation answers both surfaces. File access opens only through the operator's configured ingest roots; nothing reaching arbitrary disk paths or processes is exposed at all.
- Policy decides what actually runs. The admin Tools section enables tools individually, shows each tool's declared side effect ("reads files", "writes to the web", pure computation), and carries per-tool options. A request naming a disabled tool is not an error and not a silent skip: the refusal is narrated to the model, which answers accordingly.
- Web access is a mode, not a hope. Anything that reaches the network runs under the server's egress policy, so "the agent can search" is a decision the operator made, with an allowlist when wanted.
3MCP connectors: your tools, everyone's protocol#
Connectors attach external Model Context Protocol
servers as tool providers: http for streamable HTTP endpoints, stdio for a local process
speaking MCP on its pipes. A connector's tools join the server-executed catalog under
qualified names (docs/pdf_inspect for connector docs), and allowed_tools narrows the
offer per connector, so plugging in a third-party MCP server never means accepting its whole
surface. This is the growth path: when the agent needs your CRM, your ticketing system, or
your data warehouse, you stand up (or reuse) an MCP server for it and the tools appear in
the same catalog, same policy, same observability. The reverse direction, OTHER agents
consuming THIS server's document tools, is the MCP endpoint.
4The loop, bounded and observable#
Server-side tool use is a loop: reason, call, observe, repeat. Three properties keep it production-grade:
- Bounded:
max_tool_calls(per request or per agent) caps how many calls one turn may spend before the model is asked to answer with what it has, so a confused agent converges instead of spinning. - Observable live: streaming exchanges emit
tool_useevents as calls happen, so a UI can show "searching the web" while it is true. - Auditable after: the result summarizes every call as
tool_events, and the exchange itself lands in the requests log like any other, so "what did the agent actually do" is a lookup, not a reconstruction.
5Choosing the dispatch model#
| Situation | Use |
|---|---|
| The capability lives in your system (your database, your API) | tools, executed by your code |
| The capability is generic and the server has it (web, calculation, documents) | server_tools |
| The capability is a service that already speaks MCP, or should | A connector, consumed through server_tools |
| One turn must return one structured call | tools with a forced tool_choice |
Mixing is normal: a support agent can carry server_tools for search and documents while
your application declares tools for order lookup, in one request.
6Stated plainly#
- Two dispatch models, one rule: your code executes
tools, the server executesserver_tools, and the model composes both in one loop. - The server-side catalog is curated, policy-gated, side-effect-labeled, and shared with the MCP endpoint; connectors extend it with your own systems under per-connector allowlists.
- The loop is bounded by budget, streamed as events, and summarized in the result: agents act, and you can always see exactly how.