Skip to main content
Glama

bot-relay-mcp

CI Tether on VSCode Marketplace

A local-first coordination bus for AI coding agents. Durable inboxes, task queues, and wakeups for Claude Code, Cursor, Cline, Codex-style CLIs, scripts, and webhooks. Two interfaces, one shared SQLite database, zero infrastructure.

37 MCP tools. The headline feature is hands-free, LLM-agnostic autowake: agents running on different models (Claude Code, Codex) wake on relay mail and coordinate as a team without manual polling, via the Tether VSCode extension plus a durable cross-process outbox, so an inbox change in one process wakes subscribers in another. See the CHANGELOG for the full phase-by-phase arc.

What is this?

bot-relay-mcp gives AI coding agents and external systems a way to coordinate.

Two audiences, two transports:

  • AI coding agents (Claude Code, Cursor, Cline, Zed) connect via stdio MCP. Drop one entry into ~/.claude.json and the relay's tools appear inside Claude. No daemon required.

  • External systems (n8n, Slack, Telegram, custom scripts) connect via HTTP+SSE with optional Bearer auth. Trigger agent actions or receive webhook events.

Everything reads and writes the same SQLite file at ~/.bot-relay/relay.db. There is no cloud, no daemon you have to install, no service mesh.

Related MCP server: swarm-mcp

Why not just use Claude Agent Teams?

Fair question. Claude Code v2.1.32 shipped Agent Teams — native multi-agent coordination inside Claude. If your setup is Claude-only and stays that way, use it. It's the right tool.

bot-relay-mcp solves a different problem: coordination across a heterogeneous LLM stack. The tools your agents run on probably aren't all Claude. You might have Claude Code terminals for architecture work, Codex or Cursor on a different repo, an n8n workflow firing webhooks, a background Python process polling results, a Telegram bot forwarding alerts. Agent Teams coordinates Claude with Claude. bot-relay-mcp coordinates whatever speaks MCP (or plain HTTP) with whatever else does.

Three things it does that a vendor-native solution can't:

  • LLM-agnostic. Any MCP client — Claude Code, Cursor, Cline, Zed — registers and talks to any other. Non-MCP systems join over the HTTP+SSE transport with optional Bearer auth. No client is privileged.

  • Self-hosted. Everything lives in a single SQLite file on your machine. No cloud account, no control plane, no vendor lock-in. The HTTP daemon is optional; stdio-only works offline.

  • Federation-bound (v2.3 roadmap). Hub/edge federation is on the locked plan so multiple self-hosted relays can mesh without collapsing into one shared namespace. Teams of teams, across orgs, without routing through someone else's service.

It's also a durable bus, not an in-process coordinator. Messages, tasks, and channels survive individual agent sessions — restart a terminal, spawn a fresh one, come back tomorrow; the state is still there.

If your stack is Claude-only and stays that way, use Agent Teams. If it isn't, or if you want a self-hosted bus that survives individual sessions, that's what bot-relay-mcp is for.

Quick Start (30 seconds)

The relay is published on npm — setup is a single config entry, no cloning, no compiling, no absolute paths.

Add to your ~/.claude.json:

{
  "mcpServers": {
    "bot-relay": {
      "command": "npx",
      "args": ["-y", "bot-relay-mcp"],
      "type": "stdio"
    }
  }
}

The first invocation fetches the package and starts the server. Subsequent launches are instant.

Quick Start (from source)

git clone https://github.com/Maxlumiere/bot-relay-mcp.git
cd bot-relay-mcp
npm install
npm run build

One-command setup (relay init)

After building, run the installer:

./bin/relay init

It reconciles your ~/.claude.json (mcpServers entry), ~/.claude/settings.json (SessionStart hook), and — on macOS — installs a keep-alive launchd daemon. It never touches your agent tokens, and re-running is always safe (idempotent). Use --config-only / --skip-hooks / --skip-daemon to opt out of any step.

⚠️ Security — the default is local-trust. A default relay init install has no HTTP secret: the relay binds to 127.0.0.1 and trusts every process on the same machine. Any local process can then reach the loopback HTTP surface — including the dashboard operator endpoints (/api/snapshot, set-status, wake-agent, kill-agent, operator-identity). Per-agent tokens protect who you can act as (you can't impersonate another agent), but they do not gate the whole surface. This is a reasonable boundary on a personal, single-user machine (a local process already has OS-level access to the same data), and non-loopback binds are still refused without a secret.

On a shared machine, or any non-loopback / team / remote setup, gate it: run relay init --secret <strong-random> (or export RELAY_DASHBOARD_SECRET). The daemon requires a secret to bind to any non-loopback host.

Alternatively, wire it up by hand — add to ~/.claude.json:

{
  "mcpServers": {
    "bot-relay": {
      "command": "node",
      "args": ["/absolute/path/to/bot-relay-mcp/dist/index.js"],
      "type": "stdio"
    }
  }
}

Open two Claude Code terminals and try it:

Terminal A:

> Register on the relay as "planner" with role "orchestrator"
> Discover other agents
> Send a message to "builder": "Can you handle the API layer?"

Terminal B:

> Register on the relay as "builder" with role "builder"
> Check my relay messages
> Reply to planner: "On it."

The database is created automatically at ~/.bot-relay/relay.db on first use. That is the full setup.

File permissions (v2.1). The relay creates ~/.bot-relay/ at 0700 and relay.db + backup tarballs at 0600 — owner-only. config.json is operator-managed; the relay never chmods it but logs a warning at startup if it's more permissive than 0600. POSIX only — native Windows NTFS uses ACLs, not POSIX modes, so the chmod calls are no-ops there (documented).

Tether (v2.5 + v2.7 cross-process delivery)

relay://inbox/<agent_name> is a subscribable MCP resource. Any MCP-aware client subscribes via the standard subscribe request and receives notifications/resources/updated pushes when the agent's inbox changes (new mail, broadcast, drain). No polling, standard MCP semantics.

v2.7 added cross-process delivery. A message sent from a stdio MCP terminal (one OS process) now wakes IDE subscribers connected to the HTTP daemon (a different OS process) via a durable inbox_events outbox table polled by the daemon. v2.5 only delivered events same-process — v2.7 is the load-bearing release for any Tether-style IDE integration.

The bundled VSCode extension ships on the marketplace:

code --install-extension lumiere-ventures.bot-relay-tether

Or browse marketplace.visualstudio.com/items?itemName=lumiere-ventures.bot-relay-tether. Source for the extension lives at extensions/vscode/ in this repo for users who want to build from source. The extension surfaces pending count + last-message recency in the status bar, opens a webview with the last message preview on click, and optionally auto-types inbox into the integrated terminal so Claude Code wakes up. See extensions/vscode/README.md for install + config and docs/tether-roadmap.md for the free-vs-paid scope line.

Sentinel — relay watch (v2.18)

Tether is push-wake inside VSCode. Sentinel is wake-anywhere — the poll/marker-based sibling for agents not in VSCode: iTerm2 personas, plain terminals, remote SSH sessions.

relay watch <agent> [--interval S] [--once] [--json]

It stands watch over <agent>'s inbox and prints a wake line the moment new mail arrives, so a harness (or you) can nudge the agent to read it — the shipped replacement for a hand-rolled inbox-polling loop. When the daemon writes filesystem delivery markers (RELAY_FILESYSTEM_MARKERS=1), it waits on the marker via fs.watch (near-zero idle cost) with a slow fallback re-check so a dropped event is never a silent miss; otherwise it falls back to bounded polling. It reads the active per-instance DB directly under the local-trust boundary (no token), and --once / --json make it scriptable.

Tether = push-wake in VSCode; Sentinel = wake anywhere.

Tools

Identity

Tool

Inputs

Description

register_agent

name, role, capabilities[]

Register this terminal as a named agent. Uses upsert — safe to call multiple times.

unregister_agent

name

Remove an agent from the relay. Idempotent. Fires agent.unregistered webhook on success.

discover_agents

role (optional)

List all registered agents with status (online/offline/unknown).

spawn_agent

name, role, capabilities, cwd?, initial_message?

Spawn a new Claude Code terminal pre-configured as a relay agent. Cross-platform (v1.9): macOS (iTerm2/Terminal.app), Linux (gnome-terminal/konsole/xterm/tmux fallback chain — tmux covers headless servers), Windows (wt.exe/powershell.exe/cmd.exe). See docs/cross-platform-spawn.md.

Messaging

Tool

Inputs

Description

send_message

from, to, content, priority

Send a direct message to another agent by name.

get_messages

agent_name, status, limit

Check your mailbox. Pending messages are auto-marked as read.

broadcast

from, content, role (optional)

Send a message to all registered agents (or filter by role).

Tasks

Tool

Inputs

Description

post_task

from, to, title, description, priority

Assign a task to another agent.

post_task_auto (v2.0)

from, title, description, required_capabilities[], priority

Auto-route to the least-loaded agent whose capabilities match ALL required. Queues if no match; assigns on the next capable registration.

update_task

task_id, agent_name, action, result?

Actions: accept / complete / reject / cancel (v2.0, requester-only) / heartbeat (v2.0, renews lease). State machine + CAS enforced.

get_tasks

agent_name, role, status, limit

Query your task queue (assigned to you or posted by you).

get_task

task_id

Get a single task by ID with full details.

Channels (v2.0)

Tool

Inputs

Description

create_channel

name, description?, creator

Create a named channel for multi-agent coordination. Requires channels capability.

join_channel

channel_name, agent_name

Join any public channel.

leave_channel

channel_name, agent_name

Leave a channel.

post_to_channel

channel_name, from, content, priority

Post to a channel you are a member of.

get_channel_messages

channel_name, agent_name, limit, since?

Read messages posted to a channel since your join time.

Status + Health (v2.0)

Tool

Inputs

Description

set_status

agent_name, status

Signal online / busy / away / offline. busy/away exempt you from health-monitor task reassignment.

health_check

(none)

Report relay version, uptime, and live counts (agents, messages, tasks, channels). No auth required.

Webhooks (v1.2+)

Tool

Inputs

Description

register_webhook

url, event, filter, secret

Subscribe to relay events via HTTP POST.

list_webhooks

(none)

List all registered webhook subscriptions.

delete_webhook

webhook_id

Remove a webhook subscription.

Supported events: message.sent, message.broadcast, task.posted, task.accepted, task.completed, task.rejected, task.cancelled (v2.0), task.auto_routed (v2.0), task.health_reassigned (v2.0), channel.message_posted (v2.0), agent.unregistered, agent.spawned, webhook.delivery_failed, * (all).

v2.0 — retry with backoff. Failed webhook deliveries retry at 60s / 300s / 900s (3 attempts). CAS-claimed per row — no double delivery. Piggybacks on webhook-firing tool calls, no background thread.

When secret is provided, each delivery includes an X-Relay-Signature: sha256=... HMAC header. Filter optionally restricts firing to events where from_agent or to_agent matches.

Example: Task Delegation

Terminal A — Orchestrator:

1. register_agent("orchestrator", "planner", ["delegation", "review"])
2. discover_agents() → sees "worker" is online
3. post_task(from: "orchestrator", to: "worker",
     title: "Write auth tests",
     description: "Cover login, logout, token refresh. Use vitest.",
     priority: "high")
4. send_message(from: "orchestrator", to: "worker",
     content: "Task posted — check your queue.")

Terminal B — Worker:

1. register_agent("worker", "builder", ["testing", "backend"])
2. get_messages("worker") → message from orchestrator
3. get_tasks("worker", role: "assigned", status: "posted") → auth test task
4. update_task(task_id, "worker", "accept")
5. ... does the work ...
6. update_task(task_id, "worker", "complete", result: "12 tests passing")
7. send_message(from: "worker", to: "orchestrator",
     content: "Auth tests done. All passing.")

Terminal A checks results:

1. get_messages("orchestrator") → "Auth tests done."
2. get_task(task_id) → status: completed, result: "12 tests passing"

How It Works

Every Claude Code terminal spawns its own MCP server process via stdio. All processes read and write the same SQLite file at ~/.bot-relay/relay.db. SQLite WAL mode handles concurrent access safely. Messages older than 7 days and completed tasks older than 30 days are purged automatically on startup.

Unified relay CLI (v2.1)

One entry, six subcommands: doctor / init / test / generate-hooks / backup / restore. First-run setup:

relay init          # interactive
relay init --yes    # defaults + random HTTP secret

relay doctor runs a diagnostic sweep; relay test runs a minimal self-check against a throwaway relay; relay generate-hooks emits Claude Code hook JSON for ~/.claude/settings.json. Full reference in docs/cli.md. The standalone bin/relay-backup + bin/relay-restore from Phase 2c have been absorbed into relay backup + relay restore.

Token lifecycle (v2.1)

Two new tools for credential hygiene: rotate_token lets an agent swap its own token with history preserved; revoke_token lets an admin-capable agent nullify another agent's token_hash (target re-bootstraps via the Phase 2b migration path). New admin capability is never auto-granted — register admin agents explicitly. Full operator runbook in docs/token-lifecycle.md.

Error codes (v2.1)

Every tool error response carries a stable error_code token alongside the free-form error string. Branch on the code; never string-match the message. Full catalog + stability guarantee in docs/error-codes.md. Source of truth: src/error-codes.ts.

Protocol version (v2.1)

Beyond the package version string, the relay surfaces a protocol_version via register_agent + health_check responses. Clients should key compatibility on protocol_version (bumps only on tool-surface changes) rather than the package version (bumps on every ship). See docs/protocol-version.md for SemVer rules + a client-side compatibility snippet.

HTTP Mode — for n8n, Slack, Telegram, custom scripts

Run the relay as an HTTP daemon and any HTTP client can drive it:

RELAY_TRANSPORT=http RELAY_HTTP_SECRET=your-shared-secret node dist/index.js
# Listens on http://127.0.0.1:3777

Production deployment — set RELAY_HTTP_SECRET. v2.1 refuses to start on a non-loopback host (0.0.0.0, a public IP, Docker -p 3777:3777 without loopback pinning) unless RELAY_HTTP_SECRET is set. Loopback binds (127.0.0.1, ::1, localhost) stay zero-config for local development.

Dev-only escape hatch: RELAY_ALLOW_OPEN_PUBLIC=1 lets the relay start anyway on a public host without a secret — useful for throwaway local Docker nets, but logs a loud warning every startup. Never use in production.

Endpoints:

  • POST /mcp — JSON-RPC (the MCP protocol over HTTP+SSE). Requires Authorization: Bearer <secret> if http_secret is configured.

  • GET /health — server status (always open, no auth)

  • GET / — built-in dashboard (live view of agents, messages, tasks, webhooks). ADR-0006: protected by a Host-header allowlist (DNS-rebinding defense) + an auth gate (RELAY_DASHBOARD_SECRET or the dashboard_secret config, generated by default at relay init). Operator-power endpoints (kill-agent, wake-agent, set-status, …) require it regardless of network position. Loopback binds allow a restricted, secret-free read view; non-loopback binds require the secret. http_secret no longer authenticates the dashboard — that fallback was a removed privilege-escalation. Full policy in docs/dashboard-security.md.

  • GET /api/snapshot — JSON snapshot of relay state (same gates as /)

Three transport modes:

  • stdio (default) — per-terminal, for AI coding agents

  • http — daemon, for external systems

  • both — HTTP daemon plus a stdio connection (useful for bridge scripts)

All transports share the same SQLite database. Stdio agents and HTTP clients see the same world.

Process-boundary reminder (v2.1.3): stdio MCP clients and the HTTP daemon are separate processes. Each Claude Code terminal with "type":"stdio" in ~/.claude.json spawns its own node dist/index.js child. Restarting the :3777 HTTP daemon never affects stdio clients — their own child processes are untouched. Operator /mcp reconnect is only needed after restart for "type":"http" MCP clients pointed at the daemon URL. See docs/transport-architecture.md for the full topology + post-restart operator checklist.

n8n integration example

Trigger a Claude Code agent from an n8n workflow:

POST http://127.0.0.1:3777/mcp
Authorization: Bearer your-shared-secret
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "post_task",
    "arguments": {
      "from": "n8n-workflow-42",
      "to": "builder",
      "title": "Process new lead",
      "description": "Lead data: ...",
      "priority": "high"
    }
  }
}

Then register a webhook so n8n hears about completion:

{
  "jsonrpc": "2.0", "id": 2, "method": "tools/call",
  "params": {
    "name": "register_webhook",
    "arguments": {
      "url": "https://your-n8n.example.com/webhook/abc",
      "event": "task.completed",
      "secret": "shared-with-n8n"
    }
  }
}

When a task completes, n8n receives a POST with the result and an HMAC signature in X-Relay-Signature.

Config File (v1.2+)

Optional ~/.bot-relay/config.json:

{
  "transport": "http",
  "http_port": 3777,
  "http_host": "127.0.0.1",
  "webhook_timeout_ms": 5000,
  "http_secret": null,
  "trusted_proxies": []
}

Env vars override file config: RELAY_TRANSPORT, RELAY_HTTP_PORT, RELAY_HTTP_HOST, RELAY_HTTP_SECRET, RELAY_TRUSTED_PROXIES (comma-separated CIDRs).

Trusted Proxies and X-Forwarded-For (v1.6.2)

By default, the relay IGNORES the X-Forwarded-For header completely. Rate limits are keyed on the direct socket peer IP only. This prevents a caller from sending a spoofed header to get their own rate-limit bucket.

If you front the relay with Cloudflare, nginx, or any other reverse proxy, configure trusted_proxies with CIDRs of those proxies:

{
  "trusted_proxies": ["127.0.0.0/8", "::1/128", "10.0.0.0/8"]
}

Or via env var:

RELAY_TRUSTED_PROXIES="127.0.0.0/8,::1/128,10.0.0.0/8"

When the direct peer IP falls in the trusted list, the relay walks the X-Forwarded-For chain right-to-left, skipping trusted hops, and uses the leftmost-untrusted hop as the real client IP. This matches RFC 7239 §7.4 and how nginx/Express normally handle this.

Per-Agent Tokens (v1.7)

Every tool call (other than first-time register_agent and /health) requires an agent token. The token identifies WHO is calling — separate from and stronger than the shared HTTP secret, which only identifies a trusted network.

Issuing a token — first registration:

# The response returns `agent_token` ONCE. Save it.
{
  "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": {
    "name": "register_agent",
    "arguments": { "name": "builder", "role": "builder", "capabilities": ["tasks"] }
  }
}

