MCP Tool Surface
Agents talk to the SupaCloud platform through an MCP registry defined in
server/src/services/mcp/. Every tool belongs to exactly one tier; the tier
determines which allowlist entry unlocks it and which rate-limit bucket it
consumes.
Tiers and allowlist entries
Section titled “Tiers and allowlist entries”| Tier | Allowlist wildcard | Also granted by |
|---|---|---|
| Baseline | unconditional (always on) | supacloud.*, supacloud.full |
| Read | supacloud.read.* |
supacloud.full, exact name |
| Ops | supacloud.ops.* |
supacloud.full, exact name |
script.run carve-out |
supacloud.script.run |
supacloud.full, exact name |
| Scaffold | supacloud.scaffold.* |
supacloud.full, exact name |
| Deploy | supacloud.deploy.* |
supacloud.full, exact name |
supacloud.ops.* does not include script.run. Scripts can bind to
workspace resources and secrets at runtime, so the carve-out requires a
deliberate additional grant.
A profile-less task (workflow or auto-developer task with no
agent_profile_id) resolves to the read-only default surface
(supacloud.read.*). Ops and deploy tiers remain fail-closed unless an
explicit agent profile grants them (ADR 0033).
Any individual tool may also be granted by its exact name or by
supacloud.<tool-name>.
Baseline tools (N+4, always on)
Section titled “Baseline tools (N+4, always on)”Granted unconditionally to every authenticated agent session.
| Tool | Description |
|---|---|
delegate_subtask |
Delegate bounded work to another agent profile |
get_subtask_result |
Read the result of a delegated child task |
cancel_subtask |
Cancel a delegated child task |
memory.search |
Full-text search over workspace and project memories |
memory.get |
Read one memory by UUID |
memory.list_recent |
List recent workspace and project memories |
memory.propose_review |
Enqueue a pending memory review proposal (no immediate mutation) |
outcome.recent_for_profile |
Read recent outcome telemetry for this task’s agent profile |
Read tier — supacloud.read.*
Section titled “Read tier — supacloud.read.*”All tools are read-only and workspace-scoped from the task session.
Workspace and platform reads
Section titled “Workspace and platform reads”| Tool | Description |
|---|---|
workspace.info |
Active workspace metadata (slug, name, member count, budget remaining, plan tier) |
projects.list |
Projects visible in the workspace |
project.get |
Single project detail |
tasks.list_active |
Running and queued tasks in the workspace |
tasks.get |
Single task detail (cost, duration, last event) |
runners.status |
Runner availability and capacity |
schedules.list |
Active schedules in the workspace |
resources.list |
Configured resources — kind, name, status only; no connection strings or secrets |
apps.list |
Apps with deployment status and public URL |
app.get |
Single app detail |
workflows.list |
Defined workflows |
workflow.get |
Workflow detail including steps (flattened from workflow_nodes + workflow_edges) |
app.migration.list |
Pending and applied migrations for an app |
Run observation
Section titled “Run observation”Read-only run, board, and event tools. Runs are workspace-scoped first, so a run’s detail and events never leak across workspaces.
| Tool | Description |
|---|---|
run.list |
List recent top-level runs of every kind (agent/workflow/script/app), newest first, with optional status/kind/project/task/app narrowing |
run.get |
Read one run by id plus its flattened descendant run tree (workflow node runs and loop iterations) |
run.events |
Read a run’s event log, paginated by sequence number (after_sequence / limit / tail). Since #977 a workflow run’s log also carries a per-node step timeline (workflow_node_started / _waiting / _completed / _failed / _skipped) |
approvals.list |
List the workspace’s waiting decision gates — paused task gates (filter: "tool", prompt redacted) and workflow human gates (filter: "workflow") |
backlog.list |
List backlog items, optionally narrowed by project and state |
backlog.get |
Read one backlog item with its prior runs and routing decision |
Source reads (W7-3)
Section titled “Source reads (W7-3)”| Tool | Description |
|---|---|
app.files.list |
List app source files |
app.files.get |
Read one app source file (body capped at 64 KiB) |
script.get |
Read a script definition |
script.list |
List scripts in the workspace |
app.builds.list |
List build history for an app |
Issue reads (ADR 0033)
Section titled “Issue reads (ADR 0033)”Resolved from project.issue_tracker ?? project.git_provider — covers all
six sources (GitHub, GitLab, Bitbucket, Forgejo, Linear, Notion).
| Tool | Description |
|---|---|
issue.get |
Read a single issue by ID |
issue.list |
List open issues for the task’s project |
Conversation reads (#596)
Section titled “Conversation reads (#596)”Provider-agnostic support conversations (Chatwoot / Intercom). Addressed by an
explicit workspace resource name (the support channel) — there is no
project-level binding. Also granted by supacloud.conversation.*.
| Tool | Description |
|---|---|
conversation.get |
Read a single conversation by ID from a workspace resource |
conversation.list |
List open conversations for a workspace resource |
Documentation and contract reads (#844)
Section titled “Documentation and contract reads (#844)”The only tools that read neither the database nor a tenant resource. They serve
the instance-wide committed documentation corpus (docs/user, docs/admin,
docs/developer and the German mirror docs/de) and the four drift-gated
machine contracts, so an agent can look up how a surface behaves instead of
guessing.
| Tool | Description |
|---|---|
docs.search |
Keyword search over the product docs. Every query term must appear in a page’s path, title or body; hits rank by term frequency and carry the path for docs.get. Optional lang (en/de) and limit (default 10, max 50) |
docs.get |
Read one page by its corpus-relative .md/.mdx path (for example user/how-to/approve-agent-actions.mdx). YAML frontmatter is stripped; the body is capped at 64 KiB. lang: "de" resolves the German mirror of the given path when it exists |
spec.get |
Read one committed contract by name: openapi_public, workflow_schema, mcp_catalog or command_catalog. Narrow a large document with an RFC-6901 pointer (e.g. /paths); the rendered body is capped at 64 KiB and a truncated answer lists the selected node’s child keys so you can navigate down |
spec.get deliberately does not expose docs/openapi/management.json. The
management spec is the M2M control-plane surface, not agent reference material;
exposing it would widen the agent surface and needs an explicit owner decision.
Because they carry no tenant data and need no task context, all three are also
reachable through the external MCP gateway (ADR 0054) with an mcp:read token.
Ops tier — supacloud.ops.*
Section titled “Ops tier — supacloud.ops.*”Write tools; 10 calls per task. supacloud.ops.* does not include script.run.
| Tool | Description |
|---|---|
workflow.trigger |
Start a workflow run |
schedule.create |
Create a new schedule |
schedule.update |
Update an existing schedule |
schedule.pause |
Pause a schedule |
schedule.resume |
Resume a paused schedule |
repo_sync.trigger |
Trigger a repo sync push or pull |
issue.comment |
Post a comment on an issue (any of the six providers) |
issue.transition |
Transition an issue to a new state |
pr.create |
Create a pull request (git forge only) |
conversation.reply |
Post a customer-visible reply (or private note) into a conversation (#596; idempotency-keyed) |
conversation.assign |
Assign a conversation to an agent (#596) |
conversation.resolve |
Resolve (close) a conversation (#596; idempotency-keyed) |
conversation.handoff |
Hand a conversation off to another team (#596) |
task.create |
Create and launch a task in a project of this workspace (#977; the budget and admin-mandated-closeout clamps in the service floor apply) |
task.cancel |
Cancel a running task of this workspace (#977) |
task.intervene |
Send an intervention message into a running task (#977; delivered with a SYSTEM origin and a [coordinator] prefix) |
approval.decide |
Decide a waiting gate (#977). Question-class task gates only — tool, visual and tier_change gates, and every workflow human gate, are refused: those exist to put a PERSON in the loop, so a machine may not answer them (the #858 invariant) |
run.rerun_node |
Re-execute one failed or cancelled top-level workflow node, superseding the failed child run (#977; stop-policy nodes only, never a loop frame) |
permission.grant |
Grant a scoped tool permission (#581-A) |
permission.revoke |
Revoke a scoped tool permission (#581-A) |
script.run carve-out — supacloud.script.run
Section titled “script.run carve-out — supacloud.script.run”| Tool | Description |
|---|---|
script.run |
Execute a workspace script |
Counts against the ops rate-limit bucket (10/task). An optional
script_allowlist array on the agent profile further restricts which scripts
(by slug) may be called.
Scaffold tier — supacloud.scaffold.*
Section titled “Scaffold tier — supacloud.scaffold.*”Proposal and task-spawn tools. Proposals create a pending proposals row for
human review without mutating durable state; task-spawn tools launch a
follow-up agent task addressed at an App or Script scaffold.
| Tool | Description |
|---|---|
scaffold.script.propose |
Propose a new or updated script |
scaffold.app.propose |
Propose a new app |
scaffold.app.file_upsert.propose |
Propose an app file upsert |
scaffold.workflow.propose |
Propose a new workflow |
task.create_for_app_scaffold |
Spawn a follow-up agent task for an app scaffold |
task.create_for_script_scaffold |
Spawn a follow-up agent task for a script scaffold |
Deploy tier — supacloud.deploy.*
Section titled “Deploy tier — supacloud.deploy.*”Deployment lifecycle tools; 5 calls per task.
supacloud.ops.* does not include deploy tools.
In addition to the allowlist grant, the resolved task actor must hold workspace owner or admin permission — the same check the Deployment service applies for HTTP requests.
app.deploy and app.migration.apply require the two-step confirmation
protocol (see below).
| Tool | Description |
|---|---|
app.deploy |
Deploy an app to its public route |
app.undeploy |
Remove an app from its public route |
app.pause |
Set an app to maintenance mode |
app.resume |
Resume a paused app |
app.migration.apply |
Apply pending app migrations |
Rate limits
Section titled “Rate limits”Per-task caps
Section titled “Per-task caps”| Tier | Default | Config env var |
|---|---|---|
| Baseline + Read | 50 / task | AGENT_MCP_TOOL_RATE_LIMIT |
Ops (including script.run) |
10 / task | — |
| Scaffold | 10 / task | — |
| Deploy | 5 / task | — |
Workspace and profile caps
Section titled “Workspace and profile caps”Per-task limits are checked first; only if a call is within its per-task budget
is the workspace-daily / profile-hourly cap reservation attempted (the per-task
slot is refunded if the workspace/profile reservation fails). Counters are
derived from audit_events so they survive restarts.
| Scope | Tier | Default | Config env var |
|---|---|---|---|
| Workspace / day | Ops | 100 calls | AGENT_MCP_WORKSPACE_OPS_DAILY_LIMIT |
| Workspace / day | Deploy | 20 calls | AGENT_MCP_WORKSPACE_DEPLOY_DAILY_LIMIT |
| Workspace / day | Scaffold proposals | 10 calls | AGENT_MCP_WORKSPACE_PROPOSAL_DAILY_LIMIT |
| Workspace / day | Scaffold task-spawns | 3 calls | AGENT_MCP_WORKSPACE_TASK_SPAWN_DAILY_LIMIT |
| Profile / hour | Ops | 30 calls | AGENT_MCP_PROFILE_OPS_HOURLY_LIMIT |
| Profile / hour | Deploy | 10 calls | AGENT_MCP_PROFILE_DEPLOY_HOURLY_LIMIT |
| Profile / hour | Scaffold proposals | 10 calls | AGENT_MCP_PROFILE_PROPOSAL_HOURLY_LIMIT |
| Profile / hour | Scaffold task-spawns | 3 calls | AGENT_MCP_PROFILE_TASK_SPAWN_HOURLY_LIMIT |
Rate-limit exhaustion returns an HTTP 403 (AppError::Forbidden) error. The
per-task message names the tool (Rate limit exceeded for tool '<tool>'); the
workspace-daily and profile-hourly messages name the tier and report a
<used>/<limit> ratio (Workspace daily limit reached for this MCP tool tier (<used>/<limit>) / Profile hourly limit reached for this MCP tool tier (<used>/<limit>)).
Deploy confirmation protocol
Section titled “Deploy confirmation protocol”app.deploy and app.migration.apply use a stateless two-step confirmation
to prevent accidental deploys and stale retries.
Step 1 — request a summary. Call the tool without confirmation_token.
The server returns confirmation_required: true, a changes_summary, and a
confirmation_token (HMAC-SHA256, signed with SUPACLOUD_MCP_CONFIRMATION_SECRET,
carrying the input hash and a 5-minute expiry).
Step 2 — confirm. Call the tool again with the same inputs plus the
confirmation_token. The server verifies the signature, expiry, and that the
input hash matches the current arguments. An idempotency_key is required
when confirmation_token is present.
The HMAC key is a server-side secret set via SUPACLOUD_MCP_CONFIRMATION_SECRET
(minimum 32 bytes). When unset, confirmed deploy and migration-apply calls
fail closed. The key is never derived from task, user, workspace, or profile
credentials.
Idempotency keys for confirmed calls are stored in mcp_idempotency_keys
(scoped to task_id + tool_name + key) and cleaned up 24 hours after the
parent task reaches a terminal state.
Security properties
Section titled “Security properties”- No secret exposure.
resources.listreturns kind, name, and status only — never connection strings, passwords, or tokens. - No credential access. Agents cannot read or modify workspace credentials through MCP.
- Workspace isolation. All queries are scoped to the task’s workspace.
- Audit trail. Every tool call emits an
audit_eventsrow with task correlation ID, tool name, status, and error class.
Related
Section titled “Related”- ADR 0028 — Extended MCP agent surface
- ADR 0033 — Always-on baseline and read-only default surface
- Management API — M2M operator surface (distinct from agent MCP)