> ## Documentation Index
> Fetch the complete documentation index at: https://docs.peeve.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspace endpoint

> GET and POST /api/mcp/{workspace} — the workspace-actor MCP endpoint.

```
GET  https://api.peeve.ai/api/mcp/{workspace}
POST https://api.peeve.ai/api/mcp/{workspace}
```

`workspaceId` is your workspace UUID, from **Settings → Workspace**. Anything
that is not a well-formed UUID returns `404`.

<Warning>
  **This endpoint requires an API token.** Send a teammate's `pv_ut_…` token as
  `Authorization: Bearer`. It previously took no credential; it does now.

  The token's workspace must match the id in the URL. A token for another
  workspace gets the **same `404` as a workspace that does not exist** — the
  endpoint deliberately cannot be walked as an oracle for which workspace ids
  are real, which is also why a mismatch is not a `403`.

  Any role satisfies it (`read`) — the same gate the dashboard applies to
  viewing the console. Which **tools** are reachable is governed by that role;
  see [Roles](/authentication/roles).
</Warning>

## GET — discovery card

A small card for humans and directories.

```bash theme={null}
curl https://api.peeve.ai/api/mcp/00000000-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer pv_ut_xxx"
```

```json theme={null}
{
  "name": "Example Corp · Peeve MCP",
  "protocol": "mcp",
  "protocolVersion": "2024-11-05",
  "transport": "http-jsonrpc",
  "endpoint": "/api/mcp/00000000-0000-4000-8000-000000000000",
  "tools": 12,
  "hint": "POST JSON-RPC here: initialize, tools/list, tools/call."
}
```

Not cached (`Cache-Control: private, no-store`) — it is behind a credential, and a shared cache in front of an authenticated endpoint is how one tenant ends up served another's response.

## POST — JSON-RPC

### `initialize`

```bash theme={null}
curl -X POST https://api.peeve.ai/api/mcp/00000000-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer pv_ut_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {}
  }'
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": { "tools": { "listChanged": false } },
    "serverInfo": { "name": "Example Corp · Peeve", "version": "1.0.0" },
    "instructions": "Verified capabilities for Example Corp. Each tool returns a grounded guide — where the capability lives and how to complete it. Writes are completed by the user in-product, never executed by this server."
  }
}
```

Note `listChanged: false` — the tool list does not push updates. Clients should
re-list rather than wait for a notification.

<Note>
  The `instructions` string is what the server sends **today**, and it describes
  today's behaviour rather than a permanent guarantee. Writes through MCP happen
  in the other direction, via the
  [Custom MCP connector](/mcp/custom-mcp-connector).
</Note>

### `tools/list`

```bash theme={null}
curl -X POST https://api.peeve.ai/api/mcp/00000000-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer pv_ut_xxx" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}'
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "invite_teammate",
        "description": "Invite a teammate to the workspace",
        "inputSchema": {
          "type": "object",
          "properties": { "email": { "type": "string" } },
          "required": ["email"]
        },
        "annotations": {
          "title": "invite_teammate",
          "destructiveHint": false,
          "confirmationRequired": true,
          "readOnlyHint": false
        }
      }
    ]
  }
}
```

The annotations tell a client how to treat each tool:

| Annotation             | Meaning                                                                                    |
| ---------------------- | ------------------------------------------------------------------------------------------ |
| `readOnlyHint`         | The capability only reads or navigates. Safe to act on directly.                           |
| `confirmationRequired` | The user must confirm before it is completed. True for any write and anything destructive. |
| `destructiveHint`      | The capability destroys or irreversibly changes something.                                 |

<Note>
  These annotations describe the capability, not this endpoint. **No tool call
  here executes anything**, whatever its annotations say — they exist so a
  client can present the guidance with the right weight.
</Note>

### `tools/call`

```bash theme={null}
curl -X POST https://api.peeve.ai/api/mcp/00000000-0000-4000-8000-000000000000 \
  -H "Authorization: Bearer pv_ut_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": { "name": "invite_teammate", "arguments": { "email": "sam@example.com" } }
  }'
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Invite a teammate to the workspace\n\nWhere in Example Corp: /settings/team\nPermission: write — needs the user's confirmation\n\nThis changes data. Guide the user to complete it themselves in the product — do not assume it was executed here."
      }
    ],
    "isError": false
  }
}
```

<Warning>
  No invite was sent. The response is guidance for the assistant to relay, and
  the arguments are not acted on. See
  [the overview](/mcp/overview) for why.
</Warning>

An unknown tool name returns `-32602 Unknown tool: {name}`; a missing name
returns `-32602 Invalid params: missing tool name`.

### Batching

```json theme={null}
[
  { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {} },
  { "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
]
```

Returns an array of responses, with notifications omitted. If a batch contains
only notifications, the response is `202` with no body.

## Errors

MCP clients expect protocol errors as `200` responses with a JSON-RPC `error`
member, and that is what Peeve returns — with two exceptions noted below.

| Condition                                             | HTTP    | JSON-RPC code | Message                                         |
| ----------------------------------------------------- | ------- | ------------- | ----------------------------------------------- |
| Unparseable body                                      | 200     | `-32700`      | Parse error                                     |
| Missing `method`                                      | 200     | `-32600`      | Invalid request: missing method                 |
| Unknown method                                        | 200     | `-32601`      | Method not found: …                             |
| Bad or missing tool name                              | 200     | `-32602`      | Unknown tool: …                                 |
| Missing token, or not a live API token                | **401** | —             | Unauthenticated                                 |
| Token for a different workspace, or no such workspace | **404** | —             | Identical refusal for both, deliberately        |
| Role cannot use MCP                                   | **403** | —             | Insufficient role                               |
| Not entitled, disabled or killed                      | 200     | `-32001`      | Agent channel not available for this workspace. |
| **Rate limited**                                      | **429** | `-32002`      | Rate limited — slow down.                       |
| **Platform unavailable**                              | **503** | `-32001`      | Agent channel not available for this workspace. |

Rate limiting is a real `429` with a `retry-after` header on purpose, so a
client backs off properly instead of retrying into the wall.

<Note>
  A `-32001` does not distinguish "no such workspace" from "not entitled" —
  both look the same from outside. If you are debugging, check in order: the
  workspace id is a valid UUID; the plan is Growth or above; the agent channel
  is enabled; the kill switch is off.
</Note>

## Rate limits

Bucketed per workspace and per credential, with ceilings that follow your plan;
see [Rate limits](/reference/rate-limits).

The limiter runs **before** the capability projection, so a shed request costs
nothing.

Like every limiter in Peeve it fails open: if the limiter itself is unavailable,
requests are allowed rather than blocked.

## A friendly hostname

The endpoint is advertised as `mcp.peeve.ai/{workspace}`, which rewrites to
this route. Both work; use whichever you prefer when handing the URL to someone.
