# MCP server

The Assent MCP server address, how clients authenticate, every tool and its arguments, limits and errors.

Assent runs a remote MCP server, so any assistant that speaks MCP can send asks and act on the answers. Every agent connects as itself, so its asks carry its name and the person's rules can treat it on its own. For step-by-step setup in Claude Code, Claude Desktop, ChatGPT, Cursor and Codex, see [Connect Claude, ChatGPT, Cursor or Codex](https://docs.specvista.com/assent/connect-an-assistant.md).

## Connect

```text
https://ordia-api.specvista.com/api/v1/mcp
```

- **Transport**: Streamable HTTP with `POST`. The server keeps no session, so every call stands on its own. `GET` and `DELETE` answer `405`.
- **Agent key**: send `Authorization: Bearer <agent key>`. Keys start with `oak_`; the person creates one under **Agents** in Assent.
- **OAuth**: Claude, ChatGPT, Cursor and Claude Code connect with OAuth 2.1 (authorisation code with PKCE `S256`, dynamic client registration, refresh tokens, scope `agent`). The metadata is at `https://ordia-api.specvista.com/.well-known/oauth-protected-resource` and `https://ordia-api.specvista.com/.well-known/oauth-authorization-server`. Local apps such as Claude Code and the Cursor desktop app return to `http://localhost:<port>/callback` or `http://127.0.0.1:<port>/callback` on your computer, and the sign-in page warns about that. Each connected app gets its own agent, so disconnecting one leaves the others. Other clients use an agent key.
- **Local stdio**: `assent mcp`, from the [command line](https://docs.specvista.com/assent/cli.md), reads MCP messages on stdin and forwards each one to this server with the agent key, for clients that start servers as a command.

## Ask and wait for the answer

| Tool | Arguments | What it does |
|---|---|---|
| `assent_ask` | `kind`, `title`, and optionally `why`, `detail`, `draft`, `url`, `actions`, `items`, `fields`, `group`, `rules`, `quote`, `dueAt`, `remindAt`, `dedupeKey`, `runUuid`, `action`, `target`, `question`, `answerType`, `effortMinutes`, `seriesKey`, `idempotencyKey`, `allowDuplicate` | Ask the person. `kind` is `approve`, `todo`, `pay`, `login`, `code` or `reply`. Give a `why` and a title phrased as an action or question, or it is refused. |
| `assent_wait_for_answer` | `taskUuid`, optional `timeoutSeconds` (1 to 50, default 45) | Wait for the answer. Returns as soon as the ask is no longer open, or with `timedOut: true`; call it again to keep waiting. |
| `assent_get_task` | `taskUuid` | Read an ask you created or were handed, with its status and answer. |
| `assent_answers` | none | Your answered asks that are not picked up yet. |
| `assent_picked_up` | `taskUuid` | Mark an answer as acted on, so it is never delivered again. |
| `assent_resolve` | `taskUuid`, `outcome` (`resolved` or `withdrawn`), optional `note` | Close your own ask or to-do once the work is done or no longer needed. An answered ask cannot be closed; pick it up instead. |
| `assent_check` | `action`, optional `target` and `summary` | Check the person's standing rules before acting: `allow`, `deny` or `ask`, with the rule that decided. |

The answer says what the person chose: `answer.actionId` is the button (`done` for Approve, `decline` for Decline), `answer.selectedItemIds` the items they kept, and `answer.value` the text they sent or typed. A typical approval:

1. `assent_ask` with `kind: "approve"`, a title such as `Deploy the web app?`, a `why`, and `action` and `target` (for example `deploy:web` and `production`) so the person can answer Always or Never.
2. `assent_wait_for_answer` with the returned `uuid`, until it is no longer open.
3. Act on `answer.actionId`, then `assent_picked_up`.

An ask that matches one of the person's Always or Never rules comes back already answered, so the wait returns at once. [Asks](https://docs.specvista.com/assent/asks.md) describes every kind and field, and [Rules](https://docs.specvista.com/assent/rules.md) explains how rules match.

## To-dos and notes

| Tool | Arguments | What it does |
|---|---|---|
| `assent_task_add` | `title`, and optionally `why`, `detail`, `url`, `dueAt`, `remindAt`, `dedupeKey`, `runUuid`, `question`, `answerType`, `effortMinutes`, `seriesKey`, `idempotencyKey`, `allowDuplicate` | Add a to-do in Assent for a step the person must take for your work. A date without a time is due at 09:00 in their time zone. |
| `assent_ordia_todo_add` | `title`, and optionally `why`, `detail`, `url`, `dueAt`, `remindAt`, `dedupeKey`, `runUuid`, `effortMinutes`, `seriesKey`, `idempotencyKey`, `allowDuplicate` | Add a durable personal to-do to the person's Ordia Desk (the Specvista notes and tasks app that uses the same account), only for a concrete action they must take. |
| `assent_note` | `title`, `content`, optional `tags`, `runUuid` | Write a note under the agent's name in Assent, for example a run report. |
| `assent_ordia_note_add` | `title`, `content`, optional `tags`, `runUuid` | Add a long-lived personal reference note to the person's Ordia Desk. |
| `assent_note_update` | `noteUuid`, optional `title`, `content`, `tags`, `pinnedAsMemory`, `memoryScope` (`all` or `listed`) | Update a note you wrote, or pin it as memory for the person's other agents. |
| `assent_memory` | none | Read the pinned memory notes the person shared with you. |
| `assent_link` | `from` and `to` (each `{type, uuid}` with type `note`, `task`, `run` or `agent`), `relation` (`produced`, `about`, `blocks` or `related`) | Link two items. |

## Runs, locks, hand-offs and plans

| Tool | Arguments | What it does |
|---|---|---|
| `assent_run_start` | optional `summary`, `maxMinutes` (1 to 1440) | Record that a run started. Keep the returned uuid and pass it as `runUuid`. |
| `assent_run_finish` | `runUuid`, `status` (`ok` or `fail`), optional `exitCode`, `summary`, `logTail` | Finish a run. Emails, keys, phone numbers and home paths are redacted. |
| `assent_lock_acquire` | `resource` (lower case, for example `deploy:web`), `ttlMinutes` (1 to 720) | Take a shared lock. Returns `acquired: false` with the holder when another agent has it; do not go ahead then. |
| `assent_lock_release` | `resource`, `token` | Release a lock with the token `assent_lock_acquire` returned. |
| `assent_handoff` | `toAgentName` or `toAgentUuid`, `title`, optional `detail`, `runUuid` | Hand work to another of the person's agents. |
| `assent_handoffs` | none | Open work other agents handed to you. |
| `assent_task_complete` | `taskUuid` | Mark a hand-off you received as done. |
| `assent_plan_create` | `title`, `tracks`, `sessions`, optional `summary`, `nudge` (`off`, `morning` or `each`), `idempotencyKey` | Create a plan of dated sessions grouped into tracks, up to 1000 sessions. |
| `assent_plan_get` | `planUuid` | Read back a plan you created. |
| `assent_plan_update_sessions` | `planUuid`, `sessions` (each with `key` and optional `dueAt`, `title`, `detail`, `status`) | Move, retitle or mark sessions `open`, `done` or `cancelled`, up to 500 a call. |

Dates (`dueAt`, `remindAt`) take a date and time with an offset, such as `2026-10-15T17:00:00+01:00` or `2026-10-15T16:00:00Z`, or a bare date such as `2026-10-15`, which means 09:00 that day in the person's time zone.

## Limits

- `assent_wait_for_answer` waits at most 50 seconds per call. Each agent can wait on 4 asks at once and each account on 10; more returns status `429` with "Too many waits at once for this agent or account. Try again shortly."
- Requests count towards the agent key's per-minute rate limit, the same as the [HTTP API](https://docs.specvista.com/assent/api.md).

## Errors

A failed call comes back as a tool error whose text is `{"error": message, "status": code}`:

| Status | Meaning |
|---|---|
| `400` | Invalid arguments. The message names the argument, for example `Invalid arguments: kind: ...`. |
| `401` | No agent key or OAuth token, or it was rotated. This comes back as an HTTP `401` before any tool runs. |
| `403` | The agent is paused, or attachments are off for it. |
| `404` | The ask, run or note does not exist or belongs to another agent, or the tool name is unknown. |
| `409` | Already answered, closed or expired. Read the ask again. |
| `422` | The ask has no `why` or no clear question. The message says what to add. |
| `429` | Too many waits or requests at once. Try again shortly. |
