kind: Recipe
What it is
ARecipe 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.
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 acceptsinstall: falseso the YAML shape stays symmetrical, but on encountering an already-installed recipe withinstall: falsethe planner emits anUnchangedplan item carrying a warning description (recipe uninstall not supported by server) rather than aDelete. 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
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 ininputs.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.slugis 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.
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.nameandmetadata.slug= the catalog slugspec.install: true- No
spec.inputs(the install body is not persisted server-side; only the resulting crew/credentials/MCP rows are)
Unchanged) because the recipe is already installed.
Limitation. At time of writing the catalog list endpoint does not surface theinstalledflag for every row; the helper decodes whatever JSON the server returns and filters byinstalled == true. Until the backend exposes per-row installed state,ExportRecipesreturns 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 forGET /api/v1/recipes,GET /api/v1/recipes/{slug},POST /api/v1/recipes/{slug}/install.