The server stores only a bcrypt hash. The raw token is surfaced once in the response agent_token field and echoed to stderr as [auth] New agent_token issued for "builder". Save it: RELAY_AGENT_TOKEN=.... If you lose it, unregister_agent (auth'd) then re-register.

Presenting the token on every subsequent call — three ways:

  1. Arg field (works for stdio + HTTP):

    { "name": "send_message", "arguments": { "from": "builder", "to": "ops", "content": "hi", "agent_token": "..." } }
  2. HTTP header:

    X-Agent-Token: <token>
  3. Env var (stdio flow, also picked up by HTTP client wrappers):

    export RELAY_AGENT_TOKEN=<token>

Capabilities are set at first registration and are immutable (v1.7.1). To change an agent's capability set, call unregister_agent (with its token) then register_agent with the new capability list. Re-register attempts that change caps are ignored with a capabilities_note in the response.

Capability catalog:

  • spawn — required for spawn_agent

  • tasks — required for post_task, update_task

  • webhooks — required for register_webhook, list_webhooks, delete_webhook

  • broadcast — required for broadcast

  • All other tools are always allowed for any authenticated agent (no capability check).

Migration for pre-v1.7 agents (v2.1+): agents registered before v1.7 have no token hash. A register_agent call against such a row self-migrates — the relay detects the null hash, issues a fresh token, and the agent is first-class from that point on. No RELAY_ALLOW_LEGACY=1 required for the migration call itself. RELAY_ALLOW_LEGACY is still available as a coarser escape hatch for non-register tool calls against unmigrated legacy rows (e.g., if you want send_message to work before an agent has migrated); turn it OFF once all your agents have migrated.

Encryption at Rest (v1.7 opt-in; keyring + rotation in v2.1 Phase 4b.3)

Set the keyring to encrypt message/task/audit/webhook content fields in the SQLite database with AES-256-GCM. Three configuration sources (pick exactly one — multi-set is rejected at startup):

# 1. Inline JSON (for CI / secrets managers)
export RELAY_ENCRYPTION_KEYRING='{"current":"k1","keys":{"k1":"<base64-32>"}}'

# 2. File path (operator-friendly; chmod 600)
export RELAY_ENCRYPTION_KEYRING_PATH=~/.bot-relay/keyring.json

# 3. Legacy single-key (auto-wraps to { current: "k1", keys: { k1: <value> } }; deprecation warning at startup)
export RELAY_ENCRYPTION_KEY="<base64-32>"

# Generate a key:
openssl rand -base64 32
# or:
node -e 'console.log(require("crypto").randomBytes(32).toString("base64"))'

When the keyring is set, the relay transparently encrypts on write (with current key) and decrypts on read (with any key in the keyring). Every ciphertext carries an enc:<key_id>:... prefix so rows are self-describing. Legacy enc1:... rows (pre-Phase-4b.3 deployments) decrypt via RELAY_ENCRYPTION_LEGACY_KEY_ID (default k1).

Rotating keys (online)

Full runbook at docs/key-rotation.md. In summary:

  1. Add the new key to the keyring while keeping the old one (both decrypt; current still points to old).

  2. Flip current to the new key; restart. New writes use the new key.

  3. relay re-encrypt --from old_key_id --to new_key_id --yes — scans + migrates all existing rows across 5 encrypted columns. Resumable.

  4. relay re-encrypt --verify-clean old_key_id — exit 0 = safe to retire.

  5. Remove the old key from the keyring; restart.

Without the keyring set, content is stored plaintext (default, convenient for local dev).

Rotation Guide — HTTP Shared Secret (v1.7)

The RELAY_HTTP_SECRET shared secret can be rotated without downtime using a grace window:

Step 1 — promote the new secret as primary, keep the old as previous:

RELAY_HTTP_SECRET="new-secret-v2" \
RELAY_HTTP_SECRET_PREVIOUS="old-secret-v1" \
RELAY_TRANSPORT=http node dist/index.js

During this window, BOTH secrets are accepted. Requests using the old secret receive an X-Relay-Secret-Deprecated: true response header as a signal to upgrade.

Step 2 — update every client to present new-secret-v2 in their Authorization: Bearer … or X-Relay-Secret header.

Step 3 — watch for the deprecation header on your dashboard/logs until no more requests use the old secret.

Step 4 — drop the old secret:

RELAY_HTTP_SECRET="new-secret-v2" \
RELAY_TRANSPORT=http node dist/index.js    # RELAY_HTTP_SECRET_PREVIOUS unset

Multiple previous secrets are supported as a comma-separated list:

RELAY_HTTP_SECRET_PREVIOUS="v1-secret,v0-secret"

Secret comparisons are timing-safe (v1.7.1 — crypto.timingSafeEqual), so an attacker cannot recover the secret via byte-by-byte response-timing measurement.

Multi-machine: centralized deployment (v2.1)

bot-relay-mcp is LLM-agnostic, CLI-agnostic, and deployment-flexible. Pick the path that fits your setup:

Path A — Single-machine (default). Stdio transport, per-terminal process, zero infrastructure. Best for solo development on one laptop. No secrets, no reverse proxies, no ops — run npm install + add the stdio entry to your MCP client config and you're done. Covered throughout this README.

Path B — Multi-machine (centralized, v2.1 Phase 7r). One bot-relay-mcp hub on a VPS, multiple thin MCP clients connecting via HTTP. Agents on different machines can send_message, post tasks, subscribe to webhooks, and join channels through shared state. No new architecture — just the HTTP transport we've had since v1.2, packaged with a convenience CLI in v2.1.

When to pick centralized

  • Two or more machines in play (dev laptop + CI, work + personal, family devices)

  • AI agents running on different hosts that need to coordinate

  • Team environments where multiple people connect their MCP clients to a shared relay

Quick pair flow

On the hub (VPS, reachable at e.g. https://relay.example.com): install bot-relay-mcp, run under systemd with RELAY_TRANSPORT=http + RELAY_HTTP_SECRET, terminate TLS with Caddy/nginx. See docs/multi-machine-deployment.md for the worked VPS runbook.

On each client machine:

relay pair https://relay.example.com \
  --name "$(whoami)-$(hostname -s)" \
  --role operator \
  --capabilities spawn,tasks,webhooks,broadcast,channels \
  --secret "$RELAY_HTTP_SECRET"

relay pair probes the hub, registers this machine as an agent, captures the returned one-time agent_token, and emits an MCP client config snippet ready to paste into ~/.claude.json / ~/.cursor/mcp.json / etc. Persist the token (export RELAY_AGENT_TOKEN=… in your shell rc) so hooks can authenticate on every terminal open.

Verify after pairing:

relay doctor --remote https://relay.example.com

Expected: PASS on reachability + protocol compatibility + token auth + hub auth config.

Trust-model tradeoffs

  • Hub operator can read plaintext messages in RAM (even with RELAY_ENCRYPTION_KEY set, decryption happens server-side for routing)

  • Hub is a single point of failure for cross-machine coordination

  • Recommended for trusted groups (families, small teams, personal multi-machine setups)

  • NOT recommended for mutually distrustful parties sharing a single hub, or compliance-bound workloads where in-RAM access by the operator is a policy violation

See SECURITY.md §Centralized deployment trust model for the full posture + incident response playbook.

Bridge to other tools via MCP

bot-relay-mcp is MCP-compatible, so your MCP client can connect to both bot-relay-mcp AND other MCP servers (Slack, Discord, Matrix, email, etc.) simultaneously. That's an operator deployment choice — we don't integrate those into bot-relay-mcp. See docs/multi-machine-deployment.md §3 for the pattern.

Zero-Friction Setup

To skip approval prompts for relay tools, add this to your project's .claude/settings.json:

{
  "permissions": {
    "allow": [
      "mcp__bot-relay__register_agent",
      "mcp__bot-relay__discover_agents",
      "mcp__bot-relay__send_message",
      "mcp__bot-relay__get_messages",
      "mcp__bot-relay__broadcast",
      "mcp__bot-relay__post_task",
      "mcp__bot-relay__update_task",
      "mcp__bot-relay__get_tasks",
      "mcp__bot-relay__get_task",
      "mcp__bot-relay__register_webhook",
      "mcp__bot-relay__list_webhooks",
      "mcp__bot-relay__delete_webhook"
    ]
  }
}

Auto-Check on Session Start

Add a SessionStart hook so every terminal automatically checks the relay for pending messages when it opens. See docs/hooks.md for the full configuration.

Near-Real-Time Mail Delivery (v1.8)

The SessionStart hook only fires when a terminal opens. If an agent is actively working and mail arrives mid-session, it does not see the message until next startup (or a human pastes it in).

v1.8 adds a PostToolUse hook — hooks/post-tool-use-check.sh — that fires after every tool call, checks the mailbox, and injects pending messages as additionalContext so the running session picks them up immediately.

Install per-project (NOT global), in <project>/.claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/bot-relay-mcp/hooks/post-tool-use-check.sh",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

Important — if your path contains spaces, single-quote it inside the JSON string. Claude Code passes the command to the shell, which splits on whitespace. Without the single quotes the hook silently fails with errors like /bin/sh: ... is a directory. Example for an installation at /path/to/My Projects/bot-relay-mcp/:

"command": "'/path/to/My Projects/bot-relay-mcp/hooks/post-tool-use-check.sh'"

The outer double-quotes are JSON; the inner single-quotes are shell. Paths with no spaces do not need this treatment.

The hook prefers the HTTP path when RELAY_AGENT_TOKEN is set and the daemon is running (full auth + audit), falling back to direct sqlite on RELAY_DB_PATH otherwise. It does NOT re-register (SessionStart handles that), does NOT check tasks (simpler focus, less context pressure), and silent-exits when there is no mail. Full docs + troubleshooting in docs/post-tool-use-hook.md.

Honest limitation: idle terminals get no delivery. The hook only fires when the agent is actively running tool calls. For long-idle windows, still rely on SessionStart + human attention.

Turn-Boundary Wake (v2.1, rewritten v2.23)

PostToolUse only fires on turns that include at least one tool call. A text-only turn (Claude responds with no tool invocation) does not trigger it. The Stop hook — hooks/stop-check.sh — closes that gap by firing at every turn end, whether or not the turn invoked tools. It is a read-only wake: it peeks at the mailbox and, if mail is pending, emits decision:"block" so the agent continues immediately and fetches its own mail via get_messages. The hook itself never marks anything read — a wake that cannot prove delivery must not consume (additionalContext on Stop is queued for a next turn that may never come, so the original mark-read-and-inject shape was a silent data-loss path). Install both together in your project's .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/bot-relay-mcp/hooks/stop-check.sh",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

Same single-quote-the-path-if-it-contains-spaces rule, same env vars, same HTTP/sqlite fallback (both non-mutating: peek:true over HTTP, a bare SELECT over sqlite — no mutating SQL exists in the script), same silent-fail contract as PostToolUse. Two loop guards bound the blocking: stop_hook_active in the hook payload (one wake per natural stop) and a time damper (RELAY_STOP_WAKE_DAMPER_SECS, default 120s) that applies only when the payload lacks a parseable stop_hook_active. All guards leave mail pending when they suppress — a delayed wake, never a lost one. Full docs + troubleshooting in docs/stop-hook.md.

Honest limitation: Stop does NOT wake truly idle terminals. If no turn is in progress, neither hook fires. For long-idle windows, use the Layer 2 Managed Agent reference (examples/managed-agent-reference/).

Backup & Restore (v2.1)

Two CLIs for disaster recovery:

relay-backup                              # snapshot to ~/.bot-relay/backups/
relay-backup --output /srv/backup.tgz     # custom path
relay-restore ~/.bot-relay/backups/relay-backup-<iso>.tar.gz

relay-backup produces a tar.gz of the live DB (via a consistent snapshot — safe while the daemon is running), the optional config.json, and a manifest.json with schema version and row counts. Works on both drivers (#171): the native better-sqlite3 driver snapshots via VACUUM INTO; the optional sql.js wasm driver snapshots via an atomic in-memory serialization (.export()), and the manifest/integrity probes open through the active driver. Archives are standard SQLite files and interoperate across drivers (details).

relay-restore always safety-backs-up the current DB first (to ~/.bot-relay/backups/pre-restore-<iso>.tar.gz). If that safety backup fails, the restore aborts untouched. It then refuses if the daemon appears to be running (/health probe, best-effort), refuses schema-version mismatches (higher = hard refuse, lower = --force overrides), runs PRAGMA integrity_check on the extracted DB, and finally atomic-swaps the new DB into place.

Full docs + troubleshooting in docs/backup-restore.md.

Lost-Token Recovery (v2.1)

Close a terminal, lose RELAY_AGENT_TOKEN, and the relay rejects your register_agent with AUTH_FAILED because the row is intact. Clear the registration so the agent can re-bootstrap:

relay recover <agent-name>                 # interactive confirm
relay recover <agent-name> --yes           # skip confirm (for scripts)
relay recover <agent-name> --dry-run       # show what would change, commit nothing
relay recover <agent-name> --db-path PATH  # non-default DB location

Messages and tasks addressed to the agent are preserved — only the agents + agent_capabilities rows are cleared. After recovery, the operator calls register_agent with the same name/role/capabilities and captures a fresh agent_token.

Trust model: filesystem access to ~/.bot-relay/relay.db IS the authority (same boundary the daemon relies on). Not an MCP tool — the caller by definition cannot authenticate. The CLI emits an audit_log entry with tool='recovery.cli' + the operator's OS username for incident traceability.

External-CLI Token Mint (v2.6)

Some LLM CLI clients (Codex was the canonical case) have safety monitors that pattern-match register_agent followed immediately by use returned token as a credential handoff and cancel the second tool call. The result: the agent row exists on the relay, but the agent process never captured its token, and every subsequent re-register attempt fails with NAME_COLLISION_ACTIVE because the orphan session is still considered live.

relay mint-token solves this by minting the token outside the agent's process. The operator hands the plaintext token to the agent's environment via RELAY_AGENT_TOKEN, and the agent authenticates on its first MCP call without ever invoking register_agent:

relay mint-token codex --role builder --capabilities build,test,audit

Output (token shown ONCE):

✓ Minted token for new agent "codex"

Token (shown ONCE — store it now):

  <43-char-base64url-token>

Set in your CLI's environment before launching:

  export RELAY_AGENT_NAME=codex
  export RELAY_AGENT_TOKEN=<43-char-base64url-token>

Other forms:

relay mint-token NAME --json                          # structured output for scripts
relay mint-token EXISTING --force                     # rotate token; caps + role preserved
relay mint-token NAME --description "human-readable"  # discoverable in dashboard

Caps are locked at first mint (caps are immutable after first registration); --force rotates the token but preserves both caps and role. The CLI emits a daemon-running advisory to stderr and writes an audit_log entry (tool='agent.token_minted') with the operator's OS username, the --force flag state, and whether the row was created or rotated. Full setup walkthrough including platform-specific token-storage best practices: docs/agents/external-cli-setup.md.

Cross-Platform Spawn (v1.9)

spawn_agent opens a new Claude Code terminal on macOS, Linux, and Windows via a driver abstraction:

  • macOSbin/spawn-agent.sh (iTerm2 → Terminal.app). Unchanged from v1.6.4, preserves the 3-layer hardening + 19-payload adversarial test suite.

  • Linuxgnome-terminalkonsolextermtmux fallback chain. The tmux fallback creates a detached session (attach later with tmux attach -t <agent-name>) — covers headless servers with no GUI.

  • Windowswt.exe (Windows Terminal) → powershell.execmd.exe.

Driver selection: RELAY_TERMINAL_APP override (allowlist-gated) > process.platform auto-detect > in-driver fallback chain.

Full install requirements per platform + manual smoke-test checklists + troubleshooting: docs/cross-platform-spawn.md.

Env-var propagation is minimal by default (principle of least authority): system essentials + anything prefixed RELAY_*. Secrets like AWS_SECRET_ACCESS_KEY are NOT passed to spawned agents unless explicitly prefixed.

Plug-and-play defaults (v2.1.2): spawned terminals are configured for autonomous work out of the box — they auto-pull mail from their inbox on first turn instead of idling, run with --permission-mode bypassPermissions so they don't ask the operator to approve every tool call, get an iTerm2 / session-picker title set to the agent name, and run at --effort high to cap token spend (parent terminals doing strategic work may use xhigh; spawned children doing mechanical work shouldn't inherit it). Every default has an env override for the rare case the legacy behavior is wanted:

Default

Env override

Notes

Kickstart prompt sent as positional arg

RELAY_SPAWN_KICKSTART="custom" / RELAY_SPAWN_NO_KICKSTART=1

The default tells the spawned agent to pull get_messages and act on inbox.

--permission-mode bypassPermissions

RELAY_SPAWN_PERMISSION_MODE=<mode>

Allowlist: acceptEdits, auto, bypassPermissions, default, dontAsk, plan.

--name <agent>

RELAY_SPAWN_DISPLAY_NAME="custom"

Shows up as the iTerm2 tab + Claude Code session title.

--effort high

RELAY_SPAWN_EFFORT=<level>

Allowlist: low, medium, high, xhigh, max.

Layer 2: Managed Agents (v1.10)

Agents that are NOT Claude Code terminals — Python daemons, Node workers, Ollama/vLLM integrations, custom scripts. They connect to the relay via HTTP (recommended) or direct SQLite, use the same 37 MCP tools, and authenticate with per-agent tokens. If registered with managed:true, they also receive token-rotation push-messages over the normal get_messages channel — see docs/managed-agent-protocol.md.

Full integration guide with mental model, auth flow, lifecycle, error patterns, and security notes: docs/managed-agent-integration.md.

Runnable reference implementations (stdlib-only, ~200 LOC each):

Both demonstrate: register, send/receive messages, accept + complete tasks, discover peers, SIGINT cleanup. Each has a SMOKE.md with a 5-step manual verification checklist.

SQLite Driver Options (v1.11)

The relay uses SQLite for persistent state. Two drivers are available:

  • native (default)better-sqlite3, a compiled C addon. Fast, supports WAL mode, multi-process safe. Requires a C++ compiler at npm install time.

  • wasmsql.js, SQLite compiled to WebAssembly. Zero native compilation. Slightly slower writes (in-memory + write-back-to-file). Single-process only (not safe for multi-terminal stdio).

Switch with one env var:

npm install sql.js                    # one-time install of the optional dep
RELAY_SQLITE_DRIVER=wasm node dist/index.js

Both drivers read the same relay.db file format. Full details, performance notes, and limitations: docs/sqlite-wasm-driver.md.

Installing under npm v12+ (install scripts disabled by default)

npm v12 (released 2026-07-08) disables dependency install scripts by default (allowScripts off) — including native node-gyp/prebuild builds. The default native driver (better-sqlite3) fetches its prebuilt binary via an install script, so under npm 12's defaults that binary is never fetched and the relay fails at startup with:

Error: Could not locate the bindings file

Two ways to fix it:

A — keep native (best for multi-terminal stdio): approve better-sqlite3's install script once. The approval is recorded in your package.json allowlist — commit it.

npm install                                   # installs; records which deps have scripts
npm approve-scripts --allow-scripts-pending   # review what's pending
npm approve-scripts better-sqlite3            # approve it
npm rebuild                                    # build the native binary

B — switch to wasm (zero approval): sql.js is pure WebAssembly — no native build, no install script.

RELAY_SQLITE_DRIVER=wasm node dist/index.js

wasm is single-process only (see the note above — not for multi-terminal stdio). sql.js is an installed optional dependency; if you installed with --omit=optional, add it back with npm install sql.js.

--allow-git / --allow-remote (also default none in npm 11.10+/11.15+) don't affect the relay — every dependency resolves from the npm registry, none from git or tarball URLs. Full details: docs/sqlite-wasm-driver.md.

Roadmap

  • v1.1: Local relay, 9 tools, SQLite, auto-purge

  • v1.2: HTTP transport, webhook system, config file — 12 tools

  • v1.3: Presence integrity, unregister_agent, hook delivers mail — 13 tools

  • v1.4: spawn_agent + role templates + dashboard — 14 tools

  • v1.5: Built-in security — Bearer auth, audit log, rate limiting

  • v1.6: Hardening pass — SSRF, input validation, path traversal, stdout discipline

  • v1.7: Per-agent tokens, secret rotation, at-rest encryption, capability scoping

  • v1.8: Near-real-time mail via PostToolUse hook

  • v1.9: Cross-platform spawn (macOS / Linux / Windows / tmux) — Node/TS driver abstraction

  • v1.10: Layer 2 Managed Agents — reference Python + Node workers

  • v1.11: SQLite WASM driver (sql.js opt-in) — zero native compilation on Windows/Alpine/Docker/CI

  • v2.0: Plug-and-play — channels, smart routing (post_task_auto), task leases + heartbeat, lazy health monitor, session-aware reads, busy/DND, health_check, webhook retry with CAS, payload size limits, config validation, auto-unregister, dead-agent purge, debug mode. 22 tools.

  • v2.1: Architectural completion — explicit auth_state machine, managed-agent rotation grace, versioned ciphertext + keyring with online rotation (relay re-encrypt), lost-token recovery CLI (relay recover), admin-initiated cross-agent rotation (rotate_token_admin), structured error_code catalog, protocol_version surface, Phase 4p webhook-secret encryption, Phase 4b.1 v2 revoke/recovery redesign. 25 tools. 14 of 14 Codex architectural findings closed.

  • v2.7: Tether-ready cross-process inbox notifications. Durable inbox_events outbox table (schema v11→v12, idempotent migration); HTTP daemon polls + dispatches to relay://inbox/<agent> subscribers regardless of which OS process wrote the message. Reaper now skips sessions with active SSE GET streams (openGetStreams count). Daemon emits :keepalive\n\n SSE comment frames every RELAY_SSE_KEEPALIVE_MS (default 20s) so Electron-based clients (VS Code Tether extension) don't idle-disconnect at ~2.5min. External-review-flagged get_messages filter-after-mark P1 fix — since filter now applies in SQL BEFORE the read-mark mutation. 30 tools. Pairs with Tether VSCode extension v0.1.2+ on the marketplace.

  • v2.12: Hands-free, LLM-agnostic autowake. The multi-agent Tether watch-all wakes Claude and Codex agents on relay mail and auto-submits, no manual polling (dogfood-proven: the relay now coordinates its own development). Plus the pending-vs-history resolved plane (resolve_messages + an ack flag, so handled mail stops re-flooding fresh sessions) and a central schema-scoped auth fix closing a stray-field impersonation hole.

  • v2.13–v2.16: Presence liveness — a same-host process probe (agent_pid) so an idle-but-alive agent stops reading as offline (schema v16→v18) — plus the Tether-wakes-Codex PID handshake and the bin/codex-relay cold-start launcher (zero-turn cold summon of a Codex agent).

  • v2.17: LLM-agnostic spawn parity. spawn_agent(cli: "codex") launches a Codex CLI (macOS/Linux), driven by a central agent-CLI profile registry with an AST drift guard; Tether wake behavior is data-driven from the same registry.

  • v2.18: Sentinel (relay watch <agent>) — poll/marker-based autowake for terminals not in VSCode/Tether (iTerm2 personas, plain terminals, remote sessions). The wake-anywhere sibling of Tether's push-wake.

  • v2.19: Liveness derivation — presence that stops lying. The coarse agent status is now derived from the liveness verdict (online/offline/unknown), never from last_seen age, so a rate-limited-but-alive agent no longer reads offline.

  • v2.20: O(1) token authentication. An indexed HMAC token locator + a verified-token cache replace the O(N) per-call bcrypt scan (bcrypt stays the sole verifier); instant revocation via a generation counter; zero-lockout migration. 35 tools.

  • v2.21: ADR-0002 — agent class/flare topology. Agents declare an immutable coordination class at register_agent (orchestrator | builder | advisory | auditor | transient), a third axis orthogonal to role and capabilities. discover_agents view='topology' renders the live team grouped by class (default view='list' is unchanged), plus an opt-in SessionStart onboarding map (RELAY_ONBOARD_TOPOLOGY=1, default off) and a taxonomy drift guard in the pre-publish gate. Still 35 tools — a view parameter, not a new tool.

  • v2.22: Curl/script-caller DX hardening (ADR-0005). One-shot POST /mcp returns plain JSON, agent_token is the first field of the register response, send_message accepts a message alias, and abandon_registration lets a script self-clean a botched (never-authenticated) registration via a one-time recovery handle — safe by construction (only ever removes a row that has never authenticated). 36 tools.

  • v2.23: Config-clobber closed + orchestration-anchor safety + a HIGH dependency advisory. npm test no longer rewrites the real ~/.claude.json (an account-home chokepoint guard); non-destructive release-binding; the fast-uri HIGH advisory pinned out. 36 tools.

  • v2.24: Security hardening — operator auth (ADR-0006), dashboard content isolation, orchestration integrity. Operator power now requires a verified dashboard secret (relay init) rather than network position; the dashboard no longer serves message content to unauthenticated callers; the diagnostic traffic recorder redacts tokens. 36 tools.

  • v2.25: ADR-0011 — message disposition + read-receipts (schema v24, the get_outstanding tool). Plus a security-advisory sweep (fast-uri + ip-address HIGH advisories) and dependency majors (uuid 14, TypeScript 6.0.3 — not the 7.x Corsa rewrite). 37 tools.

  • v3.0 (current): Wake-coverage detector + undelivered-mail durability + a BREAKING since contract change. A default-on daemon sweep reports when an agent has mail stuck past the effective threshold and is no longer being woken — three verdicts (covered / uncovered / unobservable), REPORT-FIRST: an uncovered line is a possible wake-path regression, not proof of fault, and unobservable is never reported as uncovered. New agents.last_drain_at column and RELAY_WAKE_* knobs. Undelivered mail (not drained by any recipient) is no longer deleted at 7 days: it is held to a bounded 30-day grace (RELAY_UNDELIVERED_GRACE_DAYS, default 30; 0 disables the extension) with a stderr deadletter line when finally dropped. BREAKING: a pending drain now returns never-observed mail regardless of since — the 2.25 strict-cursor promise (since = "only return messages after this time") is withdrawn; audit any pending-drain since-cursor logic (see the CHANGELOG breaking entry; history reads are unaffected). Still 37 tools.

  • Beyond: cross-machine federation (hub/edge) so multiple self-hosted relays mesh without a shared namespace, plus an App Server push path for low-latency remote wake.

Dashboard

When running in http or both mode, open http://127.0.0.1:3777/ in a browser. You'll see:

  • Live agent presence (online / offline / unknown), derived from a liveness verdict — not last_seen age (v2.19)

  • Active tasks with priority and assignment

  • Recent messages

  • Registered webhooks

  • Recently completed tasks

Auto-refreshes every 3 seconds. Useful for "what's happening across all my terminals right now?" at a glance.

Role Templates

See roles/ for drop-in role specs. Examples:

  • planner.md — orchestrator that delegates and synthesizes

  • builder.md — worker that accepts and completes tasks

  • reviewer.md — skeptical reviewer with structured output

  • researcher.md — investigates questions, returns findings

Three ways to apply a role: paste into project CLAUDE.md, pass as initial_message when spawning, or wire via shell alias.

Requirements

  • Node.js 18+

  • Claude Code (or any MCP-compatible client)

License

MIT

Available Tools

37 tools
abandon_registrationA

Self-clean YOUR OWN botched (orphaned) registration when you lost the agent_token before ever authenticating — e.g. a curl/script caller truncated the register response. Authenticated by the one-time registration_recovery handle returned in the register_agent response (NOT the lost token), so it needs no auth token.

When to use: you registered but never captured/used the token, and the row is now an orphan you can't unregister (unregister needs the token you lost). NOT for a live agent that lost its token mid-session — that agent has authenticated, so this is refused; use rotate_token / relay recover instead.

Behavior: verifies the registration_recovery handle (bcrypt, name-scoped, one-time, TTL-bound) and — ONLY if the target row has NEVER authenticated (the keystone) — deletes it, bumps the auth generation, and fires an agent.unregistered webhook. The keystone is re-asserted inside the DELETE, so it can never reach a working agent (a row that authenticates between check and delete is left intact) — the safe, self-serve alternative to the operator kill endpoint. Orphans are also auto-GC'd after ~30min (never-authed + session-less + older than the orphan TTL) as a backstop.

Returns: { success, name, abandoned }. Errors (AUTH_FAILED): agent has authenticated (not an orphan), invalid/expired handle, or no such registration.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesThe agent name whose orphaned registration to abandon.
recovery_handleYesThe one-time registration-recovery handle returned in the register_agent response (the `registration_recovery` field). Name-scoped + short-lived.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden — and it delivers extensively. It documents the keystone (never-authenticated) safety guard, re-assertion inside DELETE, webhook firing, auth generation bump, the one-time/bcrypt/name-scoped/TTL-bound handle verification, the ~30min orphan auto-GC backstop, and the exact return shape plus AUTH_FAILED error conditions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose in the first sentence, then structured when-to-use/when-not, behavior, and returns sections. It's long but every sentence earns its place given the complex safety-critical semantics. Slightly verbose in places but well-organized with clear paragraph divisions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is an unusually complex tool (safety-critical deletion, one-time handles, keystone guards, TTL semantics, webhook side effects) with no annotations and no output schema. The description covers all of it: prereqs, failure modes, return values, side effects, and safety guarantees. Nothing material is left undocumented.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already describes both parameters (name and recovery_handle) thoroughly. The description adds some context (that the handle is name-scoped, one-time, short-lived, returned in register_agent response) but mostly references what the schema already documents, maintaining the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb+resource ('abandon YOUR OWN botched/orphaned registration') and clearly defines the scope. It distinguishes itself from siblings by explaining this is for unauthenticated orphans where the agent_token was lost post-register, vs unregister_agent which needs the token, and rotate_token for mid-session token loss.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (orphaned row where token was never captured), when NOT to use (live agent that lost token mid-session, which is refused), and names explicit alternatives (rotate_token, relay recover). Also notes the operator kill endpoint as related context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

broadcastA

Fan out a single message to every registered agent (or every agent of a given role).

When to use: announcements, fleet-wide pings, role-targeted prompts ('all builders, refresh your dependencies'). For 1:1 use send_message. For topical group coordination prefer post_to_channel, channels persist membership and avoid spamming agents who have explicitly opted out by leaving.

Behavior: stores one row per recipient with status='pending'; the sender is excluded from the recipient set. Fires one message.broadcast webhook event for the whole batch (delivery_id + idempotency_key in the envelope). Optional role narrows the recipient set. Same payload size cap as send_message (RELAY_MAX_PAYLOAD_BYTES).

Returns: { success: true, sent_to: string[], message_ids: string[], count, note }. count=0 with a note string when no other agents matched the filter (still success, not error).

Errors: AUTH_FAILED, PAYLOAD_TOO_LARGE, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
fromYesSender agent name
roleNoOnly send to agents with this role
contentYesBroadcast content (max 64KB by default; see RELAY_MAX_PAYLOAD_BYTES)
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It discloses substantial behavior: stores one row per recipient with status='pending', sender excluded, fires one webhook event with delivery_id/idempotency_key in envelope, optional role narrowing, payload cap reference, success/error semantics including count=0 with a note being still success. This is rich behavioral detail. Minor gap: doesn't mention rate limiting specifics or auth mechanism beyond error codes, but these are listed as errors.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (When to use, Behavior, Returns, Errors) and is front-loaded with the core purpose in the first sentence. Every sentence adds value: the alternatives, the behavioral guarantees, the return shape, and the error taxonomy. No waste or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a complex tool (broadcast fan-out with role filtering, idempotency via webhook envelope, multiple error modes), and no annotations or output schema are provided. The description compensates fully: it documents the return shape including edge case (count=0 with note as success), error codes, behavior guarantees (sender exclusion, one row per recipient, webhook event semantics), and payload limits. This is a complete spec for an agent to use the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all 4 parameters have schema descriptions. The description adds value beyond the schema: it clarifies that 'role' narrows the recipient set, and that content has the same size cap as send_message (RELAY_MAX_PAYLOAD_BYTES). The agent_token parameter is well-documented in schema with fallback resolution paths. The description doesn't add param-specific detail beyond what schema provides, but schema already covers it thoroughly, making baseline 3-4 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the purpose: 'Fan out a single message to every registered agent (or every agent of a given role).' It uses a specific verb ('fan out'), a clear resource (broadcast), and a scoping condition (all agents or by role). It effectively differentiates from siblings like send_message (1:1) and post_to_channel (topical group coordination).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use context ('announcements, fleet-wide pings, role-targeted prompts') and explicitly names alternatives with when-not-to-use conditions: 'For 1:1 use send_message' and 'For topical group coordination prefer post_to_channel' with a rationale (channels persist membership, avoid spamming opted-out agents). This is model guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_channelA

Create a named channel for many-to-many topical coordination.

When to use: ongoing conversations that more than two agents care about and that should persist beyond the lifetime of any one agent (e.g., #deploys, #triage). For 1:1 use send_message. For one-shot fleet-wide announcements use broadcast.

Behavior: creates the channel row and adds the creator as a member. Channels are flat (no hierarchy) and globally addressable by name. Auth: caller must hold the channels capability.

Returns: { success: true, channel: { id, name, description, created_by, created_at }, message }.

Errors: AUTH_FAILED (missing channels capability), ALREADY_EXISTS (name collision), INVALID_INPUT, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesChannel name (lowercase, alphanumeric + hyphens)
creatorYesAgent creating the channel
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
descriptionNoChannel description

TDQS

A4.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden — and it delivers well. It discloses what gets created ('channel row and adds creator as a member'), flat topology (no hierarchy), global addressability by name, and required auth capability ('channels'). It even lists all error codes. This is rich behavioral disclosure for a tool with zero annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured with clear labeled sections (When to use, Behavior, Returns, Errors), front-loads the core purpose, and every sentence earns its place. No fluff or redundancy; it's information-dense without being bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-param tool with 100% schema coverage, no output schema, and no annotations, this description is remarkably complete. It covers return format, error codes, auth requirements, structural semantics (flat, globally addressable), and positional guidance versus siblings. There are no meaningful gaps for an agent to be confused about.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (all 4 params have descriptions in the schema). Despite that, the description adds value by clarifying the name pattern (lowercase, alphanumeric + hyphens) is also echoed in the schema regex, and the agent_token param's optional resolution paths are documented in the schema. The description compensates for auth-related parameter meaning (agent_token) by noting capability requirements, going beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb+resource ('Create a named channel for many-to-many topical coordination') and clearly differentiates from siblings by naming alternatives (send_message, broadcast) with use-case distinction. The purpose is unambiguous and distinguishes this tool from the sibling list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section gives clear use cases (ongoing multi-agent conversations that persist beyond any single agent's lifetime) with concrete examples (#deploys, #triage). Also explicitly states when NOT to use it ('For 1:1 use send_message. For one-shot fleet-wide announcements use broadcast.'), naming alternatives directly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_webhookA

Tear down a webhook subscription by id.

When to use: cleanup when an integration is being retired, when the receiver URL is dead and you do not want delivery-log noise, or when rotating a webhook secret (delete + register fresh).

Behavior: removes the subscription row and any pending entries in webhook_delivery_log. Auth: the registrant's token, OR an authenticated agent with webhooks capability for cross-owner cleanup.

Returns on success: { success: true, webhook_id, note: 'Webhook deleted' }. Returns on missing-id: { success: false, webhook_id, note: 'Webhook not found' } with isError: true — this surfaces as a tool error so callers know the id was already gone (NOT a soft-success).

Errors: AUTH_FAILED, INVALID_INPUT. Missing-id is reported via the success: false + isError: true envelope above, not a separate error_code.

ParametersJSON Schema
NameRequiredDescriptionDefault
webhook_idYesWebhook subscription ID to delete
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure, and it delivers substantially. It explicitly states the destructive nature (removes subscription row AND pending delivery log entries), the auth requirements, exact success/failure return envelopes, the isError:true soft-fail semantics (missing-id surfaces as tool error, NOT a soft-success), and the distinction between error codes and the envelope. The only minor gap is not elaborating on side effects like dependent resources referencing the deleted webhook. No contradiction with any annotation (none exist).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Structured into labeled sections (When to use, Behavior, Returns, Errors) which aids scanning and front-loads the most critical info. At roughly 130 words, it is somewhat long but every section earns its place—the error semantics, in particular, genuinely matter for correct invocation. Slight verbosity in the Returns section where a shorter form would do, but overall well-organized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given this is a destructive mutation tool with NO annotations and NO output schema, the description compensates well by documenting side effects (delivery-log cleanup), return envelopes, error semantics, and auth requirements. It covers the essential operational context a caller needs. Minor gaps remain: no mention of idempotency behavior or whether concurrent delete failures occur, but these are edge cases beyond the minimum viable scope.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%; both parameters (webhook_id, agent_token) already have descriptions in the schema. The description reinforces that agent_token is optional and resolvable from env/header, consistent with schema. It adds marginal value by emphasizing webhook_id is the selection mechanism, but the schema already handles parameter semantics adequately. Baseline 3 is appropriate when schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb ('Tear down') + resource ('webhook subscription') + method of identification ('by id'). Clearly distinguishes from siblings: register_webhook and list_webhooks are creation/listing, while delete_webhook is the teardown counterpart. The description immediately establishes what the tool does without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section covers three concrete scenarios: integration retirement, dead receiver URL with delivery-log noise, and secret rotation (delete + register fresh). It implicitly positions this against register_webhook for the rotation use case, offering clear decision guidance. Additionally, it distinguishes auth paths (registrant's token vs cross-owner with webhooks capability) which clarifies authorization context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

discover_agentsA

List every registered agent with computed presence + operator-controlled status.

When to use: pick a routing target by role (e.g., 'any builder'), confirm an expected agent is online before sending it work, or surface the agent fleet to a dashboard. For periodic team rollups use get_standup instead, it bundles agents + recent activity in one call.

Behavior: pure read; never mutates last_seen (v1.3 presence-integrity fix). Optionally filters by role. The returned status (online | stale | offline) is computed from last_seen deltas; the returned agent_status (idle | working | blocked | waiting_user | stale | offline | abandoned | closed) is operator-controlled via set_status. Token hashes are stripped, has_token: boolean only.

Returns: { agents: AgentWithStatus[], count: number, filter: { role } | 'none' }. Ordered by last_seen DESC.

Errors: RATE_LIMITED. (No auth required, this surface is intentionally observable for orchestration.)

ParametersJSON Schema
NameRequiredDescriptionDefault
roleNoFilter by role
viewNoADR-0002: 'list' (default) = flat agent list (unchanged). 'topology' = the live team grouped by coordination class (orchestrator/builder/advisory/auditor), flat within each; transient + unclassified + dead/terminal agents are excluded from the who's-who.list
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden and delivers: states it's a pure read ('never mutates last_seen'), explains the difference between computed `status` vs operator-controlled `agent_status`, discloses token hashes are stripped (returns `has_token: boolean` only), documents the exact return shape, ordering, and error mode (RATE_LIMITED), plus the notable detail that no auth is required for observability. This is remarkably complete behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-organized with clear section labels (When to use, Behavior, Returns, Errors), front-loaded with the core purpose sentence. Every sentence earns its place. Slightly dense in the Behavior section covering multiple concepts, but the structure is clean and scannable, so only a minor deduction.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list tool with zero required params and 100% schema coverage, the description is exceptionally thorough: it documents return shape, ordering, error modes, auth posture, security behavior (token stripping), and the semantic distinction between computed vs operator-controlled status fields. Despite no output schema, the description fully compensates by spelling out the response contract.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value on top: explains that `role` is an optional filter, clarifies that `status` is computed while `agent_status` is operator-controlled (differentiating the semantics of what the tool returns). Since all params are already fully documented in the schema at 100% coverage, the description's contributions are bonus context rather than compensation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description states the exact verb+resource ('List every registered agent with computed presence + operator-controlled status'), explicitly distinguishes from sibling `get_standup` by naming it and contrasting scope. The phrase 'list every registered agent' with the added detail of computed presence and operator-controlled status makes the purpose concrete and non-tautological.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit 'When to use' guidance with three concrete scenarios (routing target by role, confirming online before sending work, surfacing fleet to dashboard) and an explicit exclusion ('use get_standup instead, it bundles agents + recent activity'). This is textbook usage guidance with clear when-to and when-not-to direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

expand_capabilitiesA

Self-managed additive capability expansion (v2.1.4).

When to use: an agent registered (often via the SessionStart hook) with a narrow capability set and now needs more, e.g., a builder later picks up a webhooks integration. Reductions are NOT supported (unregister_agent + fresh register_agent for those). For privileged cross-agent edits, no equivalent admin tool exists by design, capability changes are caller-attested.

Behavior: caller presents their token; the requested set MUST be a SUPERSET of current caps (additive only, closes the v1.7.1 immutability gap without re-opening the capability-escalation CVE). Reductions reject with REDUCTION_NOT_ALLOWED. Requesting only already-held caps rejects with NO_OP_EXPANSION. The expansion is recorded in the audit log with the verified caller name.

Returns: { success: true, agent, added: string[], capabilities: string[] }. capabilities is the new full set after expansion; added is the diff of newly-granted caps.

Errors: NOT_FOUND (unknown agent), REDUCTION_NOT_ALLOWED, NO_OP_EXPANSION, AUTH_FAILED, INTERNAL.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_nameYesYour agent name. Must match the row your token authenticates to.
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
new_capabilitiesYesThe full new capability set. Must be a superset of the agent's current caps — additive only.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so description carries full burden — and it delivers richly. It discloses the additive/superset constraint (rejecting reductions), the specific error condition REDUCTION_NOT_ALLOWED and NO_OP_EXPANSION, the immutability-gap/CVE rationale, audit-log recording with verified caller name, and token resolution paths (param, env, header). This is comprehensive behavioral disclosure for a mutation tool with zero annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear labeled sections (When to use, Behavior, Returns, Errors). It is slightly verbose — the CVE/immutability-gap rationale adds some length — but every sentence earns its place by conveying operational constraints. The front-loading of purpose and when-to-use is effective.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no output schema and no annotations, the description fully specifies the return shape (`{ success, agent, added, capabilities }`), enumerates all error cases with conditions, and covers auth mechanisms. For a mutation tool with behavioral complexity (additive constraint), this is remarkably complete. The version-pinned rationale and audit-log detail fill the gap left by absent annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents all three parameters including semantics for new_capabilities (superset requirement) and agent_token resolution. The description reinforces the superset constraint and 'added' vs 'capabilities' return distinction, but the parameters themselves are well-covered by the schema. Baseline 3 is appropriate since schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb+resource ('expand capabilities of an agent') and clearly distinguishes the operation from siblings like register_agent and unregister_agent. It identifies the version (v2.1.4), explicitly notes it is self-managed/additive-only, and explains reductions are handled elsewhere. This is a strong, specific purpose statement that differentiates from the sibling registration tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section explicitly names the scenario (agent registered with narrow caps via SessionStart, later needing more like webhooks). It also gives explicit when-not guidance: reductions are NOT supported here and require unregister+register, and it notes there's no admin equivalent by design. This is explicit when/when-not/alternatives coverage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_channel_messagesA

Read the messages you are entitled to see in a channel.

When to use: any flow that observes channel traffic, dashboard refresh, post-incident review thread, role onboarding ('catch up on #deploys'). For 1:1 mailbox use get_messages; for whole-fleet activity use get_standup.

Behavior: scoped to messages with created_at >= your join_time. Ordered by priority then created_at newest-first. Pure read, channel posts have no per-recipient read state, so this call is fully idempotent. Auth: caller must be a current member.

Returns: { messages: ChannelMessage[], count, channel_name, agent }. Each message carries id, channel_id, from_agent, content, priority, created_at.

Errors: NOT_MEMBER (caller has not joined the channel), NOT_FOUND, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax messages to return
sinceNoISO timestamp — only return messages after this time
agent_nameYesAgent reading (must be a member)
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
channel_nameYesChannel to read from

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It goes well beyond minimal by disclosing the join_time scoping, ordering semantics, idempotency ('fully idempotent'), auth requirement (current member), and error conditions. Slight gap: doesn't explain what happens to messages before join_time beyond 'entitled to see.' Otherwise thorough.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with labeled sections (When to use, Behavior, Returns, Errors) and zero filler. Every line earns its place, front-loaded with the core purpose in the first sentence. Appropriate length for a tool that needs to clarify scoping and auth.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a list-read tool with no output schema, no annotations, and 5 parameters, this description is comprehensive: it documents return shape, message fields, errors, ordering, auth, and scoping. Despite the 5-param schema, the description covers all behavioral aspects an agent needs to correctly invoke and interpret this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so all 5 parameters have schema-level descriptions. The description adds value by tying created_at/ordering to the 'since' parameter's semantics and explaining the join_time scoping inherent to the call. It doesn't restate each schema description but the schema already handles those; the description enriches behavioral context around them.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear verb+resource: 'Read the messages you are entitled to see in a channel.' It explicitly distinguishes itself from siblings by naming get_messages (1:1 mailbox) and get_standup (whole-fleet activity), which disambiguates against the closest alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides an explicit 'When to use' section listing concrete scenarios (observes channel traffic, dashboard refresh, post-incident thread, role onboarding) and names when NOT to use with alternatives ('For 1:1 mailbox use get_messages; for whole-fleet activity use get_standup'). This is exemplary guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_messagesA

Drain or peek your own mailbox.

When to use: each turn that should observe new mail; orchestrators that batch-poll many agents may prefer get_messages_summary (cheaper preview) or peek_inbox_version (counts only). For surveys that must NOT consume mail, set peek=true.

Behavior: returns messages addressed to you, ordered by priority then created_at newest-first. By default status='pending' returns un-read messages and atomically marks them read for THIS session (sessions are per-session_id; a fresh terminal re-sees previously-read messages, v2.0 final fix). Optional since ('1h' | '24h' | '7d' | ISO | 'all') bounds already-OBSERVED history (v2.1.6 default '24h'); a pending drain ALWAYS returns UNDELIVERED (never-drained) mail regardless of since (#198; 3.0.1 — keyed on delivery, not observation, so a prior peek cannot hide it). When status='pending' returns 0 with since<24h, the response includes a hint field nudging toward since='all' for older already-seen mail. peek=true (v2.2.2) suppresses the read-MARK — the returned messages are not marked read for your session — but it is NOT side-effect-free: like any first view it stamps the observation cursor (seq). (Post-3.0.1 that observation no longer changes what a later drain returns.)

Returns: { messages: MessageRecord[], count, agent, filter, since, since_bound, hint? }. since_bound is the ISO timestamp the relay actually filtered by (after resolving duration shorthands or 'session_start').

Errors: AUTH_FAILED, VALIDATION (bad since format), RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
ackNov2.12.0: when true, permanently resolve the returned messages (atomic with the read-mark) so a fresh session won't re-surface them. Only applies to status='pending'. Default false = unchanged behavior.
laneNoFilter by message lane: 'all' (default), 'direct' (point-to-point only), or 'capability' (capability-routed FYI only).all
peekNoWhen true, skip the mark-as-read side effect so repeated status='pending' polls return the same messages. Default false (consume-once).
limitNoMax messages to return
sinceNov2.1.6: time-window filter over ALREADY-OBSERVED history. Accepts duration ('15m'|'1h'|'24h'|'3d'), ISO8601 timestamp, 'session_start' sentinel, or 'all'/null to disable. Default '24h' trims stale backlog when an agent name is reused. #198: this bound applies ONLY to mail this recipient has already seen — a PENDING drain ALWAYS returns never-observed (undelivered) mail regardless of `since`, so undelivered mail can never age out of reach.24h
statusNoFilter by status. 'pending' (default) = unresolved + not-read-by-this-session (the action queue). 'read' = read by this session. 'all'/'history' = the full durable record incl. resolved. 'resolved' = only messages you've acked (v2.12.0).pending
agent_nameYesYour agent name
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and fully delivers. It discloses atomic mark-as-read semantics, the peek side effect of stamping the observation cursor, per-session read behavior, the distinction between delivery and observation, versioned behavior changes (#198, 3.0.1), response hints, and error codes. This is exceptionally transparent for a tool with significant side-effect complexity.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but every section earns its place: a crisp one-line summary, a dedicated usage section, behavioral details, return shape, and error codes. It is carefully structured with clear labels and front-loaded with the most essential information. Version markers are compact and do not bloat readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This tool has 8 parameters, complex side effects, session semantics, and no output schema, yet the description covers behavior, return shape, error cases, and alternatives. It explains subtle delivery-vs-observation semantics and what fields like since_bound mean. The description is complete enough for an agent to invoke correctly without further external knowledge.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already covers 100% of parameters in detail, so the baseline is 3. The description adds meaningful cross-parameter context beyond schema text, such as how since interacts with pending drains, the hint nudging behavior, and the observation-cursor side effect of peek. The ack parameter remains primarily schema-documented, but the overall description adds enough semantics to warrant a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening phrase 'Drain or peek your own mailbox' is a specific verb-resource pairing that immediately establishes the tool's role. The description then states exactly what it returns ('messages addressed to you, ordered by priority then created_at newest-first') and distinguishes itself from siblings like get_messages_summary and peek_inbox_version.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section explicitly says 'each turn that should observe new mail' and names concrete alternatives for different scenarios: get_messages_summary for cheaper previews, peek_inbox_version for counts only, and peek=true for surveys that must not consume mail. This is exemplary usage guidance with clear exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_messages_summaryA

Cheap, non-mutating mailbox preview (v2.1.6).

When to use: orchestrators scanning many inboxes per cycle, dashboards rendering a per-agent backlog count, or any flow where you want to see what is there without consuming it. After picking interesting IDs, expand them with get_messages (which CAN mutate) or read them by ID.

Behavior: same status + since filter surface as get_messages. Returns headers + a 100-char content_preview (decrypted on the fly when RELAY_ENCRYPTION_KEY is set). Never marks messages read. Auth: agent token (own mailbox only).

Returns: { summaries: { id, from_agent, priority, status, created_at, content_preview, content_truncated }[], count, agent, filter, since, since_bound }. content_truncated=true when the original content exceeded the 100-char preview cap.

Errors: AUTH_FAILED, VALIDATION, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax message summaries to return
sinceNov2.1.6: time-window filter over ALREADY-OBSERVED history. Accepts duration ('15m'|'1h'|'24h'|'3d'), ISO8601 timestamp, 'session_start' sentinel, or 'all'/null to disable. Default '24h' trims stale backlog when an agent name is reused. #198: this bound applies ONLY to mail this recipient has already seen — a PENDING drain ALWAYS returns never-observed (undelivered) mail regardless of `since`, so undelivered mail can never age out of reach.24h
statusNoFilter by status (same surface as get_messages). 'pending' excludes resolved mail (v2.12.0) so the preview agrees with the mutating drain.pending
agent_nameYesYour agent name
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite having no annotations, the description thoroughly discloses behavioral traits: it is non-mutating ('Never marks messages read'), it decrypts on the fly when RELAY_ENCRYPTION_KEY is set, it requires agent token auth (own mailbox only), and it notes the since filter nuance with 'ALREADY-OBSERVED history' and the PENDING drain behavior. This fully covers the behavioral burden, exceeding what annotations would typically provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections: purpose, when-to-use, behavior, returns, and errors. It is informative yet concise, with no redundant sentences. Every sentence adds value (e.g., the content_truncated flag explanation, error codes). The formatting with headers aids readability and scannability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only preview tool with 5 parameters, the description is complete: it covers the tool's purpose, usage scenario, behavioral nuances (including tricky since semantics), return format, auth requirements, and error codes. There is no output schema, so the return structure detail is valuable)Skip, and the description fully compensates for the lack of structured output metadata. It is comprehensive enough for an agent to correctly invoke and interpret results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters with detailed descriptions, including the complex 'since' semantics and status filter. The description adds a brief note about the same status/since surface as get_messages, but it doesn't go beyond what the schema already explains. Baseline 3 is appropriate since the schema carries the load.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it is a cheap, non-mutating mailbox preview. It distinguishes itself from siblings by explicitly mentioning get_messages as the mutating alternative, and the return structure provides concrete specifics. The verb 'preview' plus the resource 'mailbox' makes the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use: 'orchestrators scanning many inboxes per cycle, dashboards rendering backlog count, or any flow where you want to see what is there without consuming it.' It also provides a clear alternative: 'After picking interesting IDs, expand them with get_messages (which CAN mutate) or read them by ID.' This gives both positive and negative usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_outstandingA

The SENDER's outstanding-ask recap + the pull source of truth for overdue drift (ADR-0011).

When to use: an orchestrator (or any sender) reconstructing what it is owed — on a fresh session, or any time it wants the current picture of asks/obligations it SENT that haven't been resolved. This PULL is the source of truth; the optional message.read/message.resolved webhooks are push-on-top and may be missed.

Behavior: returns the messages YOU sent with disposition in ('ask','obligation'), each with its sender-visible lifecycle state (unread / read-unresolved / resolved) and a REPORT-ONLY overdue flag computed at query time — it NEVER mutates a message (report-first, never auto-resolve). LOG messages are excluded (LOG never goes overdue). overdue = still-unresolved AND past its bound: an obligation past its deadline, else an ask/deadline-less-obligation past created_at + RELAY_OVERDUE_SECONDS (default 24h, tunable). include_resolved=false (default) returns only the outstanding set; true adds resolved rows for the full sender view. Auth: agent token; sender-scoped (you only see mail you sent).

Returns: { success, agent, include_resolved, overdue_bound_seconds, count, overdue_count, outstanding: { id, to_agent, disposition, created_at, deadline, read_at, resolved_at, state, overdue, content_preview, content_truncated }[] }.

Errors: AUTH_FAILED, VALIDATION, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_nameYesYour agent name — the SENDER whose outstanding asks/obligations to recap
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
include_resolvedNofalse (default) = only still-outstanding (unresolved) ask/obligation messages YOU sent — the recap. true = the full sender view incl. resolved rows (unread/read-unresolved/resolved).

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and excels: it explicitly states the tool NEVER mutates a message, is report-first, and auto-resolves nothing. It details the overdue calculation, auth requirements (agent token, sender-scoped), and error codes including RATE_LIMITED. This is comprehensive behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but exceptionally well-structured, with clear sections for summary, usage, behavior, return value, and errors. Every sentence adds value, and it front-loads the core purpose immediately. No redundancy or fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema and no annotations, the description fully compensates by specifying the return object shape with all fields, explaining the lifecycle states, the overdue flag, and error codes. It is complete for an agent to know exactly what to expect and how to handle errors.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with detailed parameter descriptions already present. The description adds minimal extra meaning: it restates the include_resolved semantics and agent_name role but does not significantly go beyond the schema. Baseline 3 is appropriate because the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'The SENDER's outstanding-ask recap + the pull source of truth for overdue drift.' It clearly distinguishes this tool from siblings like get_messages or get_messages_summary by focusing on sent asks/obligations, the outstanding set, and the 'PULL' nature versus webhooks. The purpose is unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section explicitly states use cases (orchestrator reconstructing what it is owed on a fresh session or any time needing current picture) and contrasts with webhooks that may be missed. It also explains that LOG messages are excluded and that include_resolved changes the view, providing clear situational guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_standupA

One-shot team-status synthesis for orchestrators (v2.1.4).

When to use: every observation cycle that would otherwise call discover_agents + get_messages + get_tasks and synthesize in-LLM. The relay does the rollup server-side so the caller burns near-zero tokens. For specific drill-downs after the rollup, fall through to the underlying tools.

Behavior: pure read. Given a window (since: '15m' | '1h' | '3h' | '1d' | ISO), returns active_agents (filtered to non-offline by default, set include_offline=true to include them), message_activity counts, task_state breakdown, and rule-based observation bullets ('agent X has been blocked >30min', etc.). Observations are hand-rolled heuristics, NO LLM on the relay side. Optional agents / roles arrays narrow the snapshot. Auth: any agent token.

Returns: { success: true, window: { since, now, duration_ms }, active_agents: Agent[], message_activity, task_state: { completed_in_window, queued, blocked, assigned_by_agent }, observations: string[] }.

Errors: VALIDATION (bad since format), AUTH_FAILED, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
sinceYesWindow start: either a duration string ('15m' | '1h' | '3h' | '1d') or an ISO8601 timestamp. Duration shorthands: m=minutes, h=hours, d=days.
filterNoOptional narrowing filter for the standup snapshot. Combine `agents` (restrict to names) and `roles` (restrict to roles); both filters AND together. `include_offline` flips the default that drops offline agents from active_agents.
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden — and it delivers thoroughly. It declares 'pure read', explicitly states 'NO LLM on the relay side' (observations are hand-rolled heuristics), details the default filtering behavior (non-offline, flippable via include_offline), documents the auth mechanism ('any agent token'), and lists all error codes. This is exemplary disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well structured with clear labeled sections: When to use, Behavior, Auth, Returns, Errors. Every sentence earns its place — no filler, each line conveys operational information an agent needs. Front-loaded with the core purpose and usage decision, then behavioral details, then error contract.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with nested objects, 3 params, no output schema, and no annotations, this description is exhaustive. It defines the complete return structure, ALL error modes, authentication requirements, filtering semantics, and a nuanced behavioral caveat (heuristic observations, no LLM server-side). Nothing material is left ambiguous for correct invocation and result interpretation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema documents all params. The description adds meaningful semantic value on top: it explains the since values, defines what include_offline flips in the default behavior, specifies that agents/roles narrow the snapshot and AND together, and rejects the agent_token parameter since it's noted optional. It adds real context beyond the schema's field descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description is highly specific: 'One-shot team-status synthesis for orchestrators' with a clear verb-resource pair (get + standup/rollup). It explicitly defines the return contract (active_agents, message_activity, task_state, observations), distinguishes its server-side aggregation role from sibling tools, and names alternative tools to fall through to. This belongs in the Tier A category.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Excellent when/when-not guidance. It states explicitly to use this instead of chaining discover_agents + get_messages + get_tasks, points out the token-efficiency rationale, and instructs to 'fall through to the underlying tools' for drill-downs. It also enumerates all error types, giving the agent actionable preconditions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_taskA

Look up a single task by id.

When to use: any flow that needs the canonical state of one specific task, e.g., the assignee just heartbeat'd and wants to confirm the row, or the requester is checking on a known task_id from an earlier post_task response. For browsing many tasks use get_tasks.

Behavior: pure read. Returns the full task record including encrypted-at-rest description + result fields decrypted on the fly. Auth: agent token whose row is either the requester or assignee on this task, the relay refuses to leak third-party tasks.

Returns: { success: true, task: TaskRecord } with the full row including encrypted-at-rest description + result decrypted on the fly.

Errors: NOT_PARTY (caller is not the requester or assignee), NOT_FOUND (unknown task_id), RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
task_idYesTask ID to look up
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden — and it does so thoroughly. It discloses pure-read behavior, decryption-on-the-fly, and critically the auth restriction (agent must be requester or assignee; relay refuses third-party leaks). It even enumerates the actual error codes (NOT_PARTY, NOT_FOUND, RATE_LIMITED), which is rich behavioral disclosure beyond any annotation could offer.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-organized with clear section headers (When to use, Behavior, Returns, Errors). Every sentence earns its place — no filler, no repetition beyond the intentional emphasis of return payload. Efficient and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-row lookup with no output schema, this description is remarkably complete: it specifies the return shape ({ success: true, task: TaskRecord }), the decryption behavior, auth constraints, and all error conditions. Nothing material is left to guesswork.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents both parameters well. The description adds some context by tying task_id to the post_task response and noting agent_token is optional/resolvable from env or header, which is useful. This is above the baseline 3 for full coverage but not extensive extra semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Look up a single task by id') and clearly distinguishes from sibling get_tasks (singular vs many). The purpose is unambiguous and the tool's niche is well defined.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section gives concrete scenarios (heartbeat confirmation, checking a known task_id from post_task), and names the alternative get_tasks for browsing many tasks. Provides clear when-to-use/when-not-to guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_tasksA

Query the tasks you are involved with.

When to use: assignees triaging their queue (role='assigned'), requesters checking on dispatched work (role='posted'). For a single task by id use get_task. For team-wide rollup use get_standup.

Behavior: pure read. Filters by role + status; default status='all'. Ordered by priority then created_at newest-first. Auth: agent token (only your own tasks are visible).

Returns: { tasks: TaskRecord[], count, agent, role, filter }. Each task carries id, from_agent, to_agent, title, description, priority, status, result, created_at, updated_at, lease_renewed_at.

Errors: AUTH_FAILED, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
roleNo'assigned' = tasks for you, 'posted' = tasks you createdassigned
limitNoMax tasks to return
statusNoFilter by task statusall
agent_nameYesYour agent name
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations were provided, so the description carries the full burden. It explicitly declares 'pure read' behavior, explains ordering (priority then created_at newest-first), auth requirements (agent token, only own tasks), and documents the default filter value (status='all'). It does not disclose pagination behavior beyond the limit param, and error codes are listed. Very strong for an unannotated tool, though it could mention whether pagination/cursor exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured with clear labeled sections (When to use, Behavior, Returns, Errors). Every sentence earns its place with informative content. It is comprehensive yet tightly organized, with no fluff or repetition. Excellent use of section headers for scannability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-parameter query tool with no output schema, the description is quite thorough. It documents return shape (tasks, count, agent, role, filter with per-task fields listed), auth model, ordering, defaults, and error codes. Given the rich parameter set and the description's good coverage, it's close to complete. Minor gaps: no pagination explanation and limit behavior is only in the schema, but these are adequately covered by schema descriptions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema fully documents all 5 parameters. The description adds context by clarifying the meaning of role enum values ('assigned' = tasks for you, 'posted' = tasks you created) which echoes the schema. It reinforces the default status='all' behavior. The description adds marginal value beyond the thorough schema but does not introduce new parameter information. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb+resource: 'Query the tasks you are involved with.' It differentiates from siblings by explicitly naming get_task (single task by id) and get_standup (team-wide rollup), and distinguishes roles ('assigned' vs 'posted'). This is a specific and unambiguous purpose definition.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: assignees triaging their queue (role='assigned') and requesters checking on dispatched work (role='posted'). It also names alternatives (get_task for single task, get_standup for team-wide rollup) and explains auth scoping (only your own tasks visible). This is exemplary usage guidance with both usage cases and exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

health_checkA

Report relay process health + live counts.

When to use: liveness probes (/health HTTP endpoint mirrors this surface), version-pinning checks during upgrades, dashboard footers. Cheaper than get_standup for binary up/down questions.

Behavior: pure read. Counts agents by presence, pending messages, active and queued tasks, channels, and webhook subscriptions. Reports version (from package.json via the v2.1 Phase 4a single source of truth) + protocol_version (the client-compat surface, distinct from package version). Works on stdio AND HTTP transports. No capability required, intentionally observable.

Returns: { status: 'ok', version, protocol_version, transport, uptime_seconds, legacy_grace_active, agents: {...counts}, messages: {...counts}, tasks: {...counts}, channels, webhooks }. When the caller presents a token (arg / header / env), the response also includes token_validated: true, auth_error: boolean, and (on validation failure) auth_error_reason, plus agent_name + auth_state on success.

Errors: none expected (status='ok' is the only success shape).

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries the full burden, and it delivers: declares 'pure read' behavior, lists exactly what is returned including the detailed return object fields, discloses token-based conditional behavior, and states 'Errors: none expected'. Very thorough behavioral disclosure given zero annotation support.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded with a one-line summary, then clear labeled sections (When to use, Behavior, Returns, Errors). Every sentence earns its place—no filler, efficient use of paragraphs and code formatting for the return shape.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a read-only health check with one optional param and no output schema, the description fully specifies the return shape, error behavior, transport compat, and token handling. Nothing material is missing; it's complete for its complexity level.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents the single optional parameter. The description adds context by noting the token is 'also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header', going beyond the schema's own description. This is a small refinement over a strong schema; baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb+resource ('Report relay process health + live counts') and clearly distinguishes from siblings, notably naming get_standup as the heavier alternative ('Cheaper than get_standup for binary up/down questions'). The scope of what's counted (agents, messages, tasks, channels, webhooks) is explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use ('liveness probes', 'version-pinning checks', 'dashboard footers') and provides a direct comparison with get_standup including a cost/benefit rationale. It also notes transport compatibility (stdio AND HTTP) and that no capability is required, giving complete selection guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

join_channelA

Subscribe to a channel so you receive its messages from your join time forward.

When to use: any agent that wants to follow a channel's traffic, joining is open to any authenticated caller (no invite gate; channels are intentionally low-friction). Pair with post_to_channel for posting and get_channel_messages for reading.

Behavior: inserts the membership row with joined_at = now. get_channel_messages and the channel.message_posted webhook event scope to messages with created_at >= joined_at for this member, so historical traffic is NOT replayed (a deliberate design choice, channels are streams, not archives). Idempotent: rejoining is a no-op. Auth: any agent token.

Returns: { success: true, channel_name, agent_name, joined: boolean, note }. joined=false indicates the agent was already a member (idempotent no-op).

Errors: AUTH_FAILED, NOT_FOUND (unknown channel_name), RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_nameYesAgent joining
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
channel_nameYesChannel to join

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full behavioral disclosure burden and delivers richly: it explains the joined_at = now semantics, the non-replay of historical traffic ('streams, not archives'), idempotency ('rejoining is a no-op'), auth requirements (any agent token), return shape including joined=false case, and full error catalog (AUTH_FAILED, NOT_FOUND, RATE_LIMITED). This is exemplary transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (When to use, Behavior, Returns, Errors) and fronts the core purpose immediately. It's longer than minimal, but every sentence earns its place given the behavioral nuances it explains; no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-param tool with no output schema and no annotations, the description is remarkably complete: it covers auth, idempotency, event semantics, return format including edge cases, error codes, and temporal behavior. Nothing material is left unstated for an agent to invoke this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3. The description adds semantics around agent_name (who is joining) and channel_name (which channel to subscribe to) but the schema already documents each parameter adequately. The description adds the 'joined' return semantics for interpreting the join result but doesn't add much per-parameter meaning beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource ('Subscribe to a channel so you receive its messages from your join time forward'), clearly distinguishing it from siblings like post_to_channel and get_channel_messages. It explicitly positions itself against related channel tools, making the purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states 'When to use' (follow a channel's traffic), provides alternatives ('Pair with post_to_channel... and get_channel_messages'), and explains that joining is open to any authenticated caller with no invite gate. This is model-tier usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

leave_channelA

Cancel your membership so you stop receiving a channel's messages.

When to use: channel is no longer relevant to your role, or you are shutting down and want to be a clean citizen (the dashboard surfaces ghost members otherwise). Idempotent, calling it on a channel you never joined is fine.

Behavior: removes the membership row. Past messages stay in the channel (other members still see them); your joined_at cursor is forgotten so a future join_channel starts a fresh observation window. Auth: agent token (you can only leave on your own behalf).

Returns: { success: true, channel_name, agent_name, left: boolean, note }. left=false indicates the agent was not a member (idempotent no-op).

Errors: AUTH_FAILED, NOT_FOUND, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_nameYesAgent leaving
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
channel_nameYesChannel to leave

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and delivers richly: discloses that the membership row is removed, past messages persist, the joined_at cursor is forgotten, auth is per-agent token with a self-only limitation, and even the exact return shape with semantics for left=false. Error list (AUTH_FAILED, NOT_FOUND, RATE_LIMITED) adds agent-relevant expectations. This is exemplary transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Structured with clear labeled sections (When to use, Behavior, Returns, Errors). Every sentence carries distinct information with zero filler. Front-loaded with the core function in the first line, then efficient structured detail. Well-sized for the complexity of the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description properly explains the return shape ({success, channel_name, agent_name, left, note}) with semantics for left=false. Auth requirements, idempotency, error responses, and side effects on the observation window are all covered. For a mutating tool with no annotations, this is fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The schema already documents each of the three parameters clearly (channel to leave, agent leaving, token resolution options). The description adds the auth context (agent token resolution paths) that enriches the agent_token param but doesn't add meaning substantially beyond the schema since coverage is already complete.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a clear verb+resource pairing ('Cancel your membership') and states the tool's effect ('stop receiving a channel's messages'). It clearly distinguishes from siblings like join_channel and post_to_channel by focusing on leaving. The purpose is immediately obvious and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'channel is no longer relevant to your role' and 'shutting down and want to be a clean citizen'. Note about idempotency ('calling it on a channel you never joined is fine') gives clear what-happens-when guidance. It also references siblings implicitly through the fresh observation window note about join_channel.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_webhooksA

List every webhook subscription registered on the relay.

When to use: sanity-checking integrations ('is the Slack notifier still wired up?'), pre-cleanup audits, or building an admin UI. To narrow by event you currently filter client-side from this list.

Behavior: pure read. The raw HMAC secret is NEVER returned, each row exposes has_secret: boolean only. Auth: any registered agent (subscriptions are observable so admins can audit them, but secrets stay write-only).

Returns: { webhooks: { id, url, event, filter, has_secret, created_at }[], count }.

Errors: AUTH_FAILED, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It explicitly states this is a pure read, that the raw HMAC secret is never returned (only has_secret boolean), specifies auth requirements (any registered agent), and discloses errors like AUTH_FAILED and RATE_LIMITED. This is thorough behavioral disclosure for a read tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is tightly organized with clearly labeled sections (When to use, Behavior, Returns, Errors). Every sentence delivers distinct value — no filler, no redundancy with the schema. It front-loads the core purpose and groups related information efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a parameterless-read tool with 100% schema coverage on its single optional param, this description is complete. It covers purpose, usage context, security guarantees, auth model, return format, and error codes. An output schema is absent, so specifying the exact return shape is appropriate and enhances completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter agent_token has 100% schema coverage in the schema. The description adds clarity by explaining it's optional and resolvable via env var or header. Since schema coverage is high, the baseline is 3, and the extra context about alternative resolution methods earns a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists every webhook subscription on the relay. It uses a specific verb+resource combination and differentiates from siblings like register_webhook and delete_webhook by describing the read/list nature. The return shape and event filtering are explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides concrete when-to-use scenarios (sanity-checking integrations, pre-cleanup audits, admin UI) and notes client-side filtering limitation for narrowing by event. It doesn't explicitly name an alternative tool for server-side filtering, but the context of siblings is sufficient to guide selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

peek_inbox_versionA

Cheap non-mutating mailbox version probe (v2.3.0 Phase 4s, ambient wake support).

When to use: low-rate polling that wants to know 'do I have new mail?' without paying a get_messages round-trip, clients diff total_unread_count against their cached value and only call get_messages on a change. Pair with the optional filesystem-marker wake (when RELAY_FILESYSTEM_MARKERS=1) for low-latency idle wake. For full mailbox content use get_messages (mutating) or get_messages_summary (preview).

Behavior: pure read. Returns { mailbox_id, epoch, last_seq, total_messages_count, total_unread_count }. WATCH total_unread_count for new-mail detection, it advances on every send_message/broadcast to this agent. last_seq only advances when the recipient calls get_messages (read-cursor). epoch rotates on backup/restore, a client whose cached epoch no longer matches MUST reset its local last_seen_seq to 0 and re-drain. Auth: any agent token.

Returns: { success: true, mailbox_id, epoch, last_seq, total_messages_count, total_unread_count }.

Errors: AUTH_FAILED, NOT_FOUND (unknown agent_name), RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_nameYesThe agent whose mailbox to observe
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It thoroughly discloses: pure read (non-mutating), the semantics of each return field (epoch rotation on backup/restore, last_seq only advances on read-cursor, total_unread_count advances on every send), required reset behavior on epoch mismatch, auth requirements (any agent token), and possible error codes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Structure is exemplary — sections for when-to-use, behavior, returns, and errors are clearly delineated. Every sentence earns its place; the behavioral semantics around epoch, last_seq, and total_unread_count are dense but essential. No fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a no-output-schema, no-annotation tool, the description is remarkably complete. It explains error types, auth requirements, the full return shape, subtle cursor/epoch semantics, a concrete usage pattern, and a wake integration. An agent has everything needed to call this correctly and interpret results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema documents both parameters. The description adds context about agent_token ('Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header'), which goes beyond the raw schema. It doesn't add much beyond that, which is acceptable given perfect schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: a 'Cheap non-mutating mailbox version probe' that checks for new mail without a full get_messages round-trip. It explicitly distinguishes from siblings like get_messages and get_messages_summary, and the specific return fields are enumerated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use ('low-rate polling that wants to know ‘do I have new mail?’'), how to use it (diff total_unread_count against cached value), a wake integration pattern, and explicit alternatives ('use get_messages (mutating) or get_messages_summary (preview)'). This is model guidance for when-not-to-use too.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

post_taskA

Assign a tracked task to a specific agent.

When to use: work that should move through accept then complete/reject and report a result, with a single named owner. Prefer post_task_auto when you do not care which capable agent picks it up. Prefer send_message for free-text comms that do not need a state machine.

Behavior: creates a row with status='posted' and notifies task.posted webhook subscribers. The assignee accepts/completes/rejects via update_task; the assigner can cancel via the same call. Tasks have a heartbeat lease, if the assignee does not update_task action='heartbeat' within RELAY_TASK_LEASE_SECONDS, the health monitor surfaces the task as stuck. Auth: requester token; to must be a registered agent.

Returns: { success: true, task_id, from, to, title, priority, status, note }. status is 'posted' on first creation.

Errors: AUTH_FAILED, PAYLOAD_TOO_LARGE, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
toYesAssigned agent name
fromYesRequester agent name
titleYesShort task title
priorityNoTask prioritynormal
schema_idNov2.10: id of a registered task_schema that the assignee's completion result must conform to (schema-gated completion). Omit for an un-gated task.
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
descriptionYesFull task description (max 64KB by default; see RELAY_MAX_PAYLOAD_BYTES)

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of disclosure, and it delivers well. It explains the created row's initial status ('posted'), the webhook notification (`task.posted`), the heartbeat lease mechanism with `RELAY_TASK_LEASE_SECONDS`, the health monitor surfacing stuck tasks, auth requirements (requester token; `to` must be registered), and the return envelope. This is rich behavioral context, though it omits details like idempotency or whether re-posting to the same agent is allowed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with bolded section labels (When to use, Behavior, Auth, Returns, Errors) making it highly scannable. Every sentence earns its place — lifecycle, webhook, heartbeat, auth, return shape, and errors are each covered in one compact paragraph. No redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter tool with no annotations and no output schema, the description is exceptionally complete. It documents the full behavioral lifecycle (post→accept→complete/reject/cancel), the return envelope shape, error codes, auth model, and adjacent tools. Given the tool's moderate complexity, nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds value by explaining behavioral meaning of parameters beyond schema types: `to` must be a 'registered agent', `priority` maps into the returned status/task, and `schema_id` is contextualized via 'v2.10' note and 'schema-gated completion' semantics. However, most parameter details (max lengths, defaults) remain in the schema, so the incremental semantic addition is moderate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource statement: 'Assign a tracked task to a specific agent.' It clearly distinguishes from `post_task_auto` (auto-pickup) and `send_message` (free-text) by name. The task lifecycle (accept/complete/reject/cancel) is explicitly documented, making the tool's purpose unambiguous against 30+ sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section explicitly states when to use this tool (work needing accept→complete/reject state machine with single owner) and when NOT to use it, naming concrete alternatives: `post_task_auto` for anyone-capable pickup and `send_message` for free-text comms. It also documents the assignee/assigner interaction flow via `update_task`, giving clear usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

post_task_autoA

Auto-route a task to the least-loaded capable agent (v2.0).

When to use: when you know the required capabilities but do not want to hard-code a specific assignee, load balances across the fleet. Prefer post_task when the assignee is intentional. Prefer broadcast for non-tracked notifications.

Behavior: picks the agent with the smallest accepted-task backlog whose capability set is a superset of required_capabilities. Tie-break: freshest last_seen. If no live agent qualifies, the task enters status='queued' and is auto-assigned the first time a capable agent calls register_agent (the assignment is included in that response's auto_assigned). By default the sender is excluded from routing, set allow_self_assign=true to opt in (v2.1).

Returns: { success: true, task_id, status: 'posted' | 'queued', assigned_to: string | null, routed: boolean, candidate_count, required_capabilities, note }. routed=true only when an agent matched at post time; routed=false with status='queued' is the no-match path that auto-resolves on next register.

Errors: AUTH_FAILED, PAYLOAD_TOO_LARGE, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
fromYesRequester agent name
titleYesShort task title
priorityNoTask prioritynormal
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
descriptionYesFull task description (max 64KB by default)
allow_self_assignNov2.1: opt-in flag to let the sender self-assign the task when they match the required capabilities. Default false — sender is excluded from routing so auto-routed work reaches peers.
required_capabilitiesYesCapabilities the assigned agent must have (ALL must match). Also used to auto-assign from the queue when a capable agent registers later.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the burden. It thoroughly discloses the routing algorithm (smallest accepted-task backlog, superset matching, freshest last_seen tie-break), the queued/auto-assign path via register_agent, sender exclusion, and the return contract. It also lists error codes. Minor gap: no mention of rate-limit specifics or auth requirements beyond token resolution.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with labeled sections (When to use, Behavior, Returns, Errors), front-loaded with the purpose. Each sentence earns its place, and the labeled sections make scanning efficient despite its length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Complete for a routing tool with 7 params at 100% schema coverage. The no-output-schema gap is compensated by a detailed return contract, and the queuing/auto-assignment behavior is fully explained. The v2.0/v2.1 versioning is clearly documented.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by clarifying `allow_self_assign` semantics (v2.1 opt-in, sender exclusion rationale) and `required_capabilities` dual role (routing plus queue auto-assignment). It also explains the return-value semantics for `routed`/`queued` paths beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description states specific verb+resource ('auto-route a task to the least-loaded capable agent') with versioning (v2.0). It distinguishes from siblings by naming `post_task` (intentional assignee) and `broadcast` (non-tracked notifications). Clearly differentiates its auto-routing role from the other task tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section states the tool is for when capabilities are known but assignee is not, with load balancing. Excludes `post_task` for intentional assignees and `broadcast` for non-tracked notifications. Also documents the v2.1 `allow_self_assign` opt-in behavior.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

post_to_capabilityA

Route an FYI/coordination message to the current owner(s) of a capability (v2.10 — capability routing, principle #1).

When to use: surface a finding, status, or cross-cutting update to whoever owns a domain WITHOUT knowing their name — e.g. an ad-hoc agent tags a 'relationships' finding and the agent that owns that capability picks it up on its next get_messages. FYI/COORDINATION LANE ONLY: action-required completions (a STAGED build needing an audit, a SHIP that triggers a merge) MUST stay point-to-point completion reports via send_message — that point-to-point reliability is what triggers the orchestrator's next action. A capability-routed message never triggers an action.

Behavior: exact-string matches capability against every registered agent's declared capabilities (same lookup as post_task_auto), then fans the message out — one messages row per owner, stamped with routed_capability so recipients + dashboards distinguish the FYI lane from point-to-point mail. Recipients drain via the normal get_messages (use lane='capability' to read only the FYI lane, lane='direct' for only point-to-point). The sender is excluded by default (exclude_self). No current owner → routed_to:[] and nothing stored (fire-and-forget to current owners, NOT queued-until-owner). Fires one message.capability_routed webhook for the batch. Content encrypted at rest if RELAY_ENCRYPTION_KEY is set; same payload cap as send_message.

Returns: { success: true, capability, routed_to: string[], message_ids: string[], count, note }. routed_to is empty (with an explanatory note) when no agent currently owns the capability.

Errors: AUTH_FAILED, SENDER_NOT_REGISTERED, PAYLOAD_TOO_LARGE, RATE_LIMITED, VALIDATION.

ParametersJSON Schema
NameRequiredDescriptionDefault
fromYesSender agent name
contentYesFYI/coordination content (max 64KB by default; see RELAY_MAX_PAYLOAD_BYTES)
priorityNoMessage prioritynormal
capabilityYesThe single domain/capability tag to route on (exact-match against registered agent capabilities)
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
exclude_selfNoWhen true (default), the sender is excluded from recipients even if it owns the capability.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full responsibility for behavioral disclosure. It does well: explains exact-match lookup behavior, fan-out semantics (one row per owner with routed_capability stamp), fire-and-forget (NOT queued-until-owner, no owner → routed_to:[] and nothing stored), webhook firing, encryption-at-rest conditionality, the exclude_self default behavior, and the distinction from point-to-point triggering. It even discloses the return shape. The only minor gap is no explicit side-effect/airtable-style side effect discussion, but the fire-and-forget disclosure is thorough.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear labeled sections (When to use, Behavior, Returns, Errors) and front-loaded purpose. Despite covering considerable ground, every sentence earns its place — no filler. The section headers make it scannable for an agent parsing behavior quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (6 params, routing semantics, ambiguous lane behavior), the description is remarkably complete. It explains the return contract, error conditions, edge cases (no owner), auth resolution paths, encryption behavior, and how recipients read messages. Since there's no output schema, the explicit Returns section covers that gap well. No significant missing information for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds modest value: it clarifies the lane semantics for reading recipients (lane='capability'), explains exclude_self's default behavior, and references RELAY_ENCRYPTION_KEY/RELAY_MAX_PAYLOAD_BYTES context. However, most parameter meaning is already well-documented in the schema itself, so the description doesn't dramatically elevate beyond baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states what the tool does: routes an FYI/coordination message to the current owner(s) of a capability. It uses a specific verb+resource construction ('Route... to the current owner(s)') and explicitly distinguishes it from send_message (point-to-point) and post_task_auto (task posting), showing clear sibling differentiation within the same messaging family.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives excellent when-to-use guidance: 'surface a finding, status, or cross-cutting update to whoever owns a domain WITHOUT knowing their name.' It explicitly states what should NOT use this tool (action-required completions MUST stay point-to-point via send_message, noting the reliability trigger implication) and names the alternative (send_message). This is exemplary usage guidance with explicit exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

post_to_channelA

Send a message into a channel you have joined.

When to use: ongoing topical coordination among multiple agents ('deploy started', 'triage thread for incident-12'). For 1:1 use send_message; for fleet-wide one-shots use broadcast. The audience is exactly the current channel membership at post time.

Behavior: stores a channel_messages row, fires the channel.message_posted webhook event, and surfaces in every member's get_channel_messages whose joined_at <= post.created_at. Same RELAY_MAX_PAYLOAD_BYTES cap as direct messages. Encrypted at rest when keyring is configured. Auth: caller must be a current member AND hold the channels capability.

Returns: { success: true, message_id, channel_name, from }.

Errors: NOT_MEMBER (caller has not joined the channel), AUTH_FAILED (missing channels cap), PAYLOAD_TOO_LARGE, NOT_FOUND, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
fromYesSender agent name
contentYesChannel message content (max 64KB by default)
priorityNoMessage prioritynormal
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
channel_nameYesChannel to post to

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and does so thoroughly: it discloses the storage side effects (channel_messages row, webhook event, visibility in get_channel_messages with a joined_at filter), payload cap, encryption-at-rest behavior, and detailed auth requirements (current member + channels capability). Also enumerates all error cases.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence earns its place: purpose, when-to-use, behavior, auth, returns, and errors are each one focused line. Front-loaded with the action, and tightly structured with labeled sections. Zero wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite high complexity (5 params, 5 error states, auth requirements, side effects, and no output schema or annotations to fall back on), the description covers everything needed: return shape, error conditions, behavior, and alternatives. Complete and self-sufficient for a mutation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds meaningful value beyond the schema by explaining the RELAY_MAX_PAYLOAD_BYTES cap tied to content, the resolution order for agent_token (explicit > env > header), and the audience semantics tied to channel_name. This elevates it above baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Send a message into a channel you have joined') and clearly distinguishes it from siblings — send_message for 1:1, broadcast for fleet-wide one-shots. It precisely scopes the audience to current channel membership at post time.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use ('ongoing topical coordination among multiple agents') and names two concrete alternatives (send_message for 1:1, broadcast for fleet-wide one-shots), plus clarifies audience scope. This is a textbook example of usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_agentA

Register this terminal as a named agent so other agents can address it.

When to use: call this first thing in any session that needs to send/receive messages, post tasks, or join channels. Idempotent upsert, safe to call again on reconnect. The SessionStart hook (hooks/check-relay.sh) typically calls it for you.

Behavior: creates or updates the agent row keyed by name. First registration mints a fresh agent_token (returned ONCE, store it in RELAY_AGENT_TOKEN). Re-registering preserves the existing token unless recovery_token is presented (v2.1 Phase 4b.1 v2 recovery flow). Capabilities are immutable on re-register (v1.7.1), use expand_capabilities for additive changes.

Returns: { success: true, agent: AgentWithStatus, protocol_version, message }. First-time registration also includes agent_token (shown ONCE — store in RELAY_AGENT_TOKEN) and auth_note. If the request asked for capabilities that differ from the stored set, capabilities_note explains the immutability. If queued auto-routed tasks were assigned at register time, auto_assigned_tasks: { task_id, title, priority }[] lists them. Successful recovery flow includes recovery_completed: true.

Errors: AUTH_FAILED (recovery_pending row presented without recovery_token), RECOVERY_REQUIRED (token rejected, present recovery_token), INVALID_INPUT (name/role/capabilities malformed), RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesHuman-readable agent name — [A-Za-z0-9_.-]{1,64} (ASCII only)
roleYesAgent role (e.g. orchestrator, builder, ops)
classNoADR-0002 (v2.21.0): the agent's COARSE coordination posture — one of orchestrator | builder | advisory | auditor | transient (SSOT: src/agent-class.ts). Self-declared; IMMUTABLE after first registration (same rule as managed/host_id). Orthogonal to `role` (free-text label) and `capabilities` (what it does). Omit → `unclassified`; surfaced in discover_agents view='topology'.
forceNoADR-0012: request a CAS TAKEOVER of an actively-held name (NOT an unconditional bypass). MUST be accompanied by expected_session_id (the session_id you READ from the row; null = expect an offline row). The takeover is an atomic compare-and-swap on that session_id — exactly one of two racing relaunches wins; the loser gets FORCE_PRECONDITION_FAILED and re-reads (never retry-force). Default false rejects re-registration on an actively-held name with NAME_COLLISION_ACTIVE. Set true only when the prior session is unreachable (crashed/relaunched terminal); a live concurrent terminal still wins the CAS and you correctly lose.
host_idNoTether v0.3 PID-handshake (schema v16): stable OS machine GUID (macOS IOPlatformUUID / Linux /etc/machine-id / Windows MachineGuid). Host-scopes the PID match so equal PIDs on different hosts never false-match (federation-safe). Immutable after first registration (same rule as `managed`).
managedNov2.1 Phase 4b.2: true = agent is a Managed Agent wrapper that can parse push-token messages + self-update its local config on rotation. false (default) = Claude Code terminal or equivalent (restart-required on rotation). Immutable after first registration — change requires unregister + fresh register.
agent_pidNov2.13.0 presence liveness: the agent's OWN process id (this CLI/wrapper's PID). Lets the relay confirm the agent is alive-and-idle via a same-host probe instead of misreading idle silence as closed. Self-reported by managed/script agents; stdio MCP agents have it captured automatically by the relay stdio server. Host-scoped by host_id; cleared on close. Omit if unknown → age-based presence (unchanged).
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
cli_profileNoWhich agent-CLI this session runs under (e.g. "claude", "codex"). Set by the SessionStart hook; VALIDATED against the agent-CLI profile registry and stored as NULL when unrecognised — never defaulted, because a wrong default would make the verdict-absence check fire on healthy agents. Enables the server to know whether this agent OWES a session-start verdict.
descriptionNov2.0: optional human-readable description (max 512 chars). Shown in discover_agents + dashboard. Mutable on re-register — if omitted, previous value preserved.
capabilitiesYesList of capabilities
recovery_tokenNov2.1 Phase 4b.1 v2: required when re-registering an agent whose auth_state is 'recovery_pending'. Obtained from the revoker's revoke_token response (shown ONCE) and handed off to the operator out-of-band.
agent_pid_startNov2.13.0 presence liveness: an opaque start-time token for agent_pid (e.g. the process start clock time) used as a PID-reuse guard — a recycled PID with a different start-time reads dead. Optional; omit if unavailable.
host_shell_pidsNoTether v0.3 PID-handshake (schema v16): the agent's process-ancestry PID chain (own PID → … → init/launchd). Tether reads vscode.Terminal.processId for each terminal and intersects with this chain (host-scoped by host_id) to bind a terminal to this agent — no manual naming. Mutable on re-register: OVERWRITES the stored chain (replaces, not appends). Writing it under an existing name requires that name's token (same auth as any re-register).
terminal_title_refNov2.2.0: window title the agent's terminal was spawned with. Used by the dashboard's click-to-focus driver. Typically equals the agent's `--name`. Mutable on re-register (updates to reflect the current session's title).
expected_session_idNoADR-0012 CAS precondition for a force takeover. REQUIRED whenever force=true: the session_id the caller READ from the row it intends to take over (pass null to mean "I expect an OFFLINE row" → CAS matches session_id IS NULL). The re-register lands ONLY if the row's session_id still equals this value, so exactly one of two racing relaunches wins; the loser is rejected with FORCE_PRECONDITION_FAILED and MUST re-read (never retry-force, never come up mute). force=true WITHOUT this field is rejected as malformed — there is NO unconditional-force bypass, which would reopen the lost-update TOCTOU ADR-0012 eliminates.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With NO annotations provided, the description carries the full burden — and it excels. It discloses idempotency, token issuance (minted ONCE, stored in RELAY_AGENT_TOKEN), token preservation on re-register, capability immutability, recovery_token flow, CAS takeover semantics (force/expected_session_id), and specific error codes. Exceptionally thorough behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (When to use, Behavior, Returns, Errors) and front-loads the core purpose. It is somewhat long, but every paragraph earns its place — each covers a distinct behavioral aspect (idempotency, token lifecycle, return fields, error taxonomy). Arguably dense but not padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 16-parameter, high-complexity tool with no output schema and no annotations, the description is remarkably complete. It documents return shape (agent, protocol_version, agent_token, auth_note, capabilities_note, auto_assigned_tasks, recovery_completed), error taxonomy with all four codes, and usage guidance. The agents' expected behavior is fully specified despite the enormous schema surface.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds context about key parameters (name, capabilities immutability, token) but the schema already documents each of the 16 parameters richly with version tags, ADR references, and mutation rules. The description does not need to add much beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific, action-oriented statement: "Register this terminal as a named agent so other agents can address it." This clearly states the verb (register), resource (terminal/agent), and purpose (addressability by other agents). It distinguishes from siblings like unregister_agent, abandon_registration, and expand_capabilities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit "When to use" section says to call first in any session needing to send/receive messages, post tasks, or join channels. It notes idempotency (safe on reconnect), mentions the SessionStart hook that typically handles it, and implicitly distinguishes re-registration (register_agent) from expansion (expand_capabilities).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_task_schemaA

Register a reusable, immutable JSON Schema that gates task completion (v2.10 — safety).

When to use: define the PROOF shape a completing agent must satisfy — e.g. a completion report requiring {ci_status:'green', tests_passed, summary}. A requester attaches the schema id to a task via post_task's schema_id; the assignee's update_task(action='complete', result=...) is then validated against it. Built-ins ship_pong_v1 / audit_verdict_v1 / merge_ready_v1 are auto-registered on init.

Behavior: the document is meta-validated + hardened (no $ref/$dynamicRef/$recursiveRef/$data) BEFORE ajv compiles it (a registered schema is compiled, so it is an attack surface). Schemas are IMMUTABLE — re-registering an id is refused; bump the version id. Auth: requires the manage_schemas capability.

Returns: { success: true, id, created_by, created_at, note }.

Errors: SCHEMA_MISMATCH (invalid/forbidden schema document), ALREADY_EXISTS (id already registered), CAP_DENIED, AUTH_FAILED, VALIDATION.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesSchema id, name-as-version (e.g. 'ship_pong_v1'). Immutable once registered.
agent_nameYesRegistering agent name
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
json_schemaYesThe JSON Schema document (object). Meta-validated + hardened before compile; $ref/$dynamicRef/$recursiveRef/$data are rejected.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations were provided, so the description carries the full burden. It discloses immutability (re-registering an id is refused), hardening behaviors ($ref/$dynamicRef/$recursiveRef/$data rejected, meta-validation before ajv compile), auth requirement, and explains the compile-time attack surface rationale. It doesn't explicitly state whether this is read-only vs mutating, but 'register' + immutability context makes the write nature clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear section headers (When to use, Behavior, Returns, Errors). Every section earns its place, and errors are enumerated. The version tag 'v2.10 — safety' adds perspective on the tool's maturity context. No fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description compensates by listing the exact return shape ({ success, id, created_by, created_at, note }) and all error codes. For a 4-param tool with 100% schema coverage and nested objects, this is thorough. Could mention pagination or the max number of schemas, but that's a minor omission given all else.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by clarifying that json_schema rejects $ref-family keywords and is meta-validated/hardened, and that agent_token is optional (resolvable from env or header). Minor gap: no guidance on name uniqueness semantics beyond what's in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource: 'Register a reusable, immutable JSON Schema that gates task completion (v2.10 — safety).' It clearly distinguishes from siblings like task_schema_get (retrieval) and post_task (which just attaches a schema). The domain use case is explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Has an explicit 'When to use' section with a concrete example, explains the lifecycle (requester attaches via post_task's schema_id, assignee's update_task validates), and lists built-in schemas that are auto-registered, helping the agent avoid redundant registration. Auth requirement (manage_schemas) is stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_webhookA

Subscribe an HTTP endpoint to relay events.

When to use: reactive integrations, Slack notifier, audit pipeline, dashboard refresher. For polling-style observation prefer get_standup or peek_inbox_version. For local UIs the bundled /dashboard already consumes the live event stream.

Behavior: stores the subscription + optional HMAC secret (encrypted at rest with the same keyring the message body uses, v2.1 Phase 4p). Each delivery POSTs the event JSON with X-Relay-Delivery-ID + X-Relay-Idempotency-Key headers and an X-Relay-Signature HMAC-SHA256 if a secret was registered. Outbound URLs are SSRF-validated against the cloud-metadata + private-IP blocklist (v1.10). Events: message.sent | message.broadcast | task.posted | task.accepted | task.completed | task.rejected | channel.message_posted | agent.unregistered | agent.spawned | '*'. Optional filter (agent name) narrows by sender/recipient.

Returns: { success: true, webhook_id, url, event, filter, has_secret: boolean, resolved_ips: string[], note }.

Errors: AUTH_FAILED (caller needs webhooks capability), URL_BLOCKED (SSRF target), INVALID_INPUT, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesHTTP(S) URL to POST events to
eventYesEvent to subscribe to, or '*' for all events
filterNoOptional agent name filter (only fire if from_agent or to_agent matches)
secretNoOptional secret for HMAC signature (sent in X-Relay-Signature header)
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With zero annotations provided, the description carries full burden and succeeds: it discloses encryption-at-rest of the HMAC secret with keyring details, the delivery headers (X-Relay-Delivery-ID, X-Relay-Idempotency-Key, X-Relay-Signature), SSRF validation against cloud-metadata/private-IP blocklist, the full event list, the return envelope shape, and all error codes with causes. This is exemplary behavioral disclosure for a tool with no annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with labeled sub-sections (When to use, Behavior, Events, Returns, Errors) that front-load the purpose and usage before deep behavioral detail. Every sentence earns its place, though the length is substantial — justified given the tool's complexity and zero annotations, but slightly more than strictly minimal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-param, 2-required tool with no output schema and no annotations, the description is remarkably complete: it covers the output envelope, all error modes, security behaviors, event taxonomy, idempotency headers, and alternative tools. There are no significant information gaps an agent would need to guess at.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so parameters are documented in schema. The description adds meaningful context beyond the schema: the filter's mechanism ('narrows by sender/recipient') amplifies the schema's agent-name description, and the 'optional * wildcard' behavior is integrated. The secret's return-field interplay (has_secret) is disclosed. Minor credit withheld only because most param semantics already live in the schema, which the description appropriately acknowledges.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description states a specific verb+resource ('Subscribe an HTTP endpoint to relay events') that clearly differentiates from siblings like list_webhooks and delete_webhook, and identifies the subscribed event types. It enumerates the full event taxonomy and filter capability, leaving no ambiguity about what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section names reactive integration scenarios (Slack notifier, audit pipeline) and contrasts against alternatives: 'For polling-style observation prefer get_standup or peek_inbox_version' and notes /dashboard already consumes live events. This gives clear when-to and when-not-to guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

report_livenessA

Restamp your own liveness anchor (agent_pid + process start-time) — a narrow, metadata-only presence self-report.

When to use: your hooks call this automatically (SessionStart + PostToolUse) so the relay can positively probe whether your process is alive. An old/existing session that registered before the anchor mechanism becomes probe-able without a full re-register — critical because register_agent rotates your session_id and can re-surface already-read mail, whereas this touches ONLY agent_pid + start-time (+ fills host_id when unset). You rarely call it by hand.

Behavior: updates agent_pid + agent_pid_start (and host_id if NULL) for your row. Does NOT rotate session_id, bump last_seen, or touch your read cursor. Idempotent — restamping the same values is a no-op. Auth: own agent token only.

Returns: { success: true, agent_name, agent_pid, note }.

Errors: NOT_FOUND (unknown agent_name), AUTH_FAILED, INVALID_INPUT, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_pidYesThe agent CLI's own OS process id (from the hook's ancestry walk)
agent_nameYesYour agent name (must match your token)
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
agent_pid_startNoThe process start-time token (LC_ALL=C `ps -o lstart=`), or null if unreadable — PID-liveness only in that case

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations were provided, so the description carries the full burden. It thoroughly discloses behavior: updates agent_pid + agent_pid_start and fills host_id when NULL, does NOT rotate session_id, does not bump last_seen or touch read cursor, is idempotent, and requires own agent token only. Returns and errors are documented.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-organized with labeled sections (When to use, Behavior, Returns, Errors) and is front-loaded with the core purpose. It could be mildly trimmed—the Returns/Errors sections are partly visible in schema contexts—but each sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple metadata-only self-report with 100% schema coverage and no output schema, the description is remarkably complete. It covers when to use, behavior, idempotency, auth requirements, return shape, and error cases—more than sufficient for correct invocation and expectations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 4 parameters thoroughly. The description adds context about liveness semantics (restamping same values is a no-op, PID-liveness only when start-time is null) which enriches the parameter meaning beyond what schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb+resource ('Restamp your own liveness anchor — agent_pid + process start-time') with a narrow, well-scoped metadata-only self-report. It clearly distinguishes this from siblings like register_agent by explicitly noting it rotates session_id while this does not.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section explains that hooks call this automatically, and contrasts it directly with register_agent which rotates session_id and can re-surface already-read mail. Also states 'You rarely call it by hand', giving clear when-to/not-to guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resolve_messagesA

Permanently resolve (ack) specific messages so they leave your pending queue for good (v2.12.0).

When to use: PARTIAL handling — you've actioned some of your mail but not all ("I did these, not those"). For the common "I've handled everything I just drained" path, prefer get_messages(status='pending', ack=true) which drains AND resolves in one call. Resolving is the durable, session-INDEPENDENT counterpart to reading: read is a per-session observation (a fresh terminal re-sees prior-session-read mail so handovers don't drop unfinished work); resolved is a permanent "handled, archive it" that the pending filter honors, so an already-handled message never re-floods a new session.

Behavior: sets resolved_at=now() for the given ids WHERE to_agent is you AND not already resolved, in one transaction. Does NOT mark messages read (orthogonal plane) and does NOT delete them — they remain in status='all'/'history'/'resolved'. Idempotent: re-resolving, unknown ids, or ids addressed to another agent are silently skipped (reflected in the returned counts). Recipient-scoped: you can only resolve your OWN mail (the dispatcher binds your token to agent_name; the DB also filters by to_agent).

Returns: { success: true, agent, resolved_ids: string[], resolved_count, requested_count, note }. resolved_count < requested_count when some ids were already resolved, unknown, or not yours.

Errors: AUTH_FAILED (token missing/mismatched), VALIDATION (empty/oversized id list), RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_nameYesYour agent name (the recipient; only your own mail can be resolved)
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
message_idsYesThe message ids to permanently resolve (ack). Only ids addressed to you are affected; unknown/foreign ids are silently skipped.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations were provided, so the description carries full disclosure burden — and it delivers thoroughly. It specifies the exact DB operation (sets resolved_at=now() WHERE to_agent is you AND not already resolved, one transaction), side effects (does NOT mark read, does NOT delete), idempotency (silent skip semantics), recipient scoping (token bound to agent_name, DB filters by to_agent), and the full error set (AUTH_FAILED, VALIDATION, RATE_LIMITED).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is organized with clear labeled sections (When to use, Behavior, Returns, Errors) and is front-loaded with the core purpose. It's prose-dense and longer than the TDQS 4.3 exemplar, but every sentence earns its place given the semantic weight of read-vs-resolve-vs-ack. Slight verbosity around the read/resolved contrast, but it's genuinely informative.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a mutating, permission-scoped tool with no output schema and no annotations — the highest-risk category. The description covers the transactional atomicity, idempotency, recipient scoping, return-shape semantics (including the count mismatch meaning), and error handling. Nothing material is left ambiguous for an agent to safely invoke this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds meaningful semantics beyond the schema: it explains the resolved_count < requested_count asymmetry, the idempotent skip behavior for unknown/foreign ids, and the auth-fallback chain for agent_token (token param vs env var vs header). This elevates it above baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb (resolve/ack), the resource (specific messages), and the durable outcome ('leave your pending queue for good'). It also specifies a version (v2.12.0). It powerfully distinguishes from siblings by contrasting with read (session-INDEPENDENT vs per-session) and with get_messages (one-call drain vs partial).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use: PARTIAL handling' section gives precise decision context. It names the alternative get_messages(status='pending', ack=true) for the 'handled everything' path, and explains the read vs resolved duality so an agent knows which tool fits which scenario. This is model decision guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

revoke_tokenA

Invalidate another agent's token (v2.1 Phase 4b.1 v2).

When to use: confirmed compromise, lost device, or graceful retirement of an agent name. For routine key-hygiene rotation prefer rotate_token / rotate_token_admin (those keep identity). For removing the agent entirely use unregister_agent.

Behavior: transitions the target row to auth_state='recovery_pending' (when issue_recovery=true, also returns a one-time recovery_token the operator hands off out-of-band; the agent re-registers via register_agent with that token to mint a fresh agent_token) or auth_state='revoked' (terminal, only unregister_agent + register_agent can reuse the name). Original token_hash is preserved for forensic correlation; the state column, not the hash, enforces rejection. v2.6.2 R1: the per-instance vault file at <instanceDir>/agents/<name>.token is also scrubbed on every successful revoke (best-effort, ENOENT-safe) — the security boundary already held via the state check, but the scrub aligns the mental model so revoke_token leaves no credential on disk. Requires revoke_others capability.

Returns: { success: true, revoked: target_agent_name, revoked_by, revoked_at: ISO, changed: boolean, auth_state_before, auth_state_after, note }. When issue_recovery=true and the call actually changed state, also includes recovery_token (shown ONCE), recovery_note, and recovery_reissued: boolean. changed=false is an idempotent no-op (target was already revoked).

Errors: AUTH_FAILED (caller missing revoke_others), NOT_FOUND, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
revoker_nameYesName of the agent performing the revoke — must hold the 'admin' capability.
issue_recoveryNov2.1 Phase 4b.1 v2: if true (default), issue a one-time recovery_token returned on this response (shown ONCE). Target can re-register with that token to resume as 'active'. If false, revocation is terminal — operator must unregister_agent + register_agent to recreate the row.
target_agent_nameYesName of the agent whose token to revoke.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavior: state transitions, token_hash preservation, vault file scrubbing, capability requirements, idempotent no-op, and error codes. This goes far beyond what annotations would typically provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Organized into labeled sections (when to use, behavior, returns, errors) with every sentence providing necessary detail. The length is justified by the tool's security-critical nature and complex semantics.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers prerequisites, alternatives, return data, errors, and side effects. Since there is no output schema, the detailed 'Returns' section is essential and fully provided.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already describes all parameters with 100% coverage. The description reinforces the issue_recovery flag's effect but does not add significant new parameter-level meaning beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line 'Invalidate another agent's token' clearly names the action and object. It explicitly contrasts with rotate_token/rotate_token_admin and unregister_agent, distinguishing it from sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use scenarios ('confirmed compromise, lost device, or graceful retirement') and directs users to alternative tools for different use cases, making the choice unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rotate_tokenA

Self-rotate your own agent_token (v2.1).

When to use: scheduled rotation, suspected token leak, or any time you want a fresh secret without losing identity. For admin-driven rotation of someone else's token use rotate_token_admin. To wipe the token entirely use revoke_token.

Behavior: requires the current valid token. For Managed agents (registered with managed:true) the relay enters a grace window during which BOTH old and new tokens authenticate, and a priority='high' push-message carries the new token to the agent so it can self-update. For unmanaged agents (default, Claude Code terminals), the response carries restart_required:true and the old token is invalid immediately.

Returns: { success: true, agent_name, new_token, rotated_at: ISO, agent_class: 'managed' | 'unmanaged' }. Managed-with-grace adds grace_expires_at: ISO, push_sent: boolean, auth_note. Managed-with-zero-grace adds grace_expires_at: null, push_sent: false, auth_note. Unmanaged adds restart_required: true, operator_note.

Errors: NOT_FOUND (unknown agent), INVALID_STATE (auth_state ≠ active — recovery_pending / revoked / legacy_bootstrap / rotation_grace each return a state-specific hint), CONCURRENT_UPDATE (CAS race lost), INTERNAL.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_nameYesAgent name — must match the token's owner
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
grace_secondsNov2.1 Phase 4b.2: override the grace window length for managed agents. Clamped to [0, 3600]. 0 forces hard-cut (immediate invalidation) even for managed agents. Unmanaged agents ignore this field (no grace applicable). Default: RELAY_ROTATION_GRACE_SECONDS env var (fallback 900).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so the description carries full burden and delivers richly. Discloses prerequisite (current valid token), grace-window mechanics for managed agents (both tokens valid, push-message carries new token), immediate invalidation for unmanaged, and state-specific behavior. Documents exact return shapes for each agent class.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-organized with clear paragraphs for when-to-use, behavior, returns, and errors. Front-loaded with the one-line purpose. Slightly lengthy but every section adds distinct information; the detail about return variants is justified for a tool with nuanced behavior.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description must document return values, and it does so exhaustively for all three agent-class variants. Also documents the error codes with state-specific handling. For a tool with this behavioral complexity (3 agent classes, grace windows, CAS races), the description is remarkably complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by explaining grace_seconds' clamp behavior relevance and that unmanaged agents ignore it, plus alternative token resolution routes (env var, header) beyond the schema note. Slight deduction for not elaborating on agent_name semantics beyond what the schema states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear verb+resource+scope: 'Self-rotate your own agent_token (v2.1)'. Explicitly contrasts with siblings: distinguishes from rotate_token_admin (admin-driven rotation of others) and revoke_token (wiping entirely). Strong differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly lists when to use (scheduled rotation, suspected leak, fresh secret without losing identity) and names specific alternatives with precise conditions (rotate_token_admin for admin-driven, revoke_token for wiping). Also details managed vs unmanaged behavior context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rotate_token_adminA

Admin-initiated rotation of another agent's token (v2.1 Phase 4b.2).

When to use: operator-driven incident response, scheduled rotation across the fleet, or onboarding a Managed agent into a new key generation. Requires rotate_others capability on the rotator. For self-service use rotate_token. For revocation without re-issuance use revoke_token.

Behavior: same Managed-vs-unmanaged split as rotate_token. Managed targets get the new token via push-message + a grace window. Unmanaged targets return the new token in the rotator's response (the rotator delivers it out-of-band) and the response carries restart_required:true. The audit log records BOTH the rotator and the target so attribution survives.

Returns: { success: true, target_agent_name, rotator, rotated_at: ISO, agent_class: 'managed' | 'unmanaged' }. Managed-with-grace adds grace_expires_at: ISO, push_sent: boolean, note. Managed-with-zero-grace adds new_token, grace_expires_at: null, push_sent: false, note. Unmanaged adds new_token, restart_required: true, operator_note.

Errors: AUTH_FAILED (rotator not authenticated, or missing rotate_others), NOT_FOUND (unknown target), INVALID_STATE, CONCURRENT_UPDATE, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
rotator_nameYesName of the admin-capable agent performing the rotation. Must hold 'rotate_others' capability.
grace_secondsNov2.1 Phase 4b.2: override the grace window length for managed targets. Clamped to [0, 3600]. 0 forces hard-cut. Unmanaged targets ignore this field. Default: RELAY_ROTATION_GRACE_SECONDS env var (fallback 900).
target_agent_nameYesName of the agent whose token to rotate. Must differ from rotator_name — self-rotation uses rotate_token.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden and delivers rich detail: explains the Managed-vs-unmanaged behavioral split, describes token delivery mechanisms (push-message + grace window vs return-in-response), notes `restart_required:true` for unmanaged, and discloses that audit logs record BOTH rotator and target for attribution. It also enumerates full error taxonomy. Only minor gap: doesn't disclose whether operation is destructive/irreversible in explicit terms, though hard-cut grace=0 implies it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear semantic sections (When to use, Behavior, Returns, Errors) using bolded labels. Each sentence earns its place, front-loaded with the core action. The error taxonomy and return shapes are compactly enumerated without fluff. Zero filler words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-parameter, no-output-schema, no-annotation tool, the description is thoroughly complete: covers purpose, when/how to use, behavioral nuances, exact return shapes for each branch (managed-with-grace, managed-with-zero-grace, unmanaged), and all error conditions. Nothing meaningful is left unspecified for the agent to guess.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by explaining the distinction between rotator and target (self-rotation uses rotate_token), clarifying grace_seconds semantics (clamped, forced hard-cut at 0, ignored by unmanaged), and noting agent_token can come from multiple sources. It adds meaning beyond the field descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Admin-initiated rotation of another agent's token') with a specific verb and resource, and names the phase (v2.1 Phase 4b.2). It distinguishes from siblings by explicitly naming rotate_token (self-service) and revoke_token (revocation), and differentiating against the target_agent_name schema constraint.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use scenarios ('operator-driven incident response, scheduled rotation across the fleet, or onboarding a Managed agent'), states the prerequisite capability (`rotate_others`), and names explicit alternatives for different cases (rotate_token for self-service, revoke_token for revocation without re-issuance). This is model-level guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_messageA

Send a text message addressed to a single agent.

When to use: 1:1 communication, dispatching work, relaying a status update, asking a peer a question. Prefer broadcast for fan-out to many agents, post_to_channel for topical group coordination, and post_task (or post_task_auto) when the recipient should track state-machine progress (accept/complete/reject) rather than a free-text message.

Behavior: stores the message with status='pending' and notifies any matching webhook subscribers (message.sent event). The recipient sees it on its next get_messages call (which auto-marks read unless peek=true). Content is encrypted at rest if RELAY_ENCRYPTION_KEY is set. Caps at RELAY_MAX_PAYLOAD_BYTES (default 64 KB). Auth: sender must present a valid token whose row matches from.

Returns: { success: true, message_id, from, to, priority, note }. The new message is stored with status='pending' until the recipient drains it.

Errors: AUTH_FAILED (token missing/mismatched), SENDER_NOT_REGISTERED (caller's agent row absent), PAYLOAD_TOO_LARGE, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
toYesRecipient agent name
fromYesSender agent name
contentNoMessage content (max 64KB by default; see RELAY_MAX_PAYLOAD_BYTES). Alias: `message`.
messageNoAlias for `content` (parity with the REST /api/send-message endpoint + the agent-team SendMessage tool).
deadlineNoADR-0011: optional ISO8601 deadline for an 'obligation'. Overdue is reported strictly past this instant; omit to use the tunable default bound (RELAY_OVERDUE_SECONDS). Ignored for log/ask.
priorityNoMessage prioritynormal
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
dispositionNoADR-0011 disposition: 'log' (default, FYI, never overdue) | 'ask' (expects a reply/resolve) | 'obligation' (an action owed; pair with optional `deadline`). Overdue is report-only — query it via get_outstanding.log

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavior: stores message with 'status=pending', notifies webhook subscribers, recipient sees it on next get_messages, auto-marks read unless peek=true, encryption at rest, payload caps, and auth requirements. It also lists probable errors, making the tool's behavior transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections: 'When to use', 'Behavior', 'Returns', 'Errors'. It is concise yet comprehensive, with every sentence providing value. No redundancy or wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 8 parameters, no output schema, and no annotations, the description is remarkably complete. It explains the return format, error cases, storage behavior, encryption, and payload limits, giving an agent all necessary context to invoke it correctly. Alternatives and usage scenarios are also covered.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so parameters are already documented in the schema. The description does not add significant new semantics beyond the schema; it mentions the `content`/`message` alias and the disposition field, but these are already in the schema descriptions. Thus the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Send a text message addressed to a single agent,' using a specific verb and resource. It then distinguishes from siblings by explicitly naming `broadcast`, `post_to_channel`, and `post_task` as alternatives for different scenarios.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section provides explicit guidance: 1:1 communication, dispatching work, relaying updates, asking questions. It also gives clear exclusions and alternatives, such as 'Prefer `broadcast` for fan-out to many agents, `post_to_channel` for topical group coordination, and `post_task` when the recipient should track state-machine progress.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_dashboard_themeA

Set the server-side default dashboard theme (v2.2.1).

When to use: org-level theme defaults ('every new operator should land on dark'), or programmatically applying a brand palette via mode='custom'. Each individual operator's localStorage preference still beats this default for repeat visits, this only affects first-visit theming for newly-connecting clients.

Behavior: stores the chosen theme + optional custom_json in dashboard_prefs. Modes: 'catppuccin' (default Mocha palette), 'dark' (tool-neutral), 'light' (tool-neutral), 'custom' (requires custom_json with all 13 CSS color tokens). No WebSocket push, already-open dashboards adopt on full reload. Auth: dashboard-secret-equivalent capability (treated as an admin operation).

Returns: { success: true, theme, updated_at: ISO, note }.

Errors: AUTH_FAILED, INVALID_INPUT (custom mode missing required tokens), RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeYesTheme mode. catppuccin is the default; dark/light are tool-neutral; custom requires custom_json.
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
custom_jsonNoRequired when mode='custom'. JSON object with all 13 CSS-token fields (bg, panel, panel-2, border, text, muted, accent, online, stale, offline, critical, high, normal, low).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full disclosure burden and delivers richly: it states no WebSocket push exists (already-open dashboards need full reload), describes auth requirements as admin-level capability, enumerates all three error codes, and details what gets stored in dashboard_prefs. This far exceeds the minimal burden.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is thorough and well-organized with clear labeled sections (When to use, Behavior, Returns, Errors), which aids agent scanning. It's somewhat longer than the TDQS high examples but every sentence earns its place given the behavioral complexity of custom mode and the absence of annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 3 params (one nested object), no annotations, and no output schema, the description covers all bases: purpose, when/when-not, behavior, storage location, auth, error taxonomy, and return shape. Fully complete for an agent to select and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real value: it clarifies that catppuccin is the default, names the Mocha palette, and specifies that custom mode requires all 13 CSS tokens. It adds contextual meaning about mode semantics that the schema enum alone doesn't convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it sets the server-side default dashboard theme, with a specific verb+resource. It distinguishes this tool from siblings by explicitly targeting org-level theme defaults and first-visit theming, which none of the sibling tools (which are messaging, task, and agent-management focused) cover.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section explicitly names the two scenarios (org-level defaults, brand palette via custom mode), and even explains the exclusion — that individual operator localStorage preferences override this default for repeat visits. This is explicit usage guidance with clear context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_statusA

Declare your operational state independently of presence.

When to use: tell the relay what kind of work you are in, so the health monitor and orchestrators can route or skip accordingly. Distinct from last_seen-derived presence (online/stale/offline), that one is computed; this one is your declared intent. For one-call team rollup use get_standup.

Behavior: updates the agent row's agent_status (idle | working | blocked | waiting_user | offline). v2.1.3 (I6) widened the enum from the original online/busy/away/offline. busy and away map to working for backward compatibility. The health monitor exempts working/blocked/waiting_user rows from automatic task reassignment. Auth: own agent token only.

Returns: { success: true, agent, status, note, status_normalized_from? }. status_normalized_from is set when the input alias (e.g., online/busy/away) was rewritten to the canonical enum value (idle/working).

Errors: NOT_FOUND (unknown agent_name), AUTH_FAILED, INVALID_INPUT, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusYesOperational status — v2.1.3 widened enum: idle (default active state), working (actively executing a task; exempts from health-monitor reassignment), blocked (cannot proceed; also exempt), waiting_user (paused pending operator input), offline (graceful shutdown). Legacy aliases still accepted: online→idle, busy→working, away→blocked. `stale` is relay-computed, not agent-settable.
agent_nameYesYour agent name
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Rich behavioral detail: health-monitor reassignment exemption, enum aliasing (busy/away→working), auth requirements (own token only), and error codes. However, no annotations are provided, so the description carries the full burden; it does disclose mutation, auth, and enum-widening. The mapping details add significant value beyond just 'declares status'.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with labeled sections (When to use, Behavior, Returns, Errors) that front-load the critical decision context. Slightly verbose in places but every sentence earns its place with concrete operational details like the enum mapping and version reference.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Complete for a state-setting tool: describes behavior, health-monitor implications, return shape, error codes, and auth requirements. Given no output schema, the Returns section compensates by documenting the response shape. No annotations require additional disclosure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the description adds substantial semantic value: it explains the legacy alias mapping (online→idle, busy→working, away→blocked), notes which statuses exempt from reassignment, and clarifies which values are relay-computed vs agent-settable. Also explains the optional agent_token resolution chain via env/header.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Declares operational state distinct from presence, with explicit verb (declare, set) and resource (agent_status). Explicitly differentiates from last_seen-derived presence and identifies sibling get_standup for rollup use. Purpose is specific and clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section states the context (tell the relay what kind of work you're in for health monitor/orchestrator routing) and explicitly distinguishes from last_seen-derived presence and names the alternative get_standup. Provides clear when-use and when-not-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

spawn_agentA

Open a new Claude Code terminal pre-configured as a relay agent (macOS only).

When to use: orchestrators delegating work to a fresh sub-agent. The new terminal arrives in a known role + capability set with RELAY_AGENT_NAME/ROLE/CAPABILITIES already in env, and the SessionStart hook auto-registers it before the LLM's first turn. Linux/Windows drivers exist for headless smoke tests but do not open a UI window.

Behavior: pre-registers the new agent server-side (so its token is minted before the child process starts and reaches it via env), opens an iTerm2 or Terminal.app window via AppleScript, and runs the configured shell command in that window. Optional initial_message is queued in the new agent's mailbox and surfaces on its first get_messages. brief_file_path (v2.1.4) threads a durable on-disk task brief into the KICKSTART prompt, preferred over initial_message for non-trivial scopes because file-on-disk does not read as prompt-injection the way an inbox message can.

Returns: { success: true, name, role, capabilities, platform, driver, agent_token, auth_note, has_initial_message, brief_file_path, note }. agent_token is shown ONCE — already written to the per-instance file vault at <instanceDir>/agents/<name>.token (v2.6.1). The spawned terminal's launcher reads the vault before exec'ing claude (macOS / Linux), and the stdio MCP server transport's resolveToken falls back to RELAY_AGENT_TOKEN env or the per-instance vault when RELAY_AGENT_NAME is set, so the child authenticates from its first tool call regardless of platform. HTTP clients must always present an explicit token via args.agent_token or X-Agent-Token header — the daemon's own env (RELAY_AGENT_TOKEN, RELAY_AGENT_NAME) and the per-instance vault are stdio-only credentials and are never used to authenticate HTTP callers (R3 transport gate).

Errors: SPAWN_NOT_SUPPORTED (non-macOS host without an explicit driver), AUTH_FAILED, INVALID_INPUT, RATE_LIMITED.

ParametersJSON Schema
NameRequiredDescriptionDefault
cliNoWhich agent CLI to launch for the new terminal: a registered agent-CLI profile id — 'claude' (default) or 'codex'. Codex launches via bin/codex-relay (POSIX; macOS + Linux). See `relay cli-profiles`.claude
cwdNoAbsolute path working directory for the new terminal. Defaults to user's home.
nameYesName for the new agent — [A-Za-z0-9_.-]+, max 64 chars
roleYesRole of the new agent — [A-Za-z0-9_.-]+, max 64 chars
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.
capabilitiesNoCapabilities of the new agent
brief_file_pathNov2.1.4 (I10): absolute path to a task-brief file the spawned agent should read FIRST. The relay validates that the file exists at spawn time, is readable, and is <=10KB. When set, the default KICKSTART prompt appends a sentence telling the agent to read this file as the canonical source for its task scope — trust-anchored fix for respawned-agent context loss (inbox messages are not durable). macOS only for v2.1.4; Linux/Windows drivers ignore (no KICKSTART on those platforms yet).
initial_messageNoOptional message to queue for the new agent before it spawns. It will see this on session start.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It thoroughly discloses: pre-registration server-side, token minting timing and delivery via env/vault, AppleScript UI opening, return payload structure, the one-time visibility of agent_token, the stdio-vs-HTTP credential gate (R3 transport gate), and specific error codes. This is comprehensive behavioral disclosure for a complex orchestration tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but front-loaded with the one-line purpose then 'When to use,' 'Behavior,' 'Returns,' and 'Errors' sections. Every section earns its place given the tool's complexity (platform constraints, token handling, transport gates). It's long but structured clearly with leading headers; could be slightly tighter but is well organized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a complex 8-parameter tool with no annotations, no output schema, and platform-specific behavior. The description covers purpose, when-to-use, behavioral details (token lifecycle, vault, transport differences), return shape, error cases, and parameter trade-offs (brief_file_path vs initial_message). For a tool this complex, the description is remarkably complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all 8 parameters are already documented in the schema. The description adds cross-cutting guidance (brief_file_path preferred over initial_message for non-trivial scopes, brief file validation limits <=10KB). This adds value beyond the schema but the baseline-3 is appropriate since the schema already covers each parameter's semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a very specific action: 'Open a new Claude Code terminal pre-configured as a relay agent (macOS only).' This clearly differentiates from siblings like register_agent (registration), send_message (comm), and post_task (tasking). The verb+resource+scope is specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states 'When to use: orchestrators delegating work to a fresh sub-agent.' It also notes the platform constraint (macOS only, Linux/Windows drivers exist for smoke tests but don't open UI), and distinguishes brief_file_path vs initial_message with clear preference guidance. This gives the agent strong decision context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

task_schema_getA

Fetch a registered task schema by id (v2.10).

When to use: an assignee about to complete a schema-gated task reads the required shape first, so its result conforms and the completion is accepted. Pure read; no auth required.

Behavior: returns the stored JSON Schema document verbatim (the parsed object).

Returns: { success: true, id, json_schema, created_by, created_at }.

Errors: NOT_FOUND (no such schema id).

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesSchema id to fetch
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It explicitly discloses the read-only nature ('Pure read; no auth required'), states the return shape precisely, and lists the error case (NOT_FOUND). The only minor gap is not mentioning rate limits or idempotency behavior, but given no annotations exist and the tool is a simple read, this is solid disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is tightly structured with labeled sections (When to use, Behavior, Returns, Errors), each one to two sentences. Every sentence contributes value with zero waste. Format is scannable and front-loaded with the core purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only fetch tool with two well-documented parameters and no output schema, the description covers all necessary context: purpose, safe/non-auth nature, exact return contract, and error behavior. Given the low complexity, nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameters have descriptions in the schema. The description itself doesn't add parameter detail beyond what's in the schema, but the agent_token parameter has an alternative note in the schema (env var or header resolution). Baseline 3 is appropriate since the schema does the heavy lifting and the description confirms parameter usage context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb+resource combination ('Fetch a registered task schema by id') with an explicit version reference (v2.10). It clearly distinguishes from sibling `register_task_schema` by focusing on the read side. The purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section provides a concrete scenario: an assignee about to complete a schema-gated task needs to read the required shape first. It states 'Pure read; no auth required,' which is an explicit exclusion/prerequisite clarification. This is model guidance for when to invoke this tool vs. not.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unregister_agentA

Remove an agent row so the relay reflects true presence after a clean shutdown.

When to use: terminal exit, role rotation, or one half of the recovery flow when reusing a name after revoke_token set the row to revoked. For graceful working-state announcements without removing the row, use set_status with offline instead.

Behavior: deletes the agent row + all messages and tasks the agent was the from/to of (cascade). Idempotent, unregistering a name that does not exist returns removed:false instead of an error. Auth: requires the agent's own token, OR for admin removals an authenticated agent with manage_others capability.

Returns: { success: true, name, removed: boolean, note }. removed=false indicates the name was already absent (idempotent no-op) and is NOT an error.

Errors: AUTH_FAILED (token missing or wrong owner), INVALID_INPUT.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesAgent name to unregister
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full responsibility and delivers: discloses cascade deletion of messages/tasks, documents idempotency (returns removed:false instead of error), details auth requirements (own token or manage_others capability), and enumerates error codes. Exceptionally thorough behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with labeled sections (When to use, Behavior, Returns, Errors), front-loaded with the core action. Each sentence carries purpose. Slightly verbose around return semantics but all content is substantive, so it earns its length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive mutation with no annotations and no output schema, this is model-complete: covers return format, error semantics, idempotency behavior, auth model, cascade effects, and when-not-to-use. No meaningful gaps remain for an agent deciding whether and how to invoke this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by explaining the semantics of the `removed` boolean return flag and clarifying that agent_token is optional (also resolvable from env var or header) — though this largely mirrors the schema. The auth/destruction context adds meaning beyond plain parameter definitions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear verb+resource: 'Remove an agent row so the relay reflects true presence after a clean shutdown.' Distinguishes from siblings (register_agent, set_status) by explicitly contrasting with set_status for offline announcements. The purpose is specific and action-oriented.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section lists concrete scenarios (terminal exit, role rotation, recovery after revoke_token) and explicitly names the alternative tool (set_status with offline) for graceful working-state announcements. Offers strong when/when-not differentiation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_taskA

Drive a task through its state machine, or extend its lease.

When to use: assignees acknowledge work (accept), report outcome (complete / reject), or keep the lease alive on long-running tasks (heartbeat). Requesters cancel work they no longer need (cancel). Read-only progress checks belong in get_task / get_tasks.

Behavior: enforces role-by-action, accept/complete/reject/heartbeat are assignee-only; cancel is requester-only. Heartbeat refreshes lease_renewed_at without changing status, so the health monitor does not requeue a long task. result is required on complete/reject and surfaces in get_task. Fires task.accepted / task.completed / task.rejected webhooks. Auth: agent token (matching the action's required role).

Returns: { success: true, task_id, status, result, note }. Heartbeat additionally includes lease_renewed_at: ISO. Other actions transition status to accepted / completed / rejected / cancelled.

Errors: AUTH_FAILED, INVALID_STATE (action not allowed in current status), NOT_FOUND (unknown task_id), NOT_PARTY (caller is neither requester nor assignee), PAYLOAD_TOO_LARGE.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesAction to take. accept/complete/reject/heartbeat are assignee actions; cancel is requester-only.
resultNoCompletion notes, rejection reason, or cancellation reason (max 64KB by default)
task_idYesTask ID to update
agent_nameYesYour agent name
agent_tokenNoYour agent token (from register_agent response). Optional here — also resolvable from RELAY_AGENT_TOKEN env or X-Agent-Token header.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. Rich disclosure: role enforcement, heartbeat semantics (refreshes lease_renewed_at without changing status, prevented requeue), result required on complete/reject, webhook fires, auth requirements, error codes. Only minor gap is absence of explicit mutability/irreversibility notes, but the state-machine framing implicitly conveys this.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well structured with clear section labels (When to use, Behavior, Returns, Errors) and front-loaded purpose. Each sentence earns its place; the error list is comprehensive yet compact. Slightly longer than minimal but given the tool's complexity (5 actions, role matrix, webhooks, errors), the length is justified.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Exceptionally complete for a state-machine tool with 5 parameters, no output schema, and no annotations. Covers every action's semantics, role restrictions, conditional return fields, error contract, and webhook side effects. No significant gaps for an agent to select and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. Description adds value beyond schema: clarifies heartbeat behavior with lease_renewed_at, specifies result required on complete/reject, explains what each action transitions status to, and documents the complete return shape including conditional fields. This exceeds what the schema's brief param descriptions provide.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear verb+resource ('Drive a task through its state machine, or extend its lease'). Distinguishes from siblings by explicitly stating read-only progress checks belong in get_task/get_tasks, differentiating from post_task (creation) and get_task/get_tasks (reads).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Excellent when-to-use guidance: explicitly maps each action to the actor (assignee accepts/completes/rejects/heartbeats; requester cancels). Names get_task/get_tasks as the alternative for read-only checks. Also describes role-by-action enforcement, making when-not-to-use clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 2 tool updatesv3.0.1
    • Changedget_messages1 field changed
      • changedInput schema / properties / since / description
        Previous value: -"v2.1.6: time-window filter. Accepts duration ('15m'|'1h'|'24h'|'3d'), ISO8601 timestamp, 'session_start' sentinel, or 'all'/null to disable. Default '24h' trims stale backlog when an agent name is reused."New value: +"v2.1.6: time-window filter over ALREADY-OBSERVED history. Accepts duration ('15m'|'1h'|'24h'|'3d'), ISO8601 timestamp, 'session_start' sentinel, or 'all'/null to disable. Default '24h' trims stale backlog when an agent name is reused. #198: this bound applies ONLY to mail this recipient has already seen — a PENDING drain ALWAYS returns never-observed (undelivered) mail regardless of `since`, so undelivered mail can never age out of reach."
    • Changedget_messages_summary1 field changed
      • changedInput schema / properties / since / description
        Previous value: -"v2.1.6: time-window filter. Accepts duration ('15m'|'1h'|'24h'|'3d'), ISO8601 timestamp, 'session_start' sentinel, or 'all'/null to disable. Default '24h' trims stale backlog when an agent name is reused."New value: +"v2.1.6: time-window filter over ALREADY-OBSERVED history. Accepts duration ('15m'|'1h'|'24h'|'3d'), ISO8601 timestamp, 'session_start' sentinel, or 'all'/null to disable. Default '24h' trims stale backlog when an agent name is reused. #198: this bound applies ONLY to mail this recipient has already seen — a PENDING drain ALWAYS returns never-observed (undelivered) mail regardless of `since`, so undelivered mail can never age out of reach."
  2. 2 tool updates
    • Addedget_outstanding
    • Changedsend_message2 fields changed
      • addedInput schema / properties / deadline
        Added value: +{
        +  "description": "ADR-0011: optional ISO8601 deadline for an 'obligation'. Overdue is reported strictly past this instant; omit to use the tunable default bound (RELAY_OVERDUE_SECONDS). Ignored for log/ask.",
        +  "minLength": 1,
        +  "type": "string"
        +}
      • addedInput schema / properties / disposition
        Added value: +{
        +  "default": "log",
        +  "description": "ADR-0011 disposition: 'log' (default, FYI, never overdue) | 'ask' (expects a reply/resolve) | 'obligation' (an action owed; pair with optional `deadline`). Overdue is report-only — query it via get_outstanding.",
        +  "enum": [
        +    "log",
        +    "ask",
        +    "obligation"
        +  ],
        +  "type": "string"
        +}
  3. 2 tool updatesv2.24.0
    • Addedrevoke_token
    • Addedrotate_token_admin
  4. 34 tool updates
    • First observedabandon_registration
    • First observedbroadcast
    • First observedcreate_channel
    • First observeddelete_webhook
    • First observeddiscover_agents
    • First observedexpand_capabilities
    • First observedget_channel_messages
    • First observedget_messages
    • First observedget_messages_summary
    • First observedget_standup
    • First observedget_task
    • First observedget_tasks
    • First observedhealth_check
    • First observedjoin_channel
    • First observedleave_channel
    • First observedlist_webhooks
    • First observedpeek_inbox_version
    • First observedpost_task
    • First observedpost_task_auto
    • First observedpost_to_capability
    • First observedpost_to_channel
    • First observedregister_agent
    • First observedregister_task_schema
    • First observedregister_webhook
    • First observedreport_liveness
    • First observedresolve_messages
    • First observedrotate_token
    • First observedsend_message
    • First observedset_dashboard_theme
    • First observedset_status
    • First observedspawn_agent
    • First observedtask_schema_get
    • First observedunregister_agent
    • First observedupdate_task

TDQS

A4.2/5.0
Disambiguation4/5

Each tool targets a distinct operation on a distinct resource, with clear descriptions. The mailbox-related tools (get_messages, get_messages_summary, peek_inbox_version, get_outstanding) could be confused, but their descriptions explicitly differentiate consume, preview, count, and sender-recap behaviors.

Naming Consistency4/5

The majority of tools follow a verb_noun snake_case pattern (e.g., register_agent, send_message, get_tasks). A few exceptions stand out: task_schema_get reverses the pattern to noun_verb, broadcast is just a bare verb, and post_to_channel/post_to_capability use a verb_preposition_noun structure. Overall still predictable and readable.

Tool Count2/5

With 37 tools, this server is well beyond the 25+ threshold that makes a toolset heavy to navigate. While the breadth reflects a feature-rich relay with messaging, tasks, channels, webhooks, and agent lifecycle, the sheer number will force agents to spend significant effort choosing among many highly specialized tools.

Completeness4/5

The toolset provides comprehensive coverage of agent lifecycle (register, unregister, revoke, rotate, expand), messaging (send, broadcast, read, resolve), tasks (post, update, get, schema), webhooks (register, list, delete), and presence/status. Minor gaps include no list_channels or delete_channel for channel management, and no single-agent lookup beyond discover_agents, but these are workable.

Maintenance

ActivityActive
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    MCP server that lets multiple coding-agent sessions on the same machine discover each other and collaborate through a shared SQLite database.
    24
    1
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for inter-agent communication. Gives multiple Claude Code sessions a shared message board, agent registry, and orchestration layer — backed by a cloud relay so agents can coordinate across machines, repos, and teams.
    8
    53
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    A NATS-backed mailbox for local LLM agents. Provides a simple way for AI coding agents to message and notify each other via a hostable MCP server.
    2
    GPL 3.0

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Maxlumiere/bot-relay-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server