Ringtail
Reference

MCP tools

The exact tool surface a coding agent drives over the daemon's MCP connection. Every tool returns names + status, never a value.

These are the tools the daemon exposes over MCP (Streamable HTTP, 127.0.0.1/mcp, session token required). The whole surface is the generative-UI vocabulary: the agent supplies structured content; the daemon owns the pixels and the creds. No tool returns a secret value โ€” that is enforced by check:no-leak.

Plan & status

  • plan() โ†’ Status[] โ€” scan the manifest + connected state โ†’ the grid (providers ร— local/dev/staging/prod). Emits key names and status, never values. Also carries pendingUserMessages โ€” see Chat.
  • updateStatus(provider, env, status) โ€” flip one grid cell to a credential status.

Author & render the UI

  • authorWizard(provider) โ†’ Wizard โ€” derive the curated recipe's on-brand 1-2-3 setup wizard (open-url โ†’ paste โ†’ provision) and push it to the cockpit. Returns step names + kinds.
  • renderWizard(wizard) โ€” push an agent-authored Wizard (the long-tail fallback) to the cockpit. Schema-validated; malformed is rejected.
  • mapActions(actions) โ†’ Action[] โ€” map the repo-specific + cross-tool next steps into the actions panel. Each Action is schema-validated. Returns names + prerequisites + danger
    • executor.
  • renderActions(actions) โ€” re-render the living action panel as the user steers it.

Complete & execute

  • submitStep(stepId, value?) โ€” the one inbound value path. For a paste step the value flows user โ†’ daemon (validated + stored) and is never echoed back. Returns status + var name only. When the next step is a safe auto step, the daemon auto-advances it (mint โ†’ validate-after-mint โ†’ provision โ†’ sync) with no agent round-trip โ€” the result carries the value-free autoAdvanced outcome.
  • executeStep(stepId) โ€” the daemon runs the provisioning loop (mint โ†’ validate-after-mint โ†’ provision โ†’ sync) with the stored creds. Returns status + key names.
  • executeAction(id, confirmed?) โ€” run a mapped action through the gates (prereqs โ†’ hard-confirm โ†’ dispatch). A destructive action is refused unless confirmed: true. Returns status + key names.
  • mintKey(action, env?) โ€” the generic dynamic executor. The agent authors an HTTP action inline โ€” { providerAccount, method, url, headers (with a {{ROOT}} placeholder), body?, extract? } โ€” and the daemon resolves the root key from the global vault, enforces the domain allowlist (a non-allowlisted host is rejected before any HTTP; redirects off the allowlisted host are refused, not followed), runs the call, and โ€” if extract names a minted secret โ€” files it in the sink under ringtail/<repo>/<env>/<provider> naming. One path covers mint, read-only permission-check (no extract), and cross-provider wire. A consequential action (e.g. a DELETE that spends the root key destructively) comes back needs-confirm โ€” the agent gets no confirmed argument, so it can never self-approve; a human confirms out-of-band. Returns { providerAccount, varName?, status, reason? } โ€” never a value (root or minted; any reflected secret is scrubbed from reason). The root key itself is pasted by the user via POST /api/root, never through the agent.

Chat (the direction channel)

  • sendChat(message, choices?) โ€” agent โ†’ user in the dashboard chat, optionally with tappable choice pills. Text/intent only, never a value.
  • User โ†’ agent is event-driven (no poll tool). A message the user types (POST /api/chat) is queued and delivered as pendingUserMessages: string[] on the return of the agent's next plan / executeStep / updateStatus / authorWizard call โ€” intent text only, never a value. The agent re-renders actions/wizards to match on its next natural tool call.

Notice what's absent: there is no getSecret, no readValue, no tool that hands the agent a credential. The agent's entire world is names, statuses, and the UI it authors. See The guarantee.