# MCP
URL: /docs/cloud/api/mcp

Connect an MCP client to Assistant Cloud project reads.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

Assistant Cloud exposes a stateless Model Context Protocol endpoint for agents that need to inspect a project's runs, usage, threads, scores and topics. It uses the backend host and an API key, and the API key strategy still requires `Aui-Workspace-Id` even though the tools scope their reads to the project.

## The MCP endpoint

```
POST /v1/mcp
```

| Request item       | Required | Rules                                                                                                                                          |
| ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `Authorization`    | yes      | A bearer API key.                                                                                                                              |
| `Aui-Workspace-Id` | yes      | Required by the API key strategy.                                                                                                              |
| `Accept`           | no       | When sent, it must include `application/json`, `text/event-stream` or `*/*`, or the transport answers `406`. A missing header counts as `*/*`. |
| HTTP method        | yes      | Only `POST` is accepted.                                                                                                                       |
| Transport          | yes      | Stateless streamable HTTP with JSON responses.                                                                                                 |

The server identifies itself as `{ name: "assistant-cloud", version: "1.0.0" }`. It creates no MCP session because the transport is configured without a session id generator. Every exposed tool has `annotations: { readOnlyHint: true }`.

```
POST /v1/mcp
Authorization: Bearer sk_aui_proj_…
Aui-Workspace-Id: usr_000000000000000000000000
Content-Type: application/json
Accept: application/json, text/event-stream

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "list_runs", "arguments": { "limit": 50 } }
}
```

```
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"runs\":[],\"next_cursor\":null}"
      }
    ]
  }
}
```

The body is a JSON-RPC 2.0 message as the MCP streamable HTTP transport defines it; `initialize` and `tools/list` use the same envelope, and the tool sections below show only the `result` each tool returns.

The successful tool result is `{ content: [{ type: "text", text: JSON.stringify(result) }] }`. A tool failure adds `isError: true` and serializes `{ error: message }` as the text result. Only the messages in this table pass through unchanged. Every other tool failure becomes `Request failed`.

| Error message                                | Meaning                                                  |
| -------------------------------------------- | -------------------------------------------------------- |
| `Invalid cursor`                             | A thread or score cursor is not valid.                   |
| `Invalid run cursor`                         | A run cursor is not valid.                               |
| `Invalid usage day range`                    | A usage date is not a real UTC day.                      |
| `Run not found`                              | The requested run is not visible in the project.         |
| `Thread not found`                           | The requested thread is not visible in the project.      |
| `Usage day range must span at most 366 days` | The usage range is negative or spans more than 366 days. |

| Status | Body or headers                                                                              | When                                                                                  |
| ------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `200`  | A JSON MCP response                                                                          | The transport returns a tool result. Tool failures use the `isError` result shape.    |
| `401`  | An authentication error object.                                                              | The authorization header is missing, malformed or expired.                            |
| `403`  | `{ "error": "This endpoint requires an API key" }`                                           | The request does not use an API key.                                                  |
| `405`  | Empty body with `Allow: POST`                                                                | The request uses a method other than `POST`.                                          |
| `406`  | A JSON-RPC error, `Not Acceptable: Client must accept application/json or text/event-stream` | An `Accept` header was sent without `application/json`, `text/event-stream` or `*/*`. |
| `500`  | Empty body                                                                                   | The transport returns no response.                                                    |

The endpoint and all eight tools are read only. They do not create sessions or write project data, so repeating a successful read is idempotent. A client should send the same backend URL, API key and workspace header on every request.

## `list_runs`

List runs in the authenticated project. System runs are excluded, as they are from the project run read.

