Skip to main content

Recipes

A recipe is a hardcoded bundle of:
  • a curated crew identity (name, slug, icon, color),
  • the credentials it needs (provider + env var name + UX label), and
  • the MCP servers it should run (transport, command, env mapping).
Recipes are baked into the binary (internal/recipes) for the MVP — there is no admin UI to author them; a future migration may promote them to a DB-backed catalogue. The browse / preview / install endpoints below back the dashboard empty state and the “Install recipe” Sheet. The implementation lives in internal/api/recipes.go.
Install is atomic: credentials, the new crew, and its MCP servers are all created in a single transaction — any failure rolls back everything, so a half-installed recipe (orphan crew without its credentials, etc.) never exists.

Endpoints

Recipe shape

Each credential entry: Each MCP server entry:

Browse & preview

Read-only endpoints that back the dashboard cards and the install Sheet.

GET /api/v1/recipes

Return the curated recipe set in display order. No pagination — the catalogue is intentionally small (3 entries at the time of writing). Auth: authenticated session (no workspace context required — recipes are static).

GET /api/v1/recipes/{slug}

Return a single recipe by slug. Distinct from List so the install Sheet can fetch fresh detail without re-iterating the whole catalogue. Auth: authenticated session.

GET /api/v1/recipes/{slug}/preview

Dry run for the install Sheet. Tells the FE which credentials the user already has in the workspace (so the wizard can skip the “Paste your X” step for those), and which crew slug the install will actually resolve to (suffixes -2 / -3 … if the recipe’s preferred slug is already taken). Does not mutate any state. Auth: authenticated session + workspace context (normally supplied by the X-Workspace-ID header or the authenticated session). Request: slug is the required recipe path parameter. Workspace context is supplied by the optional X-Workspace-ID header (or the authenticated session). No request body. Response: 200 OK

Install

Atomically commit a recipe — credentials, crew, and MCP servers — in one transaction.

POST /api/v1/recipes/{slug}/install

Atomic install. Creates the credentials the recipe needs (or reuses existing ones), the crew, and the MCP servers in a single SQLite transaction. The response describes what was actually created vs. reused so the FE can phrase the success toast accurately.
Install requires OWNER or ADMIN role (canRole(role, "manage")). The "manage" action maps to OWNER/ADMIN only — a MANAGER is not sufficient to install a recipe, even though MANAGER can create individual crews/credentials.
Auth: authenticated session + workspace context + OWNER or ADMIN role (canRole(role, "manage")).

Validation

The handler does a cheap shape check outside the transaction so it can 400 fast on bad input: for every credential the recipe declares, either the workspace must already contain it (looked up by env_var_name) or the request body must carry a non-empty value for it. Anything else returns:

Race-safe credential upsert

Two concurrent installs of the same recipe on the same workspace must both converge on the same credential row rather than 500-ing the loser on UNIQUE(workspace_id, name). Implementation uses INSERT OR IGNORE followed by SELECT id — the loser’s insert becomes a no-op; the follow-up SELECT returns whichever id won the race.

Race-safe crew slug allocation

Crew slug allocation also happens inside the transaction. If the recipe’s crew_slug is taken at insert time, the handler retries with slug-2, slug-3, … up to 100 attempts. Slug resolution from the Preview endpoint is advisory only — the install does its own lookup inside the tx so the existence check and the insert see the same snapshot.

Response

Status: 201 Created

See also

  • Credentials — the workspace credential table that recipes upsert into.
  • Crews — the crew row that recipes create.
  • Integrations — the MCP server table (crew_mcp_servers) that recipes populate.