Rollout provides a Model Context Protocol (MCP) server that lets AI assistants like Claude Code work inside your workspace. An assistant connected over MCP can read and manage tasks, projects, objectives, documents, workflows, and more — always acting as you, inside one workspace.

Quick setup for Claude Code

1. Add the MCP server

Add Rollout to your Claude Code MCP configuration — ~/.claude.json for all projects, or .claude/mcp.json inside a project:

JSON
{
  "mcpServers": {
    "rollout": {
      "type": "http",
      "url": "https://your-rollout-instance.com/mcp"
    }
  }
}

Replace your-rollout-instance.com with your Rollout URL (or localhost:4000 for local development).

2. Authenticate

Run /mcp in Claude Code and pick the Rollout server to start authentication. Claude Code:

  1. Registers itself with Rollout automatically
  2. Opens your browser to Rollout's Authorize Application page, which shows the application name, the permissions it requested, and the workspace access will be granted in
  3. Waits for you to click Authorize (or Deny)

After you authorize, the browser redirects back to Claude Code and the connection is ready.

Note: access is granted for the workspace you are signed into when you approve. Switch workspaces before authorizing if you need a different one.

3. Start using it

Ask Claude Code in plain language:

List my open tasks
Show the Website Redesign project
Create a task to review the PR

Authentication

Rollout uses OAuth 2.1 with PKCE. This means:

  • No passwords stored in your Claude Code config
  • Access tokens expire after 1 hour and are refreshed automatically; refresh tokens expire after 30 days
  • Scopes limit what the assistant can do
  • Everything runs as you — an MCP client can never see or change more than your own membership allows

OAuth scopes

When authorizing, you see which scopes are requested:

Scope Description
read View tasks, projects, documents, workflows, and other workspace data. The default when a client requests no scopes.
write Create and update tasks, projects, documents, workflows, records, and other resources
admin Team/member administration and workspace-wide dashboard sharing/assignment, on top of write. Domain actions also require your current workspace role or team authority.

Revoking access

There is no in-app page for managing authorized MCP clients yet. To stop an assistant's access:

  • Remove the Rollout server from the client's MCP configuration — without a refreshed token, access ends when the current access token expires (at most 1 hour)
  • MCP clients can revoke their own tokens at the standard /oauth/revoke endpoint

Available tools

Reading and discovery

These tools work with the read scope and never change data:

Tool Description
query Precise, filtered reads over any data source: Task, Project, Objective, KeyResult, Milestone, Workflow, WorkflowRun, WorkflowStepRun, Document, Record, Metric, Team, Activity, ResourceRef, ResourceLink, Connection, Attachment, Dashboard, SavedView, Cycle, CycleSchedule, Label, LabelGroup, Channel (messaging channels you can see; direct messages are never exposed), and Secret (names and lifecycle metadata of the secrets you can use, never a value; there is no tool that creates, rotates, or deletes one). Channel, Secret, Cycle, CycleSchedule, Dashboard and SavedView take plain key:value filters without sorting or grouping. Supports filters, sorting, grouping, and pagination. WorkflowRun error and WorkflowStepRun inputs/outputs are list-mode opt-ins through preloads.
search Semantic + text search across tasks, projects, documents, objectives, records, and attachments
workspace_context Your identity plus the workspace vocabulary: task statuses and types, projects, teams, custom record schemas, and the per-source query grammar
node_types Paginated workflow-node catalog; filter by category, palette group, vendor, or exact node IDs
workflow_inspect One workflow's effective Draft and settings, immutable releases, authoring branches, a bounded exact-source inspection with range reads, and one run's results
attachment_url Short-lived download URL and metadata for an attachment
guide Manuals for the tools on this page: query grammar, per-source upsert fields, authoring contracts and lifecycle rules. Call it with no topic for the index; tool descriptions and error messages name the topic to read next