| Input          | Type              | Required | Default | Rules                                |
| -------------- | ----------------- | -------- | ------- | ------------------------------------ |
| `status`       | enum              | no       |         | One of the API run status values.    |
| `outcome_type` | enum              | no       |         | One of the API run outcome values.   |
| `model_id`     | string            | no       |         | 1 to 255 characters.                 |
| `provider`     | string            | no       |         | 1 to 255 characters.                 |
| `created_by`   | string            | no       |         | 1 to 255 characters.                 |
| `thread_id`    | string            | no       |         | 1 to 48 characters.                  |
| `environment`  | string            | no       |         | 1 to 64 characters.                  |
| `release`      | string            | no       |         | 1 to 255 characters.                 |
| `tag`          | string            | no       |         | 1 to 64 characters.                  |
| `from`         | ISO 8601 datetime | no       |         | Converted to a date value.           |
| `to`           | ISO 8601 datetime | no       |         | Converted to a date value.           |
| `limit`        | integer           | no       | 50      | Coerced to an integer from 1 to 100. |
| `cursor`       | string            | no       |         | 1 to 512 characters.                 |

```
{ "limit": 50 }
```

```
{ "runs": [], "next_cursor": null }
```

Results are ordered by `created_at DESC, id DESC`. The cursor is base64url encoded JSON containing `created_at` as an ISO value and `id`. Pass `next_cursor` unchanged to the next call. An invalid cursor produces the `Invalid run cursor` tool error.

## `get_run`

Get one run and its spans from the authenticated project.

| Input    | Type   | Required | Rules               |
| -------- | ------ | -------- | ------------------- |
| `run_id` | string | yes      | 1 to 48 characters. |

```
{ "run_id": "run_000000000000000000000000" }
```

```
{ "id": "run_000000000000000000000000", "spans": [] }
```

The result is the project run object with `spans`. Spans are ordered by `start_ms ASC, span_index ASC`. This tool does not use a cursor. A missing run produces the `Run not found` tool error.

## `usage_daily`

Get daily usage for the authenticated project.

| Input      | Type         | Required | Default       | Rules                                                             |
| ---------- | ------------ | -------- | ------------- | ----------------------------------------------------------------- |
| `from`     | `YYYY-MM-DD` | no       | 30 day window | Must be a real UTC calendar day. Defaults to 29 days before `to`. |
| `to`       | `YYYY-MM-DD` | no       | Today in UTC  | Must be a real UTC calendar day.                                  |
| `group_by` | enum         | no       |               | The only value is `model`.                                        |

```
{ "from": "2026-09-01", "to": "2026-09-17", "group_by": "model" }
```

```
{
  "days": [
    {
      "day": "2026-09-17",
      "provider": null,
      "model_id": null,
      "runs": 0,
      "errors": 0,
      "input_tokens": 0,
      "output_tokens": 0,
      "total_tokens": 0,
      "cost_usd": 0
    }
  ]
}
```

Without `group_by`, every day in the range is returned with zero filled values. With `group_by=model`, rows are grouped by day, provider and model as stored, with no zero fill, and `provider` or `model_id` can be `null`. `runs` counts `kind = run` rows. `errors` counts `kind = run` rows with `status = error`. Token and cost sums include `kind = sampling` rows. This tool has no cursor. An invalid date produces `Invalid usage day range`, and an invalid span produces `Usage day range must span at most 366 days`.

## `usage_period`

Get the current usage period for the authenticated project.

| Input     | Type   | Required | Rules                      |
| --------- | ------ | -------- | -------------------------- |
| No fields | object | yes      | The input object is empty. |

```
{}
```

```
{
  "plan": "free",
  "period_start": "2026-09-01T00:00:00.000Z",
  "period_end": "2026-10-01T00:00:00.000Z",
  "active_users": 0,
  "included": 200,
  "cap": 200,
  "capped_at": null,
  "thread_titles": {
    "used": 0,
    "completed": 0,
    "included": 500,
    "cap": 500,
    "capped_at": null
  }
}
```

The result is the full usage period object. `period_start` and `period_end` are the UTC calendar month boundaries. `active_users` is the greater of the stored period count and the sum of period users by day. `included` and `cap` come from the limits resolved for the project. This tool has no cursor. A missing project fails with a message outside the passthrough table, so the tool answers `Request failed`.

## `list_threads`

List threads in the authenticated project.

