relay

Documentation

Tools

Built-in tools and the full long-poll callback protocol for custom function tools. Your handler, your process — Relay just orchestrates.

Two kinds of tools

Built-in tools execute server-side in the Go runtime. Custom function tools execute in your process — the runtime calls back to your SDK over an open stream.

import { createAgent, builtin, tool } from "@relayhq/sdk";

const agent = createAgent({
  model: "claude-sonnet-4-6",
  tools: [
    builtin.calculator,         // runs in the runtime
    myCustomTool,               // runs in your process
  ],
});

Built-in tools

Today the registry has one tool. More land as the platform grows. Built-ins are useful when the operation is fast, common, and shouldn't cost a round-trip.

builtin.calculator

Performs a single arithmetic operation on two numbers. Useful as a control for testing tool calls without writing your own.

{
  "type": "object",
  "properties": {
    "a": { "type": "number" },
    "b": { "type": "number" },
    "op": { "type": "string", "enum": ["+", "-", "*", "/"] }
  },
  "required": ["a", "b", "op"]
}

Custom function tools

The pattern is: schema + handler. The schema goes to the LLM. The handler runs locally.

get-user.ts
import { tool } from "@relayhq/sdk";

export const getUser = tool({
  name: "get_user",
  description: "Look up a user by id. Returns name, tier, balance.",
  inputSchema: {
    type: "object",
    properties: {
      id: { type: "string", description: "user id like u_001" },
    },
    required: ["id"],
    additionalProperties: false,
  },
  async handler({ id }: { id: string }) {
    const user = await db.users.findById(id);
    if (!user) return { error: `no user with id ${id}` };
    return user;
  },
});

Naming & descriptions

Tool names should be snake_case, 1–64 chars. Descriptions are part of the prompt — write them like you're briefing a colleague: be specific about inputs, outputs, and side effects.

Input schemas

Plain JSON Schema. Use additionalProperties: false to keep the model honest. The runtime forwards the schema verbatim to both Anthropic and OpenAI; provider quirks are normalized for you.

Return values & errors

Whatever your handler returns gets JSON-serialized as the tool result. Throwing turns into "error: <message>"; the model sees it and almost always self-corrects on the next iteration.

How the callback works

Custom tools work through a long-poll callback — the runtime orchestrates, but execution stays in your process. End-to-end:

callback flow
rendering diagram…

The runtime stays stateless. The SDK never talks to the runtime directly. Every event is persisted in run_events on the way through.

Limits & timeouts

  • Tool result timeout: 30 seconds by default. Configure with RELAY_TOOL_RESULT_TIMEOUT_MS on the control plane.
  • Max iterations per run: 8. The agent loop bails after 8 tool round-trips to prevent infinite cycles.
  • Parallel tool calls: supported. The SDK dispatches all custom tool handlers concurrently when the model fires more than one in a turn.
  • Tool input size: bounded by provider — Anthropic and OpenAI both cap at ~100K characters per call.