Skip to content

Relay tools — declaration-only

Python · package toolnexus · SPEC §10

Declare a tool the host executes, not the library: the call rides out on the suspension. A relay tool carries a schema but no host-side behavior — the model emits a call, the call surfaces to the host as a kind:"tool_call" suspension carrying data.calls, the host executes it, and the host’s output feeds back as that call’s result. This is what lets a port act as a pure translator/proxy while still supporting standard function calling, where the client owns execution.

If you’re building a proxy in Python and want the caller to own tool execution rather than toolnexus, reach for translate (SPEC §11) instead: declare your toolkit to the provider, get back exactly one turn’s tool calls in OpenAI shape (TranslateResult.tool_calls), and execute them yourself — no suspension, no loop, no state kept between calls. SPEC’s own guidance (ADR-0011) is explicit about this: translation is the right mechanism for a pass-through posture; relay is for proxy-managed memory, which is a narrower Go-only use case today.

If you specifically need the suspend/resume mechanics relay rides on — parking a run and resuming it with an out-of-band answer — that primitive itself is fully available in Python via pending / WaitFor: write your own tool that returns pending(kind="tool_call", data={"calls": [...]}) and a wait_for that executes the calls and returns an Answer — you get the same shape relay would give you, just without the built-in constructor and collision guard Go ships.

  • pending — Return a Pending from a tool to park the run until someone answers.
  • WaitFor — The single hook where the host resolves a suspension — in-process prompt or durable queue, same contract.
  • translate — The recommended Python mechanism for a pass-through/proxy posture.