Outbound, approval and identity MCP tools
Reference for the MCP tools that approve or discard nima's drafted messages, set action policies, connect Jira and Linear writers, and link people's accounts.
Eleven MCP tools cover the outbound side of nima: reviewing the messages nima drafted, deciding how much freedom nima has to send them, connecting the credentials nima writes with, and linking people's accounts so nima can address them. This page documents each tool once. The concepts are in Outbound actions and People and identities; the matching REST routes are in Actions, Action policies, Writers and Identities.
Off until a workspace owner or admin turns it on. Outbound dispatch is on for the platform, but each workspace must set dispatchEnabled to true with PATCH /organizations/settings. That route needs a signed-in owner or admin session and there is no admin-panel control for it, so ask the nima team if you cannot make the call. With it off, nima proposes nothing, so the approval tools have nothing to list or decide. Policies, project assertions, writers and identity links can still be set in advance. See Availability.
Who can call the outbound tools
Five of the 11 outbound tools cannot be reached with an API key or an OAuth client today, and the other six need an admin key. Each tool sits behind one of three scopes, and the scopes are not equally obtainable.
| Scope | Tools | How a key gets it |
|---|---|---|
actions:approve | list_pending_actions, get_action, approve_action, discard_action | Nobody can mint it: the key form and the consent screen do not offer it. |
identities:write | link_my_identity (and link_external_identity, which accepts it as an alternative to integrations:write) | Nobody can mint it. |
integrations:write | set_action_policy, assert_project_audience, connect_jira_writer, connect_linear_writer, list_unlinked_identities, link_external_identity | A workspace owner or admin runs /nima mcp-key integrations in Slack and confirms. The key lasts 30 days and is shown once. |
The consequence is plain. list_pending_actions, get_action, approve_action, discard_action and link_my_identity exist and are registered, but no key or OAuth client can hold the scope they need. Approve and discard on the Slack approval card instead. A signed-in browser session skips scope checks, which is why the REST routes behind these tools work for first-party callers.
tools/list shows every tool behind a consent-screen scope even when your token lacks it. It shows a tool behind integrations:write, actions:approve or identities:write only when your token holds that scope. If a call returns "This tool requires an explicit headless grant", ask an owner or admin for an integrations key for the six integrations:write tools. For the approval tools and link_my_identity, no key works; use the Slack approval card. See Scopes and permissions and the MCP server overview.
List messages waiting for approval with list_pending_actions
list_pending_actions returns the messages nima drafted and is holding for a decision, newest first, paginated. Scope: actions:approve. Route: GET /v1/dispatch/actions (also served at GET /v1/actions).
| Input | Type | Meaning |
|---|---|---|
state | enum | Which actions to list. Default awaiting_approval. |
approvableByMe | "true" or "false" | Default "true": only actions the caller may approve. "false" also lists ones the caller proposed. |
page | integer | From 1. |
limit | integer | 1 to 100. Default 20. |
Each row is an ActionDto: what the action is, where it goes, who could read it, the exact text that would be sent, and when the approval lapses. Every caller, admins included, sees only the actions they proposed or may approve. The org-wide list (all=true) is REST-only and needs owner or admin rights; see Actions. Questions nima asks a teammate on your behalf (brokered questions) are never listed over MCP or with a key; approve those on the Slack card.
Read one pending message with get_action
get_action returns one action in full, by id. Scope: actions:approve. Route: GET /v1/dispatch/actions/{id} (also GET /v1/actions/{id}). Input: id, a UUID from list_pending_actions.
Use it before approve_action when an approval card says it could not show the whole message. nima refuses to draft a message longer than 9,000 characters for approval, so the full text is always available here. Only an integration admin, the person who proposed the action, or an eligible approver can read it. Anyone else gets 404, the same answer as for an id that does not exist.
Approve a pending message with approve_action
approve_action approves a message nima drafted so that nima sends it. Scope: actions:approve. Route: POST /v1/actions/{id}/approve. Input: id (UUID). It returns { actionId, decision: "approved" }.
An approval lapses 24 hours after nima asks. Before sending, nima re-checks that the stored text is still what the approver saw, so an edited row is refused instead of sent. Failures:
| Status | Cause |
|---|---|
404 | No action with that id in your workspace. |
403 INSUFFICIENT_PERMISSIONS | You are not an eligible approver of this action, or no longer a member of the workspace, or the action is a brokered question, which cannot be approved over MCP or with a key. |
409 details.reason: "expired" | The 24-hour window has closed. |
409 details.reason: "already_decided" | Someone approved or discarded it already, or the stored text no longer matched. |
409 details.reason: "dispatch_off" | An outbound switch is off. details.switch is flag_off, org_off or binding_disabled. The action keeps waiting and can still be discarded. |
Discard a pending message with discard_action
discard_action throws away a drafted message so nima never sends it. Scope: actions:approve. Route: POST /v1/actions/{id}/discard. Input: id (UUID). It returns { actionId, decision: "discarded" }.
Discarding is allowed while the outbound switch is off, which is how an admin clears the queue. The errors are those of approve_action except dispatch_off: 404, 403, and 409 with details.reason expired or already_decided.
Set an action policy with set_action_policy
set_action_policy decides whether nima may send a kind of message never, only after approval, or automatically, at workspace, project or rule scope. Scope: integrations:write. Route: PATCH /v1/dispatch/policies. It needs a live owner or admin.
| Input | Type | Meaning |
|---|---|---|
toolName | string, 1 to 80 | The effect tool the rule governs. Today only reply_on_ticket (Jira replies) and send_dm (Slack DMs) are ever proposed, so those are the two worth setting. The other registered tools are accepted too, but nothing sends through them yet. A name that is not registered returns 400 with the list of known names. |
scope | org, project or subscription | org is the whole workspace, project one tracker project, subscription one rule. |
provider, projectKey | string | Required for project, for example jira and ENG. The key is case-insensitive. |
subscriptionId | UUID | Required for subscription. Must belong to your workspace. |
rung | never, ask, auto or remove | never forbids it, ask needs a person's approval each time, auto sends without approval where every other safety rule allows it, remove deletes this rule so the broader one applies. |
askerIsMember | boolean | For auto: only when the person who asked is a verified member. |
dailyCap | integer 0 to 1000, or null | Most per target per rolling 24 hours. null or omitted sets no cap in this rule; ticket replies then fall back to 3 per ticket per day. Slack DMs keep their own per-person limits. |
perTargetCooldownS | integer 0 to 604800, or null | Seconds between two on the same target. null or omitted sets no cooldown in this rule; ticket replies then fall back to 600. |
Without a rule, send_dm sends automatically and reply_on_ticket asks. A rung above the tool's maximum is refused with 400, not lowered. reply_on_ticket can reach auto; send_dm can too. Setting auto on a ticket reply does not by itself make replies automatic: the project must also be asserted internal (see below), the asker must be a verified member, Jira must write through a service account (not the connector fallback), and no outside text can be in play. Writing a value that is already stored changes nothing and returns changed: false. Two admins changing the same rule at once get 409; retry. Every change is audited.
The response is { toolName, scopeKind, scopeRef, rung, dailyCap, perTargetCooldownS, conditions, changed }. There is no MCP tool that lists policies. GET /v1/dispatch/policies does, for a session or an integrations:write key.
Mark a project internal with assert_project_audience
assert_project_audience tells nima that everyone who can read a tracker project is inside your workspace, or withdraws that statement. Scope: integrations:write. Route: POST /v1/dispatch/project-audience. Owner or admin only.
| Input | Type | Meaning |
|---|---|---|
provider | string, 1 to 40 | The tracker, for example jira. |
projectKey | string, 1 to 80 | The project key. Case-insensitive. |
internal | boolean | true asserts it, false withdraws it. |
Until a project is asserted internal, nima treats it as possibly visible to outsiders, and no reply there can go auto. A workspace can assert at most 100 projects; the 101st returns 400. Repeating the same assertion changes and audits nothing.
Connect a Jira writer with connect_jira_writer
connect_jira_writer shows the status of the credential nima uses to comment on Jira and connects the fallback mode. It never takes a token. Scope: integrations:write. Route: POST /v1/integrations/jira/writer/mcp.
| Input | Type | Meaning |
|---|---|---|
mode | service_account or connector_fallback | Which credential nima comments with. |
acknowledged | boolean | connector_fallback only. true accepts that comments appear as the person who connected Jira. Without it the writer stays disabled. |
enabled | boolean | Turn the writer on or off. |
The result is { status, instructions }. In service_account mode, comments come from a dedicated Atlassian account, which is the preferred setup. Send that account's email and an API token to POST /v1/integrations/jira/writer from a signed-in session or an integrations:write key; the tool refuses to carry a credential. Asking for service_account before one is connected returns the current writer status (null if no writer exists) plus instructions, and changes nothing. Enabling or switching the writer returns 404 if Jira is not connected. The token needs the scopes write:comment:jira, write:comment.property:jira, read:comment.property:jira and read:comment:jira; the status lists any missingScopes.
In connector_fallback mode, comments come from the connecting user, every reply needs approval, and nima refuses an unapproved one. See Jira.
Connect a Linear writer with connect_linear_writer
connect_linear_writer lets nima reply on Linear issues as the person who connected Linear. Scope: integrations:write. Route: POST /v1/integrations/linear/writer. Linear must already be connected, or the call returns 404.
| Input | Type | Meaning |
|---|---|---|
acknowledged | boolean, required | true accepts that replies appear as the connecting user and that every reply waits for approval. Without it the writer stays disabled. |
enabled | boolean | Turn the writer on or off. Defaults to on when acknowledged. |
The result is { mode: "connector_fallback", enabled, needsAcknowledgement, acknowledgedAt, revision, connectionInvalid }. Linear replies are always approval-only; they can never be auto. Enabling the writer does not yet make nima comment on Linear: no rule or conversation proposes a Linear reply today. See Linear.
List accounts nima cannot match with list_unlinked_identities
list_unlinked_identities returns external accounts that nima has seen but not matched to a workspace member, plus pending self-claims, newest first. Scope: integrations:write. Route: GET /v1/identities/unlinked. Input: limit, 1 to 100, default 50.
Each row carries id, provider, providerScope, externalId, userId (null while unlinked), displayName, linkedVia, assurance, and claimedBy (userId and name) for a pending claim. It never returns an email. Use it when nima reported that an assignee or reporter is not linked.
Link an account to a member with link_external_identity
link_external_identity links an external account to a workspace member. Scope: integrations:write or identities:write; the route also needs owner or admin rights. Route: POST /v1/identities/link.
| Input | Type | Meaning |
|---|---|---|
provider | slack, jira, linear or notion | The integration the account lives in. |
providerScope | string, 1 to 200 | The installation: Slack team id, Jira cloudId, Linear organization id or Notion workspace id. |
externalId | string, 1 to 200 | The account id: Slack user id, Atlassian accountId, Linear user id or Notion user id. |
userId | UUID | The nima user. Must be a current member, or the call returns 400. |
An admin link has high assurance. It also resolves a pending self-claim and replaces any existing link of that account to another member. Without that link, nima cannot turn a person's name into a mention or a direct message. Mentions in outbound messages also need dispatch.mentionsEnabled, which is off by default and is set with PATCH /organizations/settings from a signed-in owner or admin session (or ask the nima team).
Link your own account with link_my_identity
link_my_identity links an external account to the caller. Scope: identities:write. Route: POST /v1/identities/link-me. It takes provider, providerScope and externalId as above, plus an optional linkToken (string, 1 to 2000).
The link is applied only when nima can prove the account is yours: the account's email equals your nima email, or you pass a valid signed linkToken. Otherwise nima records a pending claim, and an admin decides with link_external_identity. It links only the caller, and it cannot take an account another member already holds or has a pending claim on (409). A self-link has low assurance: it can receive nudges but never lowers an approval requirement. No flow issues link tokens yet, so in practice a self-link works by email match or becomes a pending claim.
ActionDto fields
ActionDto is the object list_pending_actions and get_action return for one drafted message. It never contains the provider payload or the raw tool input.
| Field | Meaning |
|---|---|
id | Action id. Use it with get_action, approve_action and discard_action. |
toolName, adapterKey | What the action is and which channel sends it. |
proposerKind | What proposed it. |
destination | kind, provider and issueKey (null when not a ticket). |
audience | Who could read the message. |
rung | ask or auto, or never on a refused action. |
state | One of proposed, awaiting_approval, approved, executing, succeeded, failed, uncertain, discarded, expired, refused. |
preview | The exact text that would be sent, or null. |
contextExcerpt | The question or comment being answered, sanitised, at most 600 characters, or null. |
receiptNotes | Notes such as an unlinked mention. |
eligibleApproverIds, approverUserId | Who may approve, and who did. |
approvalExpiresAt | When the approval lapses (ISO 8601), or null. |
refusedReason | Why nima refused to send, or null. |
externalUrl | Link to the sent message, or null. |
createdAt, executedAt | ISO 8601 timestamps; executedAt is null until sent. |
Agent operation and publishing MCP tools
Reference for the MCP tools that start and control agent operations, configure hosted compute and manage GitHub publishing, plus the tools only a nima VM can call.
Integrations
Every tool nima connects to, what each brings in or writes out, its status, and how connecting and disconnecting work.