Browse documentation

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.

Every MCP tool is gated twice, and both gates must pass:

  1. the umbrella for that side of the endpoint — mcp:read for every read, mcp:write for every mutation;
  2. 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.

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 Tools the key is served Mintable by
investigate 20 36 — read-only, no mutation at all any member
incident 38 49 owner or admin
agent-observability 10 14 owner or admin
admin 2 (the two umbrellas) 0 owner or admin

Three properties of that table are decisions rather than accidents:

  • incident contains investigate in full. Incident response is investigation plus a lifecycle surface, not a different way of looking at the same data.
  • agent-observability does not. It is deliberately narrow — agent runs, their transcripts, what they cost, and the person behind them. Add --scope traces:read when the raw spans behind a run are needed.
  • admin refuses to stand alone. It buys the two umbrellas and a statement of intent; the authority is typed out one --scope at a time. anectico apikey create --scope-profile admin with no --scope is 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

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-09-04. 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 14
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 28
Watch AI agents and their spend agents:read llm:read 4
Everything an investigation needs the investigate bundle 36

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. Drop persons:read and it falls to 11: 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). 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, resolve_alert, comment_incident, set_incident_status, plus delivery receipts and who is on call
Silence a storm silences:read silences:write silences:delete create_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
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 and/or traces:delete — project-bound only delete_log, delete_trace. An org-wide key is not served these at all

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.

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 28 to 28. 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 35, 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.