Querying Record by a schema's own fields: the filter key is the bare field name (company:acme, not data.company), and those keys are only in scope when the same filter pins exactly one object with a positive object: or object_id: term. workspace_context(include:["query_sources"]) lists only the static Record filters; read the per-schema field names, types, position and display_config from include:["objects"].

text, number, date, boolean, select and user_ref fields are filterable out of the box. rich_text and formula fields are filterable only where display_config.filterable was turned on — their default is false — and a formula must return a number. datetime, json and list fields expose no filter key at all, even though display_config reports filterable: true for all three. Filtering on one of those names rejects the entire query in either strict setting.

Rich text reads as Markdown: a description, a Document's content, an Activity's body and a record's rich-text values in data. Pass format: "json" for the editor's document JSON instead. A list row carries a plain-text summary (at most 160 characters) in place of the body; name the body in fields, or read one record by ref, to get it. Writes take the same Markdown back: a string is always Markdown and a map is document JSON, so you can read, edit and write back without converting.

Custom values come back inside the record's data object, not as top-level keys. A fields name may be a dotted path, so fields:["data.company"] returns that one value and drops its siblings, while fields:["data"] returns all of them. A shared field whose name matches one of the record's own serialized keys (id, ref, slug, title, number, primary_status, primary_date, object_id, object, data, created_at, updated_at, display) is shadowed at the top level, and which of those a read emits varies (object and display need a read that pins one object, and a schema-pinned list read drops ref and slug), so name the custom value as data.<field_name> to reach it. A requested name that resolves in no returned row is dropped: a list read reports it in warnings, a ref read hands back the record alone and cannot.

Shared write tools

These tools work across resources. Each lists the sources it supports in its source field, and each source keeps its own authority rules.

Tool Description
upsert Create or sparsely patch a record through its owning command. ref updates an exact record, upsert_key matches or creates, no selector creates. expected carries the optimistic guard a read returned, and dry_run validates without writing; both are refused on sources that cannot honour them. Per-source fields: guide(topic:"upsert.sources")
archive Archive a resource, or restore it with restore:true. Recoverable: the row leaves default reads and keeps its history. Accepts ref or up to 50 refs
purge Permanently delete a resource and what cascades from it. Its description states each source's precondition (for example, a workflow must be archived first)
access Read or change who may see or use a resource: grant/revoke a team or workspace audience, and for dashboards and saved views assign/unassign a default or required placement. Rules per source: guide(topic:"access.rules")

Tasks and planning

Write tools require the write scope:

Tool Description
upsert Create or sparsely patch a Task, Project, Objective, Milestone, or Key Result using canonical fields and ISO dates; exact ref updates and opaque upsert_key matches or creates
archive with source: "Task" Archive (soft-delete) or restore tasks, with batch support
task_planning Plan a Task into its Team's backlog, Current, Next, or an exact Cycle; move a Task, or a parent with its whole subtree (preview, then apply the reviewed digest), to another Team; add a missing contributing Team to a Project while moving the Task to it
cycle_schedule Activate, reconfigure, or deactivate one Team's Cycle Schedule at its safe boundary, with a dry_run impact preview. Applying takes expected (the Schedule's lock_version, plus the previewed impact_digest for deactivate). Requires the admin scope plus schedule-governor authority

