Scopes and roles
Every MCP tool call made with an agent key passes one check before the tool runs:
scope ∧ role: the key must hold the tool's scope, and the key's role must be at least the scope's minimum role.
Both halves are needed. A key with pages:write but the Viewer role cannot create pages; a key with
the Editor role but only read scopes cannot either. The check is the same on every tool, and a
tool that is missing from the scope map is denied for every key (fail closed).
Agent keys
- A key belongs to exactly one space. It never sees, finds or changes anything in another space, even if its creator is a member there.
- A key acts as its creator, capped by the key's role. Content it creates is owned by the creator. Reads see what the creator would see with the key's role: a page restricted to a team the creator is not in stays hidden, and a key below Admin does not inherit its creator's space-admin view of restricted pages.
- Only space administrators create and revoke keys (Settings → Knowledge → Agents and keys). A key's role can be Viewer, Commenter or Editor.
- The secret is shown once. DocSpace stores only a SHA-256 hash of it, so nobody (support included) can recover it. Revoking a key takes effect on its next request.
- Keys do not expire yet. Revoke keys you no longer use.
Scopes
| Scope | Minimum role | Custom-role permission | Tools |
|---|---|---|---|
pages:read |
viewer | CONTENT_READ |
get_context, search, get_mentions, list_pages, get_page, get_backlinks, get_graph |
pages:write |
editor | CONTENT_WRITE |
create_page, append_block |
boards:read |
viewer | BOARDS_READ |
list_boards, get_board |
boards:write |
editor | BOARDS_WRITE |
create_board, add_row, set_cell, create_automation |
catalog:read |
viewer | BOARDS_READ |
list_services, get_service, get_ownership_graph |
catalog:write |
editor | BOARDS_WRITE |
create_service, update_service |
scorecards:read |
viewer | BOARDS_READ |
list_scorecards, get_scorecard |
planner:read |
viewer | BOARDS_READ |
list_work_items, get_work_item, get_item_git_refs, list_sprints |
planner:write |
editor | BOARDS_WRITE |
create_work_item, update_work_item, start_sprint, close_sprint |
sync:write |
editor | CONTENT_WRITE |
sync_from_github |
artifacts:write |
editor | CONTENT_WRITE |
publish_artifact, update_artifact |
scorecards:write |
editor | SCORECARDS_WRITE |
none yet (reserved) |
ask:query |
viewer | ASK_BRAIN |
none yet (reserved) |
The per-tool view of the same data, with each tool's parameters, is the tool reference, which is generated from the server's registry.
Two tools look at more than one scope:
get_contextwith awork_item_idanchor also needsplanner:read(context.forbiddenotherwise).get_mentionscounts board cells only when the key also holdsboards:read; without it the result covers pages only.
Presets
The key dialog offers two presets:
| Preset | Scopes | Allowed roles |
|---|---|---|
| Read only | every scope whose minimum role is viewer (pages:read, boards:read, catalog:read, scorecards:read, planner:read, ask:query) |
Viewer, Commenter, Editor |
| Full write | every scope | Editor only |
Roles
System roles are ordered guest < viewer < commenter < editor < admin < owner. A key's role must be at
least the minimum role of the scope. With a custom role, the role must include the custom-role
permission listed for the scope instead.
Page-level rules still apply on top: on a page whose access list gives the creator less than
Editor, append_block answers page.forbidden even for an Editor key.
Rate limits
| Limit | Applies to | Window | Over the limit |
|---|---|---|---|
| 120 tool calls per key | every tools/call over HTTP (a batch counts each call) |
60 s | HTTP 429, Retry-After: 60, mcp.rate_limited; the calls do not run |
| 20 writes per key and space | create_service, update_service, create_work_item, update_work_item, start_sprint, close_sprint, sync_from_github |
60 s | the tool returns { "error": "Rate limited: …" } |
| 10 uploads per user | publish_artifact, update_artifact (shared with the REST upload route) |
60 s | artifact.rate_limited |
Counters are kept in memory per server process. Treat the numbers as upper bounds, not as a budget to spend.
Security FAQ
What is logged when an agent calls a tool?
Every call made with an agent key writes exactly one audit row and one security (SIEM) event: the
key, the tool, the scope, the result (ok, denied or error), the error code if any, the
latency, the target id (for example page:12) and a redacted summary of the arguments. The summary
keeps ids, enum values and the shape of other arguments (type and length), never free text, page
content or secrets. Space administrators see this in Agents and keys → Activity.
Is the agent key logged?
No. The key travels in the Authorization header, is compared against a stored hash in constant
time, and is never written to logs, audit rows or error messages.
What can an agent never do?
- Reach another space, or learn whether a page in it exists (missing, hidden and foreign pages all
answer
not found). - Exceed its role or scopes, or widen them during a session.
- Create, list or revoke agent keys, change members, roles, space settings or billing: no tool does that.
- Delete pages, boards or rows: no tool deletes. The tools marked overwrites in the tool reference can change existing values (a cell, a work item, a synced page).
- Publish an HTML artifact that contains a detected secret: it is always refused over MCP.
Does the agent see encrypted spaces?
Yes, in clear text, exactly like a member using the app with the same role: decryption happens on
the server. Spaces with the strict protection level do not support term counting
(get_mentions answers mentions.unavailable_encrypted).
Does anything reach the internet?
Only sync_from_github, which fetches a file from GitHub (it is annotated openWorldHint: true).
Every other tool only touches your space.
How do I cut an agent off?
Revoke its key in Agents and keys. The next request with that key gets 401 mcp.unauthorized.
Related
- Errors: what a denied or failed call returns.