| Input        | Type              | Required | Default | Rules                                |
| ------------ | ----------------- | -------- | ------- | ------------------------------------ |
| `created_by` | string            | no       |         | 1 to 255 characters.                 |
| `from`       | ISO 8601 datetime | no       |         | Converted to a date value.           |
| `to`         | ISO 8601 datetime | no       |         | Converted to a date value.           |
| `limit`      | integer           | no       | 50      | Coerced to an integer from 1 to 100. |
| `cursor`     | string            | no       |         | 1 to 512 characters.                 |

```
{ "limit": 50 }
```

```
{ "threads": [], "next_cursor": null }
```

The project scope includes every workspace, including archived threads. Results are ordered by `created_at DESC, id DESC`. The cursor is base64url encoded JSON containing `created_at` as an ISO value and `id`. Pass `next_cursor` unchanged to the next call. An invalid cursor produces `Invalid cursor`.

## `get_thread`

Get one thread and its messages from the authenticated project.

| Input       | Type   | Required | Rules               |
| ----------- | ------ | -------- | ------------------- |
| `thread_id` | string | yes      | 1 to 48 characters. |

```
{ "thread_id": "thread_0qzof3jPoDwr7K3agyJN3D4U" }
```

```
{ "thread": {}, "messages": [] }
```

The result includes the thread and its messages. Messages are mapped to `ThreadMessageDTO` and ordered by `height ASC, created_at ASC, id ASC`. This tool has no cursor. A missing thread produces `Thread not found`.

## `list_scores`

List scores in the authenticated project.

| Input       | Type    | Required | Default | Rules                                      |
| ----------- | ------- | -------- | ------- | ------------------------------------------ |
| `thread_id` | string  | no       |         | 1 to 48 characters.                        |
| `run_id`    | string  | no       |         | 1 to 48 characters.                        |
| `name`      | string  | no       |         | 1 to 64 characters.                        |
| `source`    | enum    | no       |         | `end_user`, `human`, `evaluator` or `api`. |
| `limit`     | integer | no       | 50      | Coerced to an integer from 1 to 100.       |
| `cursor`    | string  | no       |         | 1 to 512 characters.                       |

```
{ "name": "quality", "limit": 50 }
```

```
{ "scores": [], "next_cursor": null }
```

Scores are ordered by `created_at DESC, id DESC`. Each score contains `id`, `thread_id`, `message_id`, `run_id`, `name`, `data_type`, `value`, `string_value`, `source`, `author_id`, `comment`, `created_at` and `updated_at`; `value` is a number or `null`. The cursor is base64url encoded JSON containing `created_at` and `id`. Pass `next_cursor` unchanged. An invalid cursor produces `Invalid cursor`.

## `list_topics`

List topics and thread counts in the authenticated project.

| Input     | Type   | Required | Rules                      |
| --------- | ------ | -------- | -------------------------- |
| No fields | object | yes      | The input object is empty. |

```
{}
```

```
{ "topics": [] }
```

Each topic has `id`, `name`, `created_at`, `updated_at` and `thread_count`. Topics are ordered by `thread_count DESC, name ASC`. This tool has no cursor.

## Configure a client

Use the MCP URL and both required headers in your client. The Claude Code command is:

```
claude mcp add --transport http assistant-cloud https://backend.assistant-api.com/v1/mcp --header "Authorization: Bearer sk_aui_proj_…" --header "Aui-Workspace-Id: usr_000000000000000000000000"
```

Clients configured through an `mcpServers` file, such as Cursor, use the same values:

```
{
  "mcpServers": {
    "assistant-cloud": {
      "url": "https://backend.assistant-api.com/v1/mcp",
      "headers": {
        "Authorization": "Bearer sk_aui_proj_…",
        "Aui-Workspace-Id": "usr_000000000000000000000000"
      }
    }
  }
}
```

Create a key for this MCP client, scope its use to this tool, give it an expiry and revoke it when the client is no longer needed. The key grants read access to the whole project, so do not reuse it for unrelated access.