Cycles and Cycle Schedules belong to exactly one Team, so query on Cycle or CycleSchedule needs a team: filter; there is no workspace-wide current Cycle. Cycle takes state:current (the default: opening, open, or finalizing), state:next (the Team's earliest scheduled Cycle), or state:completed; a ref read returns the same row and adds the Team-lane progress report with preloads:["report"]. Only members of the Team can read its Cycles. A CycleSchedule row carries the lock_version, effective cadence, and the activation/deactivation previews cycle_schedule works from.

Workflows

Tool Description
upsert with source: "Workflow" Create a Paused workflow without a graph, or update its name, slug, description, labels, owner, team and execution settings (runtime, env, services, volumes, defaults). Its run identity (runs_as with run_as_agent, an agent id or @handle) is written alone on an existing workflow and needs manage access
workflow_edit Edit the main Draft or a branch Draft graph under the observed-source guard, or cut an authoring branch
workflow_release Publish a Draft as the Current release, restore an earlier release, pause or start production; one transition per call
workflow_run Start a production run (optionally waiting up to 120 seconds for the result), cancel or retry runs, and preview and admit exact-source node tests
archive / purge with source: "Workflow" Archive (disarms triggers, keeps runs and releases) or restore; permanently delete an archived workflow, or discard a never-published pending Draft

Each workflow tool's description names a guide topic that carries its full contract: workflow.settings, workflow.edit, workflow.inspect, workflow.release and workflow.run.

A new workflow is two calls: upsert creates it with no graph, then workflow_edit gives it one. A workflow without a graph needs no guard for its first edit. For a whole graph, send one replace_flow operation so the complete nodes-and-edges document is validated and written under one optimistic lock. Any edit that changes the graph's structure ends with a full layout of the Draft; nodes locked with locked: true (or data.locked) keep their positions and the rest is laid out around them. A node or edge the canonical shape would have to drop is refused by name rather than silently repaired. update_node supports merge or replacement modes for both config and execution settings, plus explicit top-level key removal. set_schedule updates a schedule's cron, timezone, and description without rebuilding the graph. workflow_inspect and node_types accept read-scoped tokens and survive read-only mode; every other workflow tool requires write.

workflow_inspect tells a caller what a workflow takes and returns before it starts one. action: "status" (the Current release) and action: "release" (the release asked for) return contract: {entry_node_id, inputs, outputs, digest}. inputs is a JSON Schema of the inputs the Manual start declares (required lists what must be sent, default what an absent key becomes, enum the allowed values, and any other key is refused), or null when it declares none and takes any object. outputs is a JSON Schema of what the workflow's return steps declare, or null when they declare nothing. digest changes whenever either does. Build workflow_run start's input_data from contract.inputs: a start missing a required input or sending a key no input declares is refused with every field named, and no run is created. A workflow the caller cannot read returns not found and no contract. A Tool on Run workflow declares the same inputs as typed arguments. It starts its workflow only when an AI agent step calls it inside a workflow run, so invoke refuses it and says so; start the workflow with workflow_run.

workflow_inspect with action: "branches" names this workflow's live authoring branches as identity only — id, name, revision (the branch's lock version), has_draft, timestamps — plus main_draft_revision for the main Draft, which is the workflow row and is never listed as a branch. It is how a branch id is obtained without opening the editor: action: "releases" names only branches that already published something. The graphs are neither returned nor read. Read one exact source with action: "source".

create_branch cuts a new branch from the source its selector names, using the same grammar as a source inspection's selector ({"kind":"release","id"|"name"} with "current" accepted as the name, {"kind":"branch","id"}, or {"kind":"main_draft"}, which is the default). It is the only operation in its call. A release is named by id and re-resolved server-side, so the branch's baseline is that release exactly as published. Nothing else moves: no existing Draft, no other branch, no published pin. It switches nothing and runs nothing.

The response's source is the RESOLVED selector, not the one that was sent: ask for {"kind":"release","name":"current"} and it comes back as that release's exact id, because Current is an alias that moves on the next publish. source_revision carries the baseline's lock version (null for a release). Sending source_revision pins the cut: a Draft that moved since the revision you inspected is refused with stale_source rather than seeding a graph you never read.

Graph edits carry expected: the expected_source object of the exact Draft the caller inspected. It also decides which Draft the batch writes:

JSON
{"source": {"kind": "main_draft"}, "source_revision": 7, "source_digest": "sha256:..."}
{"source": {"kind": "branch", "id": "<branch id>"}, "source_revision": 3, "source_digest": "sha256:..."}

