kind: Integration
What it is
kind: Integration declares one connected MCP server — either a
remote streamable-http endpoint (e.g. Linear’s hosted MCP) or a
locally spawned stdio process (e.g. npx -y @some/mcp-server). It is
the standalone authoring surface for the case where you want one
integration declared once and shared across many crews
(scope: workspace), or want to declare a crew-scoped integration
outside the bulkier kind: Crew document.
The legacy inline mcp_servers: block nested under a crew (see
Crew and Workspace) still works and
remains the most ergonomic shape for bundling integrations with a crew
definition. kind: Integration is the inverse: declare once, scope
explicitly.
The kind is implemented in internal/manifest/kinds/integration.go.
Scope picks the table
A singlescope: discriminator chooses where the row lands:
crew scope requires crew_slug; workspace scope rejects
it (a crew_slug under workspace scope is treated as an authoring
mistake and fails loudly).
slug == name
metadata.slug MUST equal metadata.name — the server keys MCP-server
uniqueness on name within the (workspace, crew) tuple, but every
cross-kind reference uses slug, so the manifest forces the two to
match (the same convention Label uses).
env vs env_mapping
Two env-related maps both land in the same env_json column:
env— plain static environment variables (e.g.NODE_ENV: production). The value is the literal string the MCP process sees.env_mapping— the credential-indirection layer. Keys are the env-var the MCP server expects; values are the workspace credential’sname(conventionally identical, but can differ — e.g.{GITHUB_PERSONAL_ACCESS_TOKEN: GH_TOKEN}). At agent run time the resolver looks up each credential by name and substitutes the value before the MCP process starts.
env wins (a literal value beats a credential
reference for the same key).
YAML schema
Field reference
Examples
Remote (streamable-http), workspace scope
Local (stdio), crew scope
Static env plus a credential reference
CLI reference
Integrations have both a declarative manifest surface (theapply /
export flow) and a dedicated imperative crewship integration command
group (registered as integrationCmd in
cmd/crewship/cmd_integration.go). The imperative commands are handy for
one-off break-glass work; the manifest path is the version-controlled
complement.
Workspace-scoped commands:
Crew-scoped CRUD lives under the
crew subcommand:
Agent-binding subcommands (
bind, unbind, agent-bindings <agent-slug>,
resolve <agent-slug>) and the per-tool tools / per-binding agent
groups round out the surface.
The declarative flow:
REST endpoint mapping
Endpoints used:
Validation rules
IntegrationDocument.Validate enforces:
apiVersion/kind, when set, equalcrewship/v1/Integration.metadata.nameandmetadata.slugare non-empty, andslug == name.transportis required and one ofstreamable-http|stdio.streamable-httprequires a non-emptyendpoint;stdiorequires a non-emptycommand.scope, when set, isworkspace|crew.crew_slugis required iffscope: crewand rejected underscope: workspace.env/env_mappinghave no empty keys;env_mappinghas no empty values;argshas no empty entries.- When
WorkspaceContextcarries crew data, a crew-scopedcrew_slugmust reference a declared or remote crew.
Apply behavior
ApplyUpsert (default)
- No remote on this scope →
ActionCreate: POST to the workspace or crew endpoint. - Remote on the matching scope, fields drift →
ActionUpdate: a sparse PATCH.args_json/env_jsonare compared after JSON normalisation so key-reordering doesn’t produce phantom drift. - Remote matches exactly →
ActionUnchanged.
Scope change (workspace ↔ crew)
The two scopes are different tables and there is no “move” endpoint, so a changed scope emits aDelete + Create pair, both visible in
the dry-run with a “scope change” note. The delete cascades any agent
bindings on the old row — review the plan before re-running with
--yes.
Round-trip via export
crewship export workspace calls ExportIntegrations, which walks
both the workspace scope and every crew’s crew scope, decoding
args_json back into spec.args and env_json back into spec.env.
Env lossiness: the server has no column distinguishing literal
env from credential-reference env_mapping (both live in
env_json), so every entry comes back under spec.env on export.
If you need the env_mapping shape preserved, keep your source YAML as
the source of truth and re-export to a side file rather than
overwriting the original. Output is sorted by scope, then crew, then
slug for stable diffs.
See also
- Crew — can declare integrations inline via
mcp_servers:. - Connector — install-only OAuth connectors (Linear, GitHub, …).
- Recipe — catalog installs that may bundle integrations.
- Backend:
internal/api/workspace_integrations.go,internal/api/crew_integrations.go. - This kind’s Go implementation:
internal/manifest/kinds/integration.go.