Single-turn translation — the inbound half of the adapters
SPEC.md §0 item 7 pinned the format adapters as schema only: toOpenAI/toAnthropic/toGemini translated tool declarations outbound, and nothing in the library read a provider's tool calls back inbound. Every user of those public functions hit the same wall — they could tell a provider about their tools, but not receive the calls it made.
New: translate (idiomatic naming per port). Exactly one provider call, returned in OpenAI shape. No agent loop, no tool execution, no conversation state — every call is self-contained, so it runs statelessly and scales horizontally.
- Request takes the OpenAI
messages,toolsandtool_choiceverbatim, or an ordinary toolkit (MCP tools, skills, native functions, A2A agents, builtins) which is declared and never executed. They compose. - Inbound preserves the tool structure a text flattening destroys: assistant
tool_calls→ native tool-use blocks withargumentsre-parsed to objects;toolresults → tool-result blocks keyed bytool_call_id, merged into one user turn when consecutive;systemhoisted to the provider's own field; content-parts flattened. Bothargumentswire forms accepted. - Outbound returns
argumentsas a JSON string (the wire form, echoable byte-for-byte) plus a mappedfinishReason— any tool call wins, giving"tool_calls". - Shares the loop's retries/backoff, request-param merging and
llmmetric.beforeLLM/afterLLMfire once; tool hooks never fire.
Use it when the caller owns the conversation and executes tools itself — the standard OpenAI function-calling posture. golang/examples/translator is a stateless OpenAI-compatible proxy in ~60 lines.
Parity verified by byte-diff, not assumed
One adversarial fixture hitting every §11 rule at once was run through all six ports and diffed against Go: js, python, java, csharp and elixir are byte-identical, first diff, no corrections. Committed at docs/spikes/0003-translation-parity-fixture.json.
Tests: js 13 · python 20 · golang 12 · java 13 · csharp 20 · elixir 27. Every port's full suite green; Elixir coverage 96.9%.
Fixed: python was broken for new installs
pyproject allowed mcp>=1.0.0, but mcp 2.x renamed streamablehttp_client, so mcp_source.py failed at import — a fresh resolve could not load the package at all. Now pinned mcp>=1.0.0,<2.0.0.
Relay tools + durable resume — golang ONLY, a preview
golang also gains relay (declaration-only) tools and an answer-carrying durable resume, built on the §10 suspension primitive (ADR-0010, #37). The other five ports do not implement this yet, so it is deliberately not part of the SPEC.md §0 conformance contract — the §10 subsections carry a status banner, and the remaining ports are tracked in openspec/changes/add-tool-relay-mode/tasks.md.
If you want cross-port behaviour, use §11 translation. ADR-0011 explains the split: translation for the pass-through posture (the caller owns the conversation), relay for proxy-managed memory (toolnexus owns it).