The first writes the main Draft, the second that branch's Draft; nothing else in the call can point the batch somewhere else. The main Draft needs a guard once the workflow has a graph, and a branch edit always carries one, because the guard is what names the branch. It is compared against that Draft's canonical row inside the write transaction, not against a fresh read the tool takes for itself. A moved revision or digest is refused with stale_source and nothing is written. source_revision is that row's lock_version (the workflow's for the main Draft, the branch's for a branch): it advances on every write, so an edit that restores a graph to earlier bytes is still detected even though its digest is unchanged. Unknown keys in the guard — and in any operation object — are refused rather than ignored.

Copy the whole expected_source object a read hands back into expected. workflow_inspect returns one for action: "flow" (the main Draft) and for a main_draft or branch source inspection (whose source key is a fuller identity object, not a guard, and is refused as one), and every successful edit returns the guard the next edit of the same Draft needs, so a chain of coupled batches costs one read. action: "branches" lists the branch ids. A branch that does not exist, is archived or belongs to another workflow is refused as not found and nothing is written. A branch edit reaches production only when workflow_release(action: "publish", branch_id) publishes it. A guard naming a release is refused: releases are immutable. When the stored graph cannot be digested at all, action: "flow" returns expected_source_unavailable instead of a guard, and the graph is repaired by saving it in the editor.

workflow_release publish, restore and start check every connection, secret and device the release uses with the checks Start applies. A refusal names each one that is not available and where it is used, in the sentences the editor shows ("Secret API_KEY is missing (used by Deploy)."), for a caller who can write the workflow. A connection or device that caller cannot use stays nameless; anyone else gets the undifferentiated refusal.

An edit returns a bounded changes summary (the nodes that actually moved and the paths within them, with long values previewed and secret material shown as markers) alongside the resulting expected_source. At most 16 moved nodes with at most 64 changed paths each are reported, under a fixed byte budget; an entry past that budget is reported as its node id plus detail_omitted. The whole updated graph is returned when the response fits the 64 KiB bound; otherwise draft_omitted is set and the response names the source inspection that reads it in bounded pieces.

One guarded edit is one transaction over one Draft row: the workflow row for the main Draft, the branch row for a branch Draft. A change that also writes a Document, a tool binding, or anything in another domain is a separate commit with its own recovery.

Tools, parameters and actions are closed: a parameter the chosen action does not read is refused rather than ignored, so a misspelled option (for example inert? on a node test) can never read as applied.

Node config is strict at save and publish time. Keys must be declared by that node's manifest (apart from the documented platform-owned keys), and execution settings belong at node.data.execution_settings. Unknown or misplaced keys are errors rather than silently ignored configuration.

Apps and governed tools

Tool Purpose
app Create, update, edit, read, configure, undo, deploy and validate Apps.
tool Configure actions owned by an App, Agent profile or Workflow node; inspect readiness and target consent; follow or cancel reviews.
invoke Prepare one direct native or remote call, follow its status, cancel undispatched work and download its result.

Read an App with query(source:"App",ref:"<id or slug>"). Use app action:"read" for source text by path. update replaces the source bundle; edit accepts put, delete, rename and entry operations with the observed file checks. update, edit, configure and undo require expected_version from the lock_version in your latest read. Read and reconcile a stale write before submitting another change.

For native Apps, Save validates and builds before replacing the live frontend. A failed or superseded build preserves the accepted frontend. Explicit action restrictions can still take effect: save_failed with restriction_applied:true includes the refreshed App version and diagnostics. There is no separate native Publish step.

Native Apps retain the live build and one previous accepted build. app(action:"undo",ref:"<app>",expected_version:...) restores the previous source and settings through current validation and permissions. Undo never restores revoked consent or replays calls. Rebuilding unchanged source does not consume Undo.

Git Apps separate settings Save from source Deploy. Save action settings with app action:"update" and runtime settings with app action:"configure"; these changes do not rebuild or restart the image. app action:"deploy" requests a source build. Only successful deployment installs its commit and image; accepting the request is not deployment success.

