Trial
0007. Server-side tools as opaque content
Trial
- Status: Trial
- Date: 2026-10-30
Context
Section titled “Context”Mistral’s server-side tools — web_search, web_search_premium,
image_generation, code_interpreter — custom connectors and agent mode
(/v1/agents/completions) integrate into the native provider. Live
probes against the Mistral API established the wire facts this decision
builds on:
- pi’s agent loop (pi-agent-core
agent-loop.js:161) executes everytoolCallblock in an assistant message client-side, regardless of the message’s stop reason. This is the forcing constraint: any server-side execution mapped to a pitoolCallblock would be executed a second time, locally, against a tool that does not exist on the client. - Server-side tool execution streams as multi-message deltas inside one
response: first
delta.tool_calls[]for the server-side call (e.g. namegenerate_image) carryingmetadata: {tool_type, integration_id, locale}, often withfinish_reason: "tool_calls"on that same chunk; then chunks withdelta.role: "tool", thedelta.tool_call_id, the executed result asdelta.content(a JSON string such as{"url":"https://..."}or plain error text) anddelta.metadata.tool_call_result: {type: "success"|"error", errors?}; then assistant text deltas with a higherdelta.index; a final chunk carriesfinish_reason: "stop". Later finish_reason values overwrite earlier ones in the stream loop. - Chat-request tool entries are
{type:"web_search"},{type:"web_search_premium"},{type:"image_generation"},{type:"code_interpreter"}and{type:"connector", connector_id:"<id-or-name>"}(CustomConnector), per the vendored spec (ADR 0003); the optionaltool_configurationfield is omitted. /v1/agents/completions(AgentsCompletionRequest) isadditionalProperties: falsewith requiredmessagesandagent_id;model,temperatureandstream_optionsare not allowed fields.- All replay strategies for folding the executed result back into the assistant turn were accepted by the live API.
- Per-endpoint server-tool support is asymmetric (wire-verified
2026-11-03 on the chat completions endpoint, models mistral-large-4 and
zai-glm-5-3):
web_search,web_search_premiumandcode_interpreterentries return HTTP 400 code 1800 (“WebSearchTool connector is not supported” etc.) — per-endpoint support, not account/tier related (Mistral’s docs mark them Conversations/Agents-API-only);image_generationand{type:"connector"}entries are accepted, and function tools work. Connector OAuth grants are stored server-side at Mistral; nothing is stored locally andauth.jsonstays untouched (ADR 0004).
Decision
Section titled “Decision”- Server-side executions fold into opaque assistant text, never pi
toolCallblocks. Each executed call becomes its own text block viaformatServerToolReport([Mistral server-side tool: <name> — executed by Mistral]\nArguments: …\nResult: …), emitted with balanced text_start/text_delta/text_end events as soon as the result delta (or the finalization pass) completes it. A mid-streamfinish_reason: "tool_calls"that belongs to a server execution is remapped tostopwhen the final message contains no pitoolCallblocks;rawStopReasonkeeps the wire value. - Client function calls still map to pi
toolCallblocks. In server-tools mode,delta.tool_calls[]entries are first accumulated as pending candidates (same index/id matching as the existing tool-call path). A result delta or server metadata (tool_type/integration_id) marks a candidate as server-executed and folds it; candidates without either are materialized astoolCallblocks at the finalization pass (toolcall_start, one toolcall_delta with the full arguments, toolcall_end); the arguments are re-parsed via the same salvage helper pi’s built-in providers use (parseToolCallArguments), so truncated or invalid argument JSON follows that salvage behavior. - Enablement is environment-driven with
options.env(pi’s ProviderEnv) taking precedence overprocess.env:MISTRAL_SERVER_TOOLS(comma/space separated; unknown values ignored, never thrown),MISTRAL_CONNECTORS,MISTRAL_AGENT_ID. With none set, the stream behaves byte-for-byte as before; the folding logic is entirely gated onserverExecutionPossible. - The chat-completions request path filters entries to the supported
set and warns about the rest.
chatCompletionsServerToolEntriessends onlyCHAT_COMPLETIONS_SERVER_TOOLS(currentlyimage_generation) plus connector entries; the configured-but- unsupported types (unsupportedChatCompletionsServerTools) are dropped from the request and surface a once-per-session warning naming them and pointing at the alternatives — web search via the model-callableweb_searchpi tool, code interpreter via hosted agent mode. Agent mode keeps the full tool set server-side and never drops or warns. Web search is no longer a chat-completions entry at all: it ships as theweb_searchpi tool (one-shot stateless Conversations backend; configured search modelmistral-small-latest, becausemistral-large-4refuses builtin connectors on/v1/conversationswith error 3004). A forthcoming sibling ADR owns the search-tool design details. Code interpreter stays Conversations/Agents-only for now. - Agent mode posts a minimal body to
{baseUrl}/agents/completions: exactlyagent_id,messages,stream: trueandmax_tokens(the context-clamped value). No model, temperature, stream_options, tools or sampling parameters are merged — the schema rejects them. - Connector management is exposed as seven pi tools
(
extensions/tools/connectors.ts, registered after the knowledge tools):mistral_connector_list,mistral_connector_tools,mistral_connector_auth_url,mistral_connector_activate,mistral_connector_deactivate,mistral_connector_share,mistral_connector_unshare. Theauth_urlis treated as a bearer URL: it reaches the caller viacontentandstructuredContentonly, neverdetails(the same content/details split as the signed URLs in ADR 0006); connector OAuth grants live on Mistral’s side,auth.jsonis unchanged (ADR 0004).
Consequences
Section titled “Consequences”-
The transcript stays portable: a server-executed tool appears as ordinary assistant text, which replays cleanly through any provider and does not re-trigger pi’s client-side execution loop. The trade-off is that the report format is prose, not structured tool-call data — programmatic consumers must parse the report text or use the wire events’
onProviderStreamEventhook. -
No client-side execution loop runs for server tools; the model’s follow-up text arrives in the same response, so latency for e.g. image generation is one streamed request.
-
Enabling server tools is a per-environment opt-in; nothing changes for existing users who set none of the three variables.
-
Limitations:
web_search,web_search_premiumandcode_interpreterare not sent to chat completions (HTTP 400 code 1800 there — Conversations/Agents-API-only, per-endpoint support verified on two models); their request declaration and folding paths remain covered by mocked tests modeled on the recordedimage_generationwire shape. Web search moved to theweb_searchpi tool; code interpreter runs in hosted agent mode. Agent mode does not merge sampling parameters or reasoning effort — an agent’s server-side configuration wins; revisit if the Agents API grows those fields. Connector tool invocations happen server-side inside chat completions ({type:"connector"}entries); the seven pi tools manage connectors only. -
The same connectors also have a client-side execution model (ADR 0011): the loader tool
mistral_connector_loadregisters an authenticated connector’s MCP tools as client-side callable pi tools. This ADR governs server-side refs ({type:"connector"}entries); ADR 0011 documents when each execution model is preferable.