Skip to content

Roles & Permissions

The ladder, briefly

Four roles, each including everything below it: viewer < commenter < editor < owner. A viewer can read a canvas's state but never change it. A commenter can read and can write, but its writes never apply directly — they become proposals for someone else to act on. An editor and an owner can both apply a write immediately; the distinction between those top two matters for administrative actions rather than for day-to-day drawing. The full ladder, and exactly what each role returns from a write call, is in Canvas Roles — this page is about what the ladder means for how you actually work with it.

There is no default

An agent with no explicit grant on a canvas is not treated as a viewer, and it is not treated as having no access in a way that fails silently. It gets a clear error the moment it calls any canvas_* tool against that canvas. Nothing about being in the same workspace, having a role on a different canvas, or having called canvas_list successfully changes this — each canvas's grants are its own.

This is deliberate, not an oversight: see Explanation: Canvas Model for why an ambient default was rejected in favor of requiring every grant to be a decision someone actually made.

Getting a role onto a canvas

This is the part the tool surface doesn't cover. No canvas_* tool grants or changes a role — check MCP Tools's table and you won't find one — and that means a role has to already be in place by the time an agent makes its first call. If you're integrating a new agent against a canvas and its very first call comes back refused, that is the first thing to confirm: not whether the call itself is malformed, but whether anyone has actually granted this agent a role on this specific canvas at all.

What each role changes in practice

RoleReadingWriting
viewerFull read access — canvas_list, canvas_get_state, canvas_query_region all workEvery write tool returns rejected
commenterSame as viewerA write is accepted but never applied directly — it comes back as proposed, pending someone else's approval
editorSame as viewerA write applies immediately, subject to the same revision check every role's writes go through
ownerSame as viewerSame as editor for ordinary writes; administrative actions (where they exist) are reserved for this level

Every write, at every role, still has to carry a valid expectedRevision and a clean, single-line reason — role only decides whether an otherwise-valid write applies, proposes, or is refused outright. See Revisions & Conflicts for the revision side of that, and Proposals & Approval for what happens after a commenter's write is accepted as a proposal.

Debugging a rejected outcome

A rejected outcome carries a reason. Two causes produce it, and they're fixed differently:

  • Role too low — the calling agent's role doesn't allow this action at all (a viewer attempting any write, most commonly). Fix: get the role raised, then retry the same call unchanged.
  • Malformed reason argument — the write's own reason field contained a newline or a control character and was rejected outright rather than cleaned up. Fix: resend with a single clean line; nothing about role is at issue here.

Neither of these is the same problem as a conflict outcome, which is about a stale revision, not about permission — see Revisions & Conflicts for that distinction in more detail.

Where to go next

Built with purpose.