Workflow action edits remain in the Workflow Draft. Publish the Workflow to use those edits for new runs; existing admitted runs retain their captured configuration. Agent default edits likewise preserve admitted runs. Explicit revocation still stops affected undispatched calls.

Use access(source:"App",...) for placement and exposure, and archive(source:"App",...) to retire an App. Manuals include app.lifecycle, app.components, app.tools and app.constraints.

Consumer-owned actions

Actions belong to their App, Agent profile or Workflow node and have local aliases. Discover native recipes with guide(topic:"tool.templates"), then guide(topic:"tool.template",args:{"node_type":"<id>"}). For authorized remote names and schemas, use:

guide(topic:"tool.templates",args:{"connection_id":"<uuid>"})
guide(topic:"tool.template",args:{"connection_id":"<uuid>","remote_tool":"<name>"})

Read the complete editable list and its write guards:

tool(action:"read",consumer:{"consumer_kind":"app","consumer_id":"<uuid>"})

consumer_kind is app, agent or workflow; a Workflow selector also requires node_id. tool action:"configure" replaces the complete tools list using the returned expected and expected_version values. Recipes contain a local name and label, plus either node_type and settings, or connection_id, remote_tool and settings.

tool action:"remove" takes those same guards and an action_ref containing id, name and path. To repair an unavailable action, edit its recipe and Save the complete owning list. Healthy siblings remain callable. Local and nested actions exist only for rollout.ai with tool_mode:"replace"; under inherit or none the list must be [], and a non-empty list is refused. Switching a node out of replace clears its list.

Save validates configuration and target consent together. MCP-authored configuration using per-call never requires an exact human review under workspace policy. Pending replacements remain unavailable until approval. tool action:"readiness" reports draft validation, not a promise that a later dispatch will succeed.

tool action:"grants" inspects an exact attachment address and attachment_id. delegate additionally takes the observed action digest as expected and a stable request_key; it requests human consent rather than granting access immediately. review_status and review_cancel take approval_id. revoke_delegation takes the exact delegation UUID. Revocation closes affected undispatched calls and their pending reviews; running, unknown and completed evidence remains intact.

Direct calls and large payloads

invoke action:"run" selects exactly one target:

  • attachment: {consumer_kind,consumer_id,node_id?,path?,name};
  • node_type with settings;
  • connection_id with the discovered remote_tool;
  • app: an App id or slug for a source update.

An attachment selector requires configuration access—App/Agent management or Workflow authoring write—plus invoke permission. It selects App live, Workflow published or Agent default configuration and executes as the current authenticated caller. It does not run that consumer or borrow its grants, accounts or installations. A caller-selected run ID cannot supply execution authority. Actual App and admitted Workflow execution use their own server-owned consumer context.

Every new call requires a stable idempotency_key. Selecting an exact, accepted consumer attachment preserves its configured per-call policy: always requires a human decision; never can run without a fresh ask. The caller must independently retain configuration access, invoke permission and current target and resource rights. Selecting configuration never transfers the consumer's authority, identity, grants or installations.

Raw one-off node_type, connection_id/remote_tool and App-source requests require one exact human review, including read-only recipes. Configuration, target and call review are combined into that decision. No saved action or reusable target grant is created. An Agent caller remains an Agent.

For example:

invoke(action:"run",
  node_type:"rollout.query",
  settings:{"presets":{"source":"Team"},"slots":{"query":{}}},
  arguments:{"query":"name:Operations"},
  idempotency_key:"operations-query-001")

Reuse the key only for the exact same selector, settings and arguments. Changed requests conflict. An intentional new business call uses a new key. An outcome_unknown call requires reconciliation; do not submit it again under a new key.

For the App source shortcut, arguments is {"source":{"entry":"index.tsx","files":{...}},"expected_version":...}. Keep the version you observed. An approved successful native source update goes through Save and becomes live without a separate Publish step.

Results up to 16 KiB are returned inline. Larger results expose result_bytes; retrieve them with invoke action:"result".

For a large request:

  1. Write the exact JSON object bytes to body.json and compute their SHA-256, including whitespace and key order.
  2. Call invoke action:"run" with the target, idempotency_key, presign:true and body_sha256:"sha256:<64 lowercase hex>". Omit arguments.
  3. Use the returned request.curl to POST those bytes. The reply includes invocation_id; 202 means approval is pending or execution continues. When review is required, match it using the returned binding_code.

status, result and cancel take that existing invocation_id. status reports state, approval details and bounded results. cancel withdraws your undispatched call. After a transport timeout, inspect the existing call before retrying.

result returns a single-use GET receipt and download command for /api/tenants/<tenant_id>/tool-results. It reads the immutable succeeded result. Detaching the original configuration does not erase that history. Current principal and original source authorization remain required. Use the same API key, or a current OAuth token in the original family; another API key owned by the same user is a different source.

Admission and result bearers expire within ten minutes and are single-use. Accepted calls and human reviews have their own lifetime. Bearer expiry does not erase an accepted call: follow that admitted call with its invocation_id. A consumed admission bearer cannot be reused. If a result bearer expires or has been consumed, invoke action:"result" can issue a fresh receipt while the original source remains authorized. run and cancel require MCP write; status and result require read.

Private transport accepts at most 2 MiB of request bytes and 4 MiB of result bytes. App/model execution keeps its separate 200,000-byte result ceiling and App source constraints. Outbound MCP responses retain their 2,000,000-byte cap. An uncertain external effect is recorded as outcome_unknown, never silently replayed.

See guide(topic:"tool.lifecycle") and guide(topic:"invoke.transport") for the current contracts.

Documents and records

Tool Description
upsert Create or sparsely patch Tasks, Projects, Objectives, Milestones, Key Results, custom records, Documents, and authored comments or notes. ref updates an exact record; an opaque upsert_key matches or creates within the tenant, source, and custom schema; no selector creates. Custom-record insert-only batches accept up to 50 items. Existing Document body replacements require the digest returned by query; edits change passages of an existing Document's text without it. Only custom records accept a request idempotency_key
object Define custom objects and their fields: create and update a type, and create, update, archive, or restore a field. Archive a whole type with archive

Collaboration and organization

Tool Description
upsert with source: Activity Add an authored comment or note to a resource; comment edits require the existing Activity ref and note edits are unsupported. On a Document, on (a quote of its text) with an optional occurrence anchors a comment thread or an ask on that passage; an ambiguous quote writes nothing and lists the occurrences to pick from. on only starts a thread: combined with upsert_key or ref it is refused and nothing is written. query with anchored:true status:open lists a document's threads with their anchor (quote, anchored or detached, section) and replies. An ask with review: true on a Document puts it up for review (Approve or Changes requested unless you give options); the comments the person writes on it while the review is open belong to it, and reading the ask lists them as ask.review_comments (passage, thread, author, body) beside its answer
link Link or unlink internal records, external resources, and URLs. A contributes_to link from a milestone or project to an objective carries a contribution weight
label Attach, detach, or replace the labels on one resource. Label and label-group definitions are read with query (sources Label, LabelGroup) and written with upsert, archive, and purge
upsert / archive / access with source: SavedView Create, revise (guarded by expected = lock_version, with dry_run validation), archive, and share query-only saved views. A saved view's ref is always its UUID (slugs repeat across contexts). guide(topic:"views.query") lists index contexts and query shapes
task_config Administer the workspace's task statuses and types: create, update, reorder, permanent delete with task reassignment, the default status, and per-project status sets. Deactivate or reactivate one with archive (source: TaskStatus or TaskType)

Dashboards

Dashboards are Pages with immutable applied widget revisions. Saved Views are reusable query presentations that a Table widget may reference; they are not dashboard layouts.

Tool Description
dashboard create a private dashboard with its first widget revision, update title/settings, move its owning context or Document target, and edit widgets (add, update, move, duplicate, remove) as one atomic revision. update/move guard with expected = lock_version; edit guards with expected = current_revision_id. dry_run validates a create or edit without saving
query with source: Dashboard List visible dashboards (filters archived, project, team; preloads:["widgets"]), or read one with ref including its widgets. archived:true lists only your own archived dashboards
archive with source: Dashboard Archive, or restore with restore:true; both require expected = lock_version
access with source: Dashboard Share with a team/workspace and set or unset default/required dashboard assignments. Team actions require write plus team authority; workspace actions require admin plus workspace-admin authority.

Recommended flow: guide(topic:"dashboard.authoring") for the widget catalog, guide(topic:"dashboard.viz") for chart and stat widgets, dashboard(action:"create", dry_run:true), then dashboard(action:"create"). Re-read with query(source:"Dashboard", ref) before a guarded update or edit.

For a Metric chart, select a metric_definitions entry from guide(topic:"dashboard.viz") and persist its recommended_bucket_fn; workspace_context(include:["query_sources"]) also exposes tenant-scoped Metric name values. For custom-record aggregates, copy the selected Record source's canonical required_query so the executor loads that schema's shared fields.

Dashboard replies no longer carry policy.can_read (a dashboard you can read is one you were returned), the list-level policy.can_create and owner_id_for_new_dashboard, or next_offset (page with limit/offset and has_more). Widget catalog entries no longer carry catalog_zones, page_zones or picker_enabled (use pickable; every Page widget lives in the main zone). Whether you may create a dashboard, and the owner id a new one gets, are in guide(topic:"dashboard.authoring") under caller.

MCP can create, revise, share, assign, archive, and restore dashboards. Publishing a Page to a public URL remains an owner/admin action in the web UI because public data/form bindings require a separate security review. Dashboard authoring is capped at 100 widgets and a 1 MiB encoded composition document.

Administration

These require the admin scope:

Tool Description
team Create and update teams; add, update, and remove team members. Delete a team with purge (source: Team, admin token)
member Invite, update roles, deactivate, reactivate, and remove workspace members
agent Create an agent, owned by you and placed in your Private space. The caller must be an active human workspace member. Move or expose it to a Team with access before that Team's workflows run as it

Configuration

Read-only mode

For safer AI access, self-hosted deployments can enable read-only mode, which removes all write tools from the server:

BASH
export ROLLOUT_MCP_READ_ONLY=true

The value is read when the application starts, including from a prebuilt release, so changing it requires a restart but not an image rebuild. Accepted true values are true, 1, yes, and on; accepted false values are false, 0, no, and off. Any other value stops startup instead of silently exposing write tools.

query(source:"Secret") survives read-only mode. It returns metadata and decrypts nothing, and a read-only deployment is where an agent reading a workflow most needs to know which secret names resolve for it.

Read-only mode removes write tools from MCP discovery and dispatch. OAuth authorization remains an independent boundary: writable deployments still require each write tool's write or admin scope and domain authorization.

Rate limits

Limit Default
MCP requests 100 per minute per user
Client registration (/oauth/register) 10 per minute per IP
Token requests (/oauth/token) 30 per 15 minutes per IP
Token revocation (/oauth/revoke) 30 per 15 minutes per IP

Requests over a limit receive 429 Too Many Requests with a Retry-After header. MCP responses include x-ratelimit-limit and x-ratelimit-remaining headers.

Troubleshooting

"Authentication successful, but reconnection failed"

This usually means Claude Code has stale credentials. Try:

  1. Remove any hardcoded Authorization headers from your MCP config
  2. Restart Claude Code
  3. Run /mcp again to re-authenticate

"Invalid or expired token"

Your access token has expired and couldn't be refreshed. Run /mcp to re-authenticate.

"Unauthorized" errors

Make sure:

  1. You've authorized the MCP client in your browser
  2. Your token has the required scope for the operation (e.g. write for creating tasks)
  3. You're an active member of the workspace you authorized

"Session not found"

After a Rollout server restart, clients holding an old session get a 404 and must reinitialize. Most clients do this automatically; if yours doesn't, restart it.

"Too many requests"

You hit a rate limit. Wait the number of seconds in the Retry-After header and try again. A retried read is free; a retried write is not, so see the next section before resending one.

A write whose reply never arrived

A connection reset, a timeout, or upstream connect error or disconnect/reset before headers tells you the request failed, not that the write failed. The server may have committed and lost the reply on the way back.

For custom records, send idempotency_key on the original upsert call: use a value unique to one logical request, such as a UUID. Retrying that exact request returns the stored result without a second write. Reusing it with changed data, selector, or match policy is refused. This request key is separate from upsert_key, which identifies the durable record. Other sources do not accept a request idempotency_key.

For any source, an exact ref is update-only and fails if the target is gone. An upsert_key atomically binds one record under the tenant, source, and schema and can be reused to update it; the key never comes from hidden matching on a custom record's business fields. With neither selector, a repeat is a fresh create. Read the Document with query before replacing its body and send its content_digest as expected; a stale digest refuses the replacement. The replacement merges into open editors without reloading them.

To change part of a Document, send edits in data instead of a new body: [{"find": "the old sentence", "replace": "The **new** sentence.", "occurrence": 2}]. find quotes the Document's visible text (words in order; line breaks and Markdown syntax do not matter), replace is Markdown, and occurrence picks one of several matches. Text found within one block is replaced in place and keeps its formatting; a replacement with blocks (paragraphs, a heading, a list) needs find to cover whole blocks. "replace": "" deletes. Edits apply in order, all or nothing: a quote that is missing or ambiguous writes nothing, and the error lists the matches to pick from. Edits need no expected, never go with content, and merge into open editors like a collaborator's typing. A comment thread whose passage an edit changed is detached; the others stay anchored.

For other MCP clients

Rollout implements standard OAuth 2.1 with these discovery endpoints:

Endpoint Purpose
/.well-known/oauth-protected-resource RFC 9728 resource metadata
/.well-known/oauth-protected-resource/mcp Same document, path-aware form (RFC 9728 §3.1)
/.well-known/oauth-authorization-server RFC 8414 authorization server metadata
/oauth/register RFC 7591 dynamic client registration
/oauth/authorize Authorization endpoint
/oauth/token Token endpoint
/oauth/revoke Token revocation endpoint

The server supports the authorization_code and refresh_token grant types, PKCE with S256, and public clients (no client secret). Any MCP client that supports OAuth 2.1 with dynamic client registration should work automatically.

Redirect URIs may be HTTPS, a loopback callback (http://127.0.0.1:PORT/…, http://localhost:PORT/…), or a private-use scheme owned by a desktop client (com.raycast-x:/oauth, vscode://…). Schemes that name a code-execution target — javascript, data, file, vbscript — are rejected.

Device settings: query(source:"Device") reads authorized devices and their max_agent_turns (default 4). upsert(source:"Device", ref:<persisted UUID>, data:{max_agent_turns:6}) changes only that setting, with current owner/exact Manager authority. Device creation and other fields are rejected.

Workflow execution-path advisories

Successful workflow_edit edits/restores, workflow_inspect graph reads and workflow_release publish/restore responses include advisories and advisories_omitted. Detail is bounded; the omitted count explicitly reports warnings that did not fit. Warnings identify the trigger, unscheduled source, consumer, wire and missing contributor, without including input or config data. They permit publication. A batch-data source outside the selected plan, such as a disconnected document read, needs a path from each intended trigger: task._done -> role._trigger. Streaming members share a contracted execution unit: a producer with no incoming edge can still be planned when its consumer is reachable. That producer, and a scheduled source skipped by a branch, do not receive this advisory. Run inspection also shows the consumer's persisted step warnings, recorded before it executes.