The StackShip MCP resource is:
https://mcp.stackship.run/mcpIt uses Streamable HTTP and publishes protected-resource metadata at:
https://mcp.stackship.run/.well-known/oauth-protected-resource
https://mcp.stackship.run/.well-known/oauth-protected-resource/mcpBoth documents identify https://api.stackship.run/api/auth as the
authorization server and advertise the mcp:tools and mcp:write scopes.
Clients use OAuth 2.1 with PKCE and send the access token only in the
Authorization header.
Validation
Every MCP request verifies:
- the configured API issuer;
- the JWT signature against the API JWKS;
- expiry and other temporal claims;
- the MCP resource audience;
- the
mcp:toolsscope; and - one exact
org_organization claim.
Validation fails closed. A token intended for the dashboard, OpenTofu cloud
protocol, or another resource cannot be reused at MCP. Missing and invalid
credentials return an OAuth bearer challenge that points to the protected
resource metadata and advertises the required mcp:tools scope. An invalid
bearer challenge also includes error="invalid_token" so OAuth clients can
distinguish rejected credentials from an initial authorization challenge.
Both cases return HTTP 401. A valid token that lacks mcp:tools instead
returns HTTP 403 with error="insufficient_scope" and
scope="mcp:tools" in WWW-Authenticate, as required by RFC 6750.
Scopes
mcp:tools grants the read catalog. Mutating tools additionally require
mcp:write; the server publishes them only to write-scoped tokens, and the
API independently refuses an MCP mutation without mcp:write using the
MCP_SCOPE_INSUFFICIENT error code.
Dynamically registered clients receive mcp:tools by default and must
explicitly request mcp:write. Scopes cap what a token may attempt; the
caller’s organization role still decides what is permitted. Every tool call
re-reads the caller’s live membership, so a permission the role lacks returns
403 FORBIDDEN regardless of scope.
Execution model
MCP tools contain no business logic. Each tool is a thin adapter over exactly
one frozen v1 REST operation: the MCP Worker forwards the
caller’s own access token to api.stackship.run, and the API re-verifies the
token — signature-authentic through its hashed token record, unexpired,
unrevoked, MCP-audience-bound, and organization-claimed — before resolving the
same tenant, permission, idempotency, and audit path the dashboard uses.
The API confines MCP-audience bearer sessions to the published automation
surface. An operation outside the catalog — including every delete — returns
403 MCP_OPERATION_NOT_SUPPORTED before any route logic runs. Operations
that require step-up reauthentication (apply recovery, raw state and
sensitive artifact downloads) are structurally unreachable because an MCP
session never carries step-up evidence.
Read tools
| Tool | Purpose |
|---|---|
stackship_status |
Service status plus the authenticated subject, organization, and scopes |
stackship_list_projects, stackship_get_project |
Project inventory |
stackship_list_workspaces, stackship_get_workspace |
Workspace inventory, regions, OpenTofu versions, cloud names |
stackship_get_workspace_source |
GitHub repository, branch, working directory, and trigger paths |
stackship_list_runs |
Cursor-paginated organization run feed |
stackship_list_workspace_runs, stackship_get_run |
Workspace runs and full run state |
stackship_get_run_review |
Review evidence, available actions, and the exact approval binding |
stackship_get_run_logs |
Sanitized plan, policy, or apply log chunks (tail or forward polling, 256 KiB cap) |
stackship_get_run_plan_changes |
Non-sensitive plan change summary (512 KiB cap) |
stackship_get_run_policy |
Policy evaluation outcome and per-attachment decisions |
stackship_list_workspace_state_versions |
Immutable state history metadata, cursor-paginated |
stackship_list_workspace_variables |
Variable metadata; sensitive values are never returned |
stackship_list_workspace_access_assignments |
Cross-workspace state access assignments |
stackship_list_audit_events |
Newest sanitized audit events, cursor-paginated |
stackship_list_policy_sets, stackship_list_policy_set_versions, stackship_list_workspace_policy_attachments |
Policy inventory |
stackship_get_github_installation, stackship_list_github_repositories |
GitHub App status and visible repositories |
stackship_list_registry_modules, stackship_get_registry_module, stackship_list_registry_module_versions, stackship_get_registry_module_version, stackship_list_registry_module_ingestions |
Private module registry inventory |
stackship_list_workspace_credential_profiles, stackship_list_credential_profile_versions, stackship_get_credential_profile_trust_template |
Credential metadata and OIDC trust templates; secret material is never readable |
stackship_list_opentofu_versions |
Selectable OpenTofu versions |
Write tools
All write tools require mcp:write plus the same product permission the
dashboard requires for the equivalent action.
| Tool | Purpose |
|---|---|
stackship_create_project |
Create a project |
stackship_create_workspace |
Create a workspace |
stackship_update_workspace |
Replace execution settings; optional rename |
stackship_update_workspace_source |
Bind or rebind the GitHub source |
stackship_create_run |
Propose a managed run |
stackship_approve_run |
Approve a gated plan with the exact review binding |
stackship_reject_run |
Reject a gated plan with the binding and a reason |
stackship_cancel_run |
Signal a non-terminal run to cancel |
stackship_create_workspace_variable, stackship_update_workspace_variable |
Variable writes; sensitive values stay write-only |
stackship_create_policy_set |
Create a policy set (source publication stays outside MCP) |
stackship_create_policy_attachment, stackship_create_workspace_policy_attachment |
Attach built policy versions |
stackship_create_run_policy_override |
Justified, expiring soft-mandatory override |
stackship_create_registry_module, stackship_sync_registry_module, stackship_update_registry_module_source |
Registry module lifecycle |
stackship_deprecate_registry_module_version, stackship_restore_registry_module_version |
Reversible version deprecation |
Approvals are never bypassed
Run approval and rejection carry the same exact plan, dependency,
predecessor-state, and policy-result binding the dashboard submits. Agents
must read stackship_get_run_review first and echo the binding verbatim; a
since-changed plan is rejected with 409 APPROVAL_STALE instead of
authorizing different changes. A closed policy gate advertises rejection but
never approval. Soft-mandatory policy overrides require the dedicated
policy:override_soft permission and record a justification and expiry.
Idempotency and attribution
Every mutation sends an Idempotency-Key. Tools accept an optional
idempotency_key argument for exact retries and generate a fresh key when it
is omitted. Replay, conflict, and in-progress semantics are identical to the
v1 REST API.
Every MCP mutation is attributed to the human principal who authorized the
OAuth client — the audit actor is that user, never a synthetic agent — and
the audit event’s details carry an automation label with kind: "mcp" and
the acting OAuth client_id, so agent-performed changes are distinguishable
in the audit feed.
Not available through MCP
The automation surface deliberately excludes destructive and secret-bearing operations:
- deletes of any resource (variables, modules, policy attachments, access assignments) and service-account token revocation;
- apply recovery and every step-up-gated operation;
- raw state, sensitive artifact, and emergency-state downloads;
- credential profile creation, secret version writes, verification, archive, and revoke (credential metadata stays readable);
- service-account and token minting;
- access-assignment grants;
- policy source upload and version registration from artifacts;
- the GitHub App installation browser flow; and
- platform admin controls.
These operations must not be inferred from the dashboard or v1 API; the API refuses them for MCP-audience credentials.
Token lifetime and revocation
An MCP access token is a signed JWT bound to the organization selected during
authorization. It expires after one hour. If a client requests
offline_access, the authorization server also issues a hashed, opaque
refresh token that expires after 30 days and rotates on use.
The MCP Worker validates signatures and claims locally, so a revoked JWT can briefly keep satisfying the Worker’s own check; the API behind it, however, resolves every tool call against the hashed token record, so a revoked or deleted grant stops executing tools immediately. Changing the bound organization requires a new authorization.
Data handling
Tool output is the sanitized v1 response body: log streams are
terminal-sanitized, sensitive variable values are never present, and secret
material has no read operation. Operational analytics receive only the MCP
request event and opaque identity values. Authorization headers and
access-token plaintext are not logged, and API errors surface to the agent
as bounded {code, status, request_id} documents.