Scope recipes for agent jobs
The minimum scope set for each common agent job, what each scope buys, and what to add when a job needs more.
On this page
Every MCP tool is gated twice, and both gates must pass:
- the umbrella for that side of the endpoint —
mcp:readfor every read,mcp:writefor every mutation; - the domain scope in that tool's own row of the MCP tool reference.
A key holding an umbrella and nothing else authenticates and is served an empty tool list. That is the single most common "the server does not support this" report, and it is a scope problem.
A person creates and changes keys in the Console, in Agents and keys (/agents), or with the
CLI. The recipes below name the scopes; the Console and the CLI both take the same scope names. See
The Console.
Start from a bundle
anectico apikey create --scope-profile <name> and anectico agent bootstrap --scope-profile <name>
materialize a bundle into explicit scopes, stored on the key. The key records the scopes, never
the bundle name, so its authority is fixed at mint time and is auditable by reading it back with
anectico apikey list -o json.
| Bundle | Scopes | What it is for | Mintable by |
|---|---|---|---|
investigate |
34 | Default investigation reads, model costs and stored evaluation scores; no configuration changes or evaluation execution | any member |
incident |
57 | Investigation plus incident response | owner or admin |
agent-observability |
13 | Agent operations, governance and evaluation writes | owner or admin |
admin |
2 (the two umbrellas) | Exact administrative permissions added with --scope |
owner or admin |
investigate includes llm:read and evals:read. It can read model spend and stored scores
without a second key. It cannot change prices or evaluation settings or start a paid evaluation.
Three properties of that table are decisions rather than accidents:
incidentcontainsinvestigatein full. Incident response is investigation plus a lifecycle surface, not a different way of looking at the same data.agent-observabilitydoes not. It covers agent runs, transcripts, cost, and person context, plus an evaluation write loop:mcp:write,evals:write, andscores:writeallow release-gate creation and evaluation and annotation submission through the matching actions. Annotation writes additionally require an authenticated user to attribute the judgment to; scope alone does not make an unattributed API key a human reviewer. It is not a read-only monitoring bundle. Add--scope traces:readfor raw spans behind a run.adminrefuses to stand alone. It buys the two umbrellas and a statement of intent; the authority is typed out one--scopeat a time.anectico apikey create --scope-profile adminwith no--scopeis a usage error, and a key holding only the umbrellas is served nothing.
No bundle ever materializes a *:delete scope. Deletion is the one class of write that cannot be
undone, and the preview-then-confirm gate that makes it survivable lives in the MCP write framework —
a key used against the REST API gets no such gate. Every delete scope must be named explicitly.
Two things about who can mint. A key can never carry a scope its creator lacks — the mint is
refused by name, so incident, agent-observability and admin are owner-or-admin work because all
three carry mcp:write. And a viewer cannot mint a key at all, of any shape: key creation is
itself a privileged act, and the refusal comes before the scope list is read, so hunting for the
missing scope is a dead end.
Recipes
Let the agent read its own documentation
Add docs:read alongside mcp:read when an agent must discover procedures with search_docs and
read them with get_doc through the read gateway, or use the compatible search/fetch tools.
These two scopes alone are enough for documentation-only MCP access; no organization-data scope is
needed. The investigate and incident bundles already include docs:read; the
agent-observability and admin bundles do not. Add --scope docs:read explicitly to those bundles
or to a custom recipe. Counts below describe the minimal recipes without that optional addition.
The URL's ?profile= filter is separate from the mint-time scope bundle. In particular,
?profile=agent-observability excludes docs even with docs:read; omit the filter or select a
profile that allows docs. See Connect MCP and
Machine-readable documentation.
Project selection and counts
Each row is the minimum that makes the job work end to end. Every one also needs the umbrella already shown.
These recipes assume a project-bound key — what anectico agent bootstrap mints unless you pass
--org-wide. A project-bound key carries its project signed into the credential; it supplies that
project on every call, and an explicit different one is denied. It therefore needs no
projects:read: there is no project for it to discover.
An organization-wide key adds exactly one scope: projects:read. Without a binding, it must pass
a project id to every project-addressed read and write, and list_projects is the only way an agent
finds one. That is the whole difference — everything else in each row is the same. Counts below are
for the project-bound form, derived from the current capability catalog and registration rules on
2026-10-10. Server tests pin the published bundle and served-tool counts, but catalog or profile
changes can alter them; verify the surface served to your own key before automating against a count.
Read-only jobs (mcp:read)
| Job | Add these scopes | Tools |
|---|---|---|
| Triage an error issue | errors:read logs:read traces:read persons:read services:read |
16 |
| Answer "what happened to this customer" | persons:read persons:profile:read errors:read logs:read traces:read analytics:read replay:read replay:content:read groups:read agents:read |
27 |
| Watch AI agents and their spend | agents:read llm:read |
4 |
| Everything an investigation needs | the investigate bundle |
34 |
The triage row is the smallest genuinely useful read key: it gets search, fetch,
list_issues, get_issue, get_affected_users, search_logs, get_log_patterns,
search_traces, get_trace, get_person_timeline, resolve_person, search_persons,
search_issues and get_service_health, plus the two read-action doors list_read_actions and
execute_read_action. Drop persons:read and it falls to 13: no person
timeline, no identity resolution, and search stops having a people source to search.
The agent-watch row looks too small, and is not. Four tools is right: run metadata is
list_agent_runs, fleet posture is list_agent_fleet, and everything else this key can do is a read
action reached through list_read_actions and execute_read_action, which is why none of it adds
an entry to the tool list. Behind those two doors the same key gets all three LLM-cost capabilities
— the summary, the series and the top spenders — plus list_agent_sessions (which conversations
happened), list_agent_turns (the exchanges within one of them), get_agent_run_graph (one run's
step structure), list_context_events (the context assembly evidence),
get_telemetry_completeness (how current the agent telemetry itself is), and
verify_receipt_chain (whether the agent's action receipts still verify). The same key reads the
same answers from a terminal: anectico agent-sessions list|get|turns, anectico agent-runs graph
and anectico agent-runs completeness. Two scopes are commonly
added to the row, and each should be a decision rather than a reflex:
| Add | What it buys | What it also exposes |
|---|---|---|
agents:content:read |
get_agent_run — one run's detail |
The verbatim transcript: the prompts, completions and tool messages the model exchanged. If the agent under observation handled customer data, this is that data |
persons:read |
get_person_timeline, resolve_person, search_persons — tying a run or a bill to the person behind it |
The people surface generally, and a search that now has a person source |
Neither is required to watch runs or to read spend. Add them when correlation to a person, or reading what a run actually said, is the job.
Jobs that change something (mcp:read + mcp:write)
| Job | Add these scopes | What it unlocks |
|---|---|---|
| Triage and resolve issues | errors:read errors:write logs:read traces:read persons:read |
set_issue_status, snooze_issue, merge_issues, unmerge_issue |
| Take a page and run the response | alerts:read alerts:acknowledge alerts:resolve incidents:read incidents:respond channels:read oncall:read members:read |
acknowledge_alert, unacknowledge_alert, resolve_alert, comment_incident, add_incident_evidence, set_incident_status, plus delivery receipts and who is on call. Add alerts:rules:write for evaluate_alert_rule, which fires and notifies |
| Silence a storm | silences:read silences:write silences:delete |
create_silence, update_silence, delete_silence |
| File a ticket from an issue | connections:read errors:read tickets:read tickets:write |
create_ticket, list_tickets_for_issue, get_ticket. Add tickets:delete for unlink_ticket |
| Build and maintain dashboards | dashboard:read dashboard:write dashboard:delete |
the seven dashboard and widget actions. dashboard:delete covers delete_dashboard and delete_widget. A person opens the result as a proof page |
| Wire feature flags or product events into an application and check the sample | docs:read projects:read query:read analytics:read, plus flags:read for flags. Add flags:write for an approved flag, api_key:read and api_key:write for keys (the key's creator must also hold the scope the new key carries: analytics:write for a capture key, flags:read for the flags key), projects:write for a new project, and persons:read, tracking_plans:read and agents:content:read for the identity, plan and property checks |
verify_integration, verify_integration_events, verify_flag_integration, create_flag, create_intake_key, create_api_key, revoke_api_key, create_project. Without persons:read, tracking_plans:read or agents:content:read, the check that needs it is withheld, never absent. See the integrate skill |
| Build a program you host on the REST API (a status board, support page, bot or nightly script) | docs:read mcp:read projects:read services:read to read the contract, the guides and the evidence. The program's own key is a separate project key with only what it calls: services:read for a board, persons:read for a support page, oncall:read projects:read members:read for an on-call bot, errors:read for the nightly issues, and analytics:read persons:read with the result's source scopes for a saved result. Add api_key:read and api_key:write (and mcp:write over MCP) to create or revoke that key, which also needs every scope the new key carries; activity:read and audit:read to watch it |
get_rest_contract, search_docs, get_doc, get_service_health, create_api_key, get_api_key, revoke_api_key, get_agent_budget, get_agent_usage, list_agent_activity. The program itself calls REST and never MCP. See the build-an-app skill |
| Export data for offline analysis | export:read export:write export:download plus the read scope of every signal exported (logs:read, traces:read, …) |
create_export_job, export_logs, cancel_export_job, retry_export_job, and the three export reads |
| Delete exact telemetry on request | logs:delete, traces:delete or errors:delete — project-bound only |
delete_log, delete_trace, delete_error_group. Add persons:read for delete_person_errors, which erases one person's errors under every identity they have. An org-wide key is not served these at all |
| Publish a live screen | live:read live:write live:publish plus the read scope of every panel's source (analytics:query, analytics:read, insights:read, persons:read, errors:read, services:read, alerts:read, logs:read) |
create_live_screen, preview_live_screen, publish_live_screen, and the pause, resume, rotate and revoke actions |
Export is the row where a key most often "sees a tool it cannot call", and it is deliberate.
export:write alone commissions nothing: creating a job also requires the exported signal's own read
scope, so a key cannot export data it may not read. retry_export_job re-checks that against the
stored job, so a key that has since lost the read scope cannot regenerate the file. And
get_export_download re-checks it and requires being the job's creator or an organization owner
or admin — so a key with only export:write can create a job and can neither poll it nor download
it, and a key that did not create a job is answered as though the job does not exist.
Publish a live screen
A live screen is published on a project API key and keeps running
on that key's current permissions, rechecked on every refresh. The key therefore needs the Live
scopes and the read scope behind every panel it publishes — publishing without one is refused by
name, and losing one later turns that panel into Denied on the display rather than removing it.
The full recipe, for a key that composes a screen from any template and publishes it:
anectico agent bootstrap --host claude --name 'live publisher' --scope-profile '' \
--scope mcp:read --scope mcp:write \
--scope live:read --scope live:write --scope live:publish \
--scope analytics:query --scope analytics:read --scope insights:read \
--scope persons:read --scope errors:read \
--scope services:read --scope alerts:read --scope logs:read
That is thirteen scopes: the two umbrellas, the three Live scopes, and the eight source reads. Trim
the source reads to the panels you actually publish — a screen of saved-insight trends and nothing
else needs analytics:query, analytics:read, insights:read and persons:read beside the Live three.
Two scopes in that list do more than they look like they do, and both are deliberate:
| Scope | Why a publisher needs it |
|---|---|
persons:read |
Trend panels require it alongside the analytics and saved-insight permissions. People-affected panels require errors:read |
live:read |
publish_live_screen replaces the revision a link is already serving, and the revision about to be replaced can only be learned by reading the screen first |
live:publish is the scope to withhold by default. live:write builds screens and shows nobody
anything; live:publish mints a link that anyone can open without signing in. A key that drafts
screens for review does not need it. And because live:publish is not a :read scope, a key frozen
to its read permissions stops serving its display — which is the intended behaviour, not a bug to
work around.
An agent connected over OAuth rather than an API key can read live screens and can never publish one,
at any role. A person lists and revokes Live links in the Console, in Published links
(/published-links). See Live viewing links and access.
A signed-in member session can publish through MCP or the CLI with the required Live and source
scopes and api_key:write. Confirmation creates a dedicated project publisher key; the viewing
link uses that key's authority after the session ends. Delegated OAuth remains ineligible.
What to add, and why
projects:read, only for an organization-wide key. It buys exactly one capability,
list_projects, which is how an unbound agent discovers the project ids the rest of the surface
takes as an argument — without it such a key fails on the first call an agent makes. A project-bound
key gains nothing from it: adding projects:read to the customer recipe above changes the tools
served from 27 to 27. It is in the investigate bundle because that bundle has to work either way.
The read that matches the write. A write scope without its matching read scope is what turns an
ordinary edit into an unrecoverable one: the credential cannot capture what it is about to
overwrite, so the preview can only tell you it is unable to show you — and the action is graded
destructive for precisely that reason. escalation:write without escalation:read cannot list the
policy it is about to replace. evals:write without evals:read can replace a sampling rule
or a budget it never saw, and setting a rate to zero turns automatic evaluation off. If an agent should be able to
undo its own edits, give it the read.
A content scope, only when verbatim customer content is genuinely needed. persons:profile:read
(profile properties), replay:content:read (session playback and its console and network payloads)
and agents:content:read (prompts, completions, tool arguments, reasoning transcripts) each name
recorded content rather than a summary of it. The investigate bundle grants all three, because
that detail is what makes an investigation possible — if an agent should not see it, name scopes
individually instead of taking the bundle. traces:read, logs:read and metrics:read used to be
second routes to model prompts and completions recorded as attributes by third-party
instrumentation, and are not any more: those attributes are withheld from a credential without
agents:content:read — on spans, on log records (including the live tail) and on metric exemplars —
and the response comes back carrying an anectico.content.withheld attribute naming what was
removed. An agent with the signal read scopes and no content scope reads latency, status, token
counts and log messages, never model bodies.
Your project's content policy can withhold the same bodies from an agent that does hold
agents:content:read. Those responses carry anectico.content.policy_withheld instead, and the
difference matters to whoever is debugging: the first is fixed by minting a key with the content
scope, the second only by changing the project's policy. One more thing to know before you blame a
key: a content policy belongs to a project, so a read that does not name one — the MCP trace and log
searches on an organization-scoped key, or an API call without project_id — comes back without
content for that reason alone. Name the project and the bodies return.
A delete scope, one at a time and never by reflex. --scope logs:delete, --scope traces:delete, --scope dashboard:delete, and so on. Typing it out is the point.
Nothing for the organization itself unless the job is administration. members:write,
projects:write, settings:write, connections:write and api_key:write all belong to the
admin privilege class. A key that investigates does not need them, and the wildcard platform-admin
permission is not grantable to any key.
Narrow a session without minting a key
Adding ?profile=<name> to the endpoint URL narrows one connection. It takes tools away for that
whole connection — an excluded tool is not listed and cannot be called — but it can only ever
subtract from what the key's scopes already permit, and the key's authority is unchanged.
The useful case is turning a write-capable key into a read-only session:
https://app.anectico.com/mcp?profile=investigate
investigate declares no mutation at all, so a write-capable key connecting that way is served no
write gateway whatsoever — an incident key drops from 49 tools to 34, and cannot invoke a mutation
for the life of that connection. Reconnecting without the parameter restores everything, which is
why this is a focus control and not an authorization boundary: the caller chooses it.
Watch for the side effect. investigate also excludes reads that are native to other bundles, so an
incident key connected this way loses get_incident, list_incidents and the on-call and
delivery reads too, despite holding their scopes. For a genuinely narrower authority, mint a
narrower key; see How agent access is enforced for exactly where that
line falls.
- Permission scope reference — the complete scope catalog.
- MCP tool reference — every tool with the scopes it requires.
- How agent access is enforced
Support contact facts
Read and recover selectively recorded contacts with errors:read and persons:read. Recording also requires errors:write; withdrawal also requires errors:delete. MCP requires mcp:read and, for either mutation, mcp:write. Preview, confirmed apply and recovery all require the same current grants. Use a scoped write credential and explicit user approval. See the contact guide.
Read one support requester
Use an OAuth read credential or a scoped read key with persons:read. Add errors:read, traces:read and replay:read only for the evidence sources needed; complaint selection requires errors:read. MCP also needs mcp:read. get_support_story through execute_read_action, and native support story, share the same source coverage. Follow-up content retains its separate permission and current project policy. No write scope or support-provider credential is needed.
Optional issue-impact support evidence uses the same two read grants. get_error_impact
requires errors:read and MCP mcp:read; add persons:read for
include_support_contacts=true. Without it the support block is withheld and baseline
impact remains available. This is a read, with no complaint-write permission or approval.
Recorded support and subject export
Keep the existing read credential for investigation. Select an explicitly scoped API key for an
approved record (errors:write, errors:read, persons:read) or withdrawal (errors:delete,
errors:read, persons:read). MCP adds mcp:read and mcp:write; preview and recovery have the
same current grants. Subject export additionally uses persons:export; inclusion of recorded
support facts needs both reads. These are selected steps, never blanket grants to a triage or
weekly-review session. See the bridge guide.