---
title: "MCP server"
description: "OAuth 2.1 protection, the full StackShip tool catalog, write scopes, idempotency, and agent attribution."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.stackship.run/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP server

The StackShip MCP resource is:

```text
https://mcp.stackship.run/mcp
```

It uses Streamable HTTP and publishes protected-resource metadata at:

```text
https://mcp.stackship.run/.well-known/oauth-protected-resource
https://mcp.stackship.run/.well-known/oauth-protected-resource/mcp
```

Both 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:tools` scope; 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](/automation/api): 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](/automation/api#idempotency).

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.

## Related pages

- [Authentication](/authentication)
- [Access tokens](/admin/tokens)
- [Automation](/automation)
- [v1 REST API](/automation/api)

Source: https://docs.stackship.run/automation/mcp/index.mdx
