Agents
Scopes & tools
The nine agent scopes, the MCP tools they unlock, and how writes, retries and approvals behave.
Scopes are resource:action pairs and deny by default. An agent holds
exactly the pairs it was created with; the presets in the console are shortcuts
that expand to these pairs and are never stored as a level of their own.
Scopes
| Scope | Grants | Read-only | Assistant | Operator |
|---|---|---|---|---|
contacts:read | Read contacts | ✓ | ✓ | ✓ |
contacts:write | Edit contact fields | ✓ | ||
deals:read | Read deals | ✓ | ✓ | ✓ |
deals:write | Move deals between stages | ✓ | ||
tasks:read | Read tasks | ✓ | ✓ | ✓ |
tasks:write | Create and update tasks | ✓ | ✓ | |
notes:write | Add notes | ✓ | ✓ | |
activity:read | Read recent activity | ✓ | ✓ | ✓ |
email:draft | Draft emails for review | ✓ | ✓ |
There is no email:send scope. Agents draft; a person sends.
Tools
tools/list returns only the tools the agent's scopes allow, always in the
order below, so a client can cache the list. Calling a tool outside the
agent's scopes gets the same -32602 Unknown tool error as a tool that does
not exist, so a client cannot probe for what it was not given.
| Tool | Scope | Kind | What it does |
|---|---|---|---|
search_contacts | contacts:read | read | Find contacts by name, email or company. Returns ids to use with other tools. |
get_contact | contacts:read | read | Read one contact by id, with its recent activity. |
list_deals | deals:read | read | List deals, optionally filtered by stage or contact. |
count_by | contacts:read | read | Count contacts, deals or tasks grouped by a field. |
list_tasks | tasks:read | read | List tasks, optionally filtered by status or assignee. |
recent_activity | activity:read | read | Read the recent activity log for the organisation. |
create_note | notes:write | write | Add a note, optionally linked to a contact or deal. |
create_task | tasks:write | write | Create a task with an optional due date. |
update_task | tasks:write | write | Change a task's title or status. |
update_contact | contacts:write | write | Edit a contact's editable fields. |
move_deal | deals:write | write | Move a deal to another stage. |
draft_follow_up_email | email:draft | write | Propose a follow-up email to one contact for a person to review and send. |
ack_event | none | event state | Confirm receipt of a webhook event so Clivly stops redelivering it. Takes eventId. Does not lift the review hold after a visitor message. |
count_by is offered to any agent holding contacts:read, but it checks the
grant for the dataset you ask about when it runs: an agent with only
contacts:read can count contacts and is refused for deals or tasks.
Annotations
Every tool publishes MCP annotations so your client can decide what to confirm with its user:
- Reads are
readOnlyHint: trueandidempotentHint: true. - Writes are
readOnlyHint: false.update_task,update_contactandmove_dealareidempotentHint: true— setting the same value twice lands on the same state.create_note,create_taskanddraft_follow_up_emailareidempotentHint: false, because a second call would be a second record. - No tool is
destructiveHint: true. No agent tool deletes anything.
Annotations are hints for the client, not the security boundary. Scope checks on the server are the boundary.
Write tool inputs
Every write takes an idempotencyKey (8–200 characters) alongside its own
fields. Ids are up to 64 characters of letters, digits, _ and -.
| Tool | Fields |
|---|---|
create_note | title (1–200), body (1–10,000), optional contact and/or deal |
create_task | title (1–200), optional dueAt (ISO 8601 date-time), optional contact and/or deal |
update_task | the task, optional title (1–200), optional status: todo, in_progress, done or cancelled |
update_contact | the contact, optional phone (up to 50), status (up to 50), title (up to 200) |
move_deal | the deal, stage (up to 100) |
draft_follow_up_email | the contact, subject (1–200), body (1–10,000) |
Naming records
Each record a write touches can be given by id or the way a person would name it. Pass one or the other, never both:
| Record | By id | By name |
|---|---|---|
| Contact | contactId | contact: a full or partial name, or an email |
| Deal | dealId | deal: its title |
| Task | taskId | task: its title |
So a user can say "add a note to Ada Lovelace" and the agent passes
contact: "Ada Lovelace" without looking anything up first. Clivly matches
from the most specific to the least: id, then email, then exact name
(ignoring case), then part of the name. Archived and merged contacts are never
matched.
- One match: the write goes ahead on that record.
- No match: an error suggests the read tool to search with.
- Several matches: nothing is written. The result is an error that lists up to five candidates, each with an email, company, stage or status to tell them apart, and its id. The agent should ask its user which one they mean, then call again with that id. This costs nothing against the write limit.
Naming a record needs the matching read scope (contacts:read,
deals:read or tasks:read), because the candidates show names and emails.
Without it, pass the id. A proposal waiting for approval stores the id the name
resolved to, so approving it later always changes the record the agent meant.
update_contact refuses fields synced from your app. On a synced contact those
stay as your app wrote them — change them in your app and they update on the
next sync.
Idempotency and retries
A write must carry an idempotencyKey. Use a fresh key for each change you
intend, and the same key when you retry that change.
- A retry with a key that already succeeded replays the first result. It does not write again, and it does not count as a second change.
- A retry while the first attempt is still running is refused with An identical call is already in progress. Retry again with the same key.
- A call that failed releases its key, so retrying the same key runs it again.
Keys are scoped to one agent and one tool, so two agents — or two different tools — never collide on the same key.
Results
A write's outcome is one of three shapes. On success or a pending approval,
the tools/call result carries it as structuredContent, with a readable
summary in content. An error comes back as a tool result with
isError: true and the reason as text, so the model can read it and adjust.
{ "status": "ok", "data": { ... } }
{ "status": "pending_approval", "approvalId": "…", "summary": "…" }
{ "status": "error", "error": "…" }What needs approval
These writes are not made directly. They are recorded as a proposal and return
pending_approval with an approvalId:
move_dealto a Won or Lost stage. Moves between other stages are direct.- Every
draft_follow_up_email. The draft is the proposal; a person reviews and sends it. - Every write while a visitor message is outstanding for this agent — see Limits & security.
A person approves or rejects the proposal under Ask your CRM. Approving applies the change with the arguments the agent sent, re-running every check except the approval rule; if it no longer applies, the proposal stays pending with the reason. See Review what agents propose.
Retrying a call that went to approval replays the same approvalId rather
than queueing a second proposal. To learn what became of an email, subscribe
to draft.decided.
Deal stages
stage is a stage key from the deal's own pipeline. Any other key is
refused with Choose a stage from this deal's pipeline. Whether a stage counts
as Won or Lost comes from the stage's type, not its label, so renaming "Won"
does not let a move past the approval rule. On a deal with no pipeline, the
keys won and lost are the terminal ones.
Email drafts
draft_follow_up_email writes to one contact, who must have an email address,
and the subject must be a single line. A draft whose subject or body contains
a link, or an email address other than the recipient's, is refused rather
than queued: an agent that has read hostile record text must not be able to
slip a link or another address into a message a busy person approves.
Approving a draft proposal does not send it. It becomes a pending draft in the approver's own queue, which they edit and send.