Skip to main content

kind: Recipe

What it is

A Recipe is a server-curated 1-click bundle (crew + MCP servers + credentials) that ships baked into the Crewship binary under internal/recipes. The manifest can request that a recipe be installed into the current workspace, but it cannot create, edit, or define new recipes — authoring belongs in server source code, not in user YAML. This makes Recipe an install-only reference kind, in contrast to the full-CRUD kinds (Project, Label, Milestone, etc.). The distinction matters when you’re writing manifests:
  • Full-CRUD kinds let the manifest describe shape and trigger creation/updates/deletion of the entity itself.
  • Reference kinds only express intent (“this recipe should be installed in this workspace”); the underlying entity is owned by the server.
Recipes are identified by metadata.slug, which must match a recipe slug from the server catalog (GET /api/v1/recipes). Installs are not declarative in the same way as Projects — the server may also create credentials, crews, and MCP servers as side-effects of POST /api/v1/recipes/{slug}/install, and those side-effects appear as their own rows once installed.
Heads-up on uninstall. The backend currently exposes no DELETE endpoint for installed recipes. The manifest still accepts install: false so the YAML shape stays symmetrical, but on encountering an already-installed recipe with install: false the planner emits an Unchanged plan item carrying a warning description (recipe uninstall not supported by server) rather than a Delete. Future server versions may add a DELETE verb, at which point this kind upgrades without a YAML schema change.

YAML schema

Field reference

Examples

Minimal example — install with no extra inputs

Realistic example — install with credential values and a target crew

Uninstall intent — recorded but not executed today

The planner records intent and emits a warning (“recipe uninstall not supported by server”) on dry-run and apply summaries. No mutation is performed — the recipe and its installed entities (crew, MCP servers, credentials) remain in the workspace.

Cross-kind references

A recipe install often pairs with a crew defined elsewhere in the same bundle. Apply orders Phase 2 (Crews + agents) before Phase 9 (Recipes), so the crew slug referenced in inputs.crew_slug will exist on the server by the time the recipe plan runs.

CLI reference

Recipe is driven by the generic crewship apply / crewship export commands; there is no per-kind crewship recipe … subcommand today. The endpoints below back the manifest plan/apply machinery; raw HTTP callers can hit them directly.

REST endpoint mapping

There is no DB column that uniquely maps to spec.install; “installed” is a derived state on the server (typically: “a crew matching the recipe’s crew_slug exists in the workspace and has the recipe’s MCP servers”). The manifest treats the boolean as opaque and trusts the server’s answer.

Validation rules

Validate (offline, no HTTP) enforces:
  • metadata.slug is non-empty.
  • spec.inputs.crew_slug, when present, must be a string.
  • spec.inputs.crew_slug, when present and non-empty, must reference a crew that’s either declared earlier in the bundle or already on the server.
Catalog membership (metadata.slug actually exists in GET /api/v1/recipes) is not checked at validate time — the WorkspaceContext doesn’t carry remote recipes today, so the check is deferred to Plan, which has live HTTP access. A missing slug surfaces as a 404 from LookupRecipeRemote → a transparent “recipe not found” plan error.

Apply behavior

Default (ApplyUpsert)

Plan decision table:

ApplyStrict

Strict mode adds no extra constraints for this kind — a recipe already installed is not an error (recipes are workspace-scoped catalog entries, not user-named entities, so re-declaring an installed one is a normal Unchanged outcome).

ApplyReplace

Replace mode is also a no-op for this kind today: the server has no DELETE endpoint, so even “destructively recreate” cannot uninstall the existing bundle. The planner emits Unchanged with the same warning as install: false against an installed recipe. When the backend grows a DELETE verb this kind will start emitting Delete + Create pairs in Replace mode without any YAML schema change.

Round-trip via export

crewship export workspace walks GET /api/v1/recipes and emits one RecipeDocument per row whose installed field is true. The emitted document carries:
  • metadata.name and metadata.slug = the catalog slug
  • spec.install: true
  • No spec.inputs (the install body is not persisted server-side; only the resulting crew/credentials/MCP rows are)
Re-applying an exported recipe doc is a no-op (Unchanged) because the recipe is already installed.
Limitation. At time of writing the catalog list endpoint does not surface the installed flag for every row; the helper decodes whatever JSON the server returns and filters by installed == true. Until the backend exposes per-row installed state, ExportRecipes returns an empty slice in practice. This is correct behaviour (“nothing to round-trip yet”) and upgrades silently once the backend ships the field.

See also

  • docs/manifest/crew_template.md — the deploy-only sibling for crew templates.
  • docs/manifest/connector.md — the install-only reference kind for integration connectors.
  • internal/recipes/recipes.go — the in-binary catalog source of truth.
  • internal/api/recipes.go — backend handler for GET /api/v1/recipes, GET /api/v1/recipes/{slug}, POST /api/v1/recipes/{slug}/install.