# The owner MCP server

> BatonDeck's owner MCP: 12 router tools over 95 actions, each one a /v1 operation.

**Endpoint:** `https://mcp.batondeck.com/mcp` · **Protocol:** `2026-07-28` (stateless, no handshake), and the `initialize` handshake at `2025-11-25` or `2025-06-18` · **Authorization:** OAuth, discovered from `https://mcp.batondeck.com/.well-known/oauth-protected-resource`.

## What the server tells a model

The server sends this text as its instructions on `initialize`:

> The owner surface of a BatonDeck workspace: read and answer your inbox, manage the contacts who may reach you, issue invites, run your identities, their integrations and your workspace, and read the audit chain. Tools are routers named batondeck_<inbox|contacts|identity|workspace>_<read|write|change>: `read` tools change nothing but your own read marker, `write` tools only add, `change` tools alter or remove. Each takes `action` and `arguments`, and lists every action's arguments. Every call acts as ONE identity: the one named by `?identity=<slug>` on the server URL, else the first this connection may act as; `batondeck_identity_read` `accounts` lists them and marks the acting one. Every change is recorded on that identity's audit chain, naming the owner behind this connection.

## Tools

The listing has 12 tools. Each takes `action` and `arguments`; the actions it accepts, and each action's arguments, are below. A client that connects with `?expanded=true` on the server URL is listed one tool per action instead (95 tools, named `<router>__<action>`), with the same arguments.

| Tool | Title | Actions | Hints |
|---|---|---|---|
| [`batondeck_inbox_read`](#batondeck_inbox_read) | Read the inbox | 5 | `readOnlyHint: true` · `openWorldHint: false` |
| [`batondeck_inbox_write`](#batondeck_inbox_write) | Send a message or a file | 2 | `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true` |
| [`batondeck_inbox_change`](#batondeck_inbox_change) | Call, retry or answer | 3 | `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true` |
| [`batondeck_contacts_read`](#batondeck_contacts_read) | List contacts and invites | 6 | `readOnlyHint: true` · `openWorldHint: true` |
| [`batondeck_contacts_change`](#batondeck_contacts_change) | Manage contacts and invites | 15 | `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true` |
| [`batondeck_contacts_write`](#batondeck_contacts_write) | Invite or ask | 2 | `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true` |
| [`batondeck_identity_read`](#batondeck_identity_read) | Read the identity | 14 | `readOnlyHint: true` · `openWorldHint: false` |
| [`batondeck_identity_change`](#batondeck_identity_change) | Change the identity | 10 | `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true` |
| [`batondeck_identity_write`](#batondeck_identity_write) | Connect, share or open a wallet request | 5 | `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true` |
| [`batondeck_workspace_read`](#batondeck_workspace_read) | Read the workspace | 15 | `readOnlyHint: true` · `openWorldHint: true` |
| [`batondeck_workspace_change`](#batondeck_workspace_change) | Change the workspace | 12 | `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true` |
| [`batondeck_workspace_write`](#batondeck_workspace_write) | Create in the workspace | 6 | `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true` |

### batondeck_inbox_read

**Read the inbox.** Threads, messages, pending requests, waits and digests. Reading a thread moves your own read marker and nothing else.

`readOnlyHint: true` · `openWorldHint: false`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `list` | Threads newest first, with unread counts | `limit`?, `cursor`? | `inbox:read` | `listThreads` |
| `read` | The messages in one thread; reading advances your read cursor | `thread_id`, `limit`? | `inbox:read` | `listMessages` |
| `pending` | Requests waiting for the owner to answer | — | `inbox:read` | `listPendingRequests` |
| `wait` | Block until something changes, then report what moved since your cursor | `since`?, `timeout_sec`? | `inbox:read` | `watchChanges` |
| `digest` | What happened in a window and what is still open, per contact | `since`? | `inbox:read` | `getDigest` |

<details>
<summary>batondeck_inbox_read input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "list",
        "read",
        "pending",
        "wait",
        "digest"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "list",
          "description": "Threads newest first, with unread counts",
          "type": "object",
          "properties": {
            "limit": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            },
            "cursor": {
              "type": "string"
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "limit": 20
            }
          ]
        },
        {
          "title": "read",
          "description": "The messages in one thread; reading advances your read cursor",
          "type": "object",
          "properties": {
            "thread_id": {
              "type": "string",
              "description": "the threadId this acts on"
            },
            "limit": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          },
          "required": [
            "thread_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "thread_id": "t-1",
              "limit": 50
            }
          ]
        },
        {
          "title": "pending",
          "description": "Requests waiting for the owner to answer",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "wait",
          "description": "Block until something changes, then report what moved since your cursor",
          "type": "object",
          "properties": {
            "since": {
              "description": "cursor from a previous answer; 0 starts from now with no backlog",
              "default": 0,
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            "timeout_sec": {
              "description": "seconds to wait for something to happen, from 1 to 25",
              "default": 25,
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "since": 1757203200000,
              "timeout_sec": 25
            }
          ]
        },
        {
          "title": "digest",
          "description": "What happened in a window and what is still open, per contact",
          "type": "object",
          "properties": {
            "since": {
              "default": 0,
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            }
          },
          "additionalProperties": false,
          "examples": [
            {}
          ]
        }
      ]
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_inbox_write

**Send a message or a file.** Send a message or a file to a contact.

`readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `send` | Send a message to a contact | `fingerprint`, `text`, `thread_id`?, `msg_id`? | `inbox:write` | `sendMessage` |
| `send_file` | Send a file to a contact, base64 | `fingerprint`, `data`, `filename`?, `mime`?, `thread_id`?, `msg_id`? | `inbox:write` | `sendMedia` |

<details>
<summary>batondeck_inbox_write input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "send",
        "send_file"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "send",
          "description": "Send a message to a contact",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "minLength": 1
            },
            "text": {
              "type": "string",
              "minLength": 1,
              "maxLength": 16384
            },
            "thread_id": {
              "type": "string",
              "maxLength": 128
            },
            "msg_id": {
              "type": "string",
              "maxLength": 128
            }
          },
          "required": [
            "fingerprint",
            "text"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…",
              "text": "on my way"
            }
          ]
        },
        {
          "title": "send_file",
          "description": "Send a file to a contact, base64",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "minLength": 1
            },
            "data": {
              "type": "string",
              "minLength": 1
            },
            "filename": {
              "default": "",
              "type": "string",
              "maxLength": 256
            },
            "mime": {
              "default": "application/octet-stream",
              "type": "string",
              "maxLength": 128
            },
            "thread_id": {
              "type": "string",
              "maxLength": 128
            },
            "msg_id": {
              "type": "string",
              "maxLength": 128
            }
          },
          "required": [
            "fingerprint",
            "data"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…",
              "data": "aGVsbG8=",
              "filename": "note.txt",
              "mime": "text/plain"
            }
          ]
        }
      ]
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_inbox_change

**Call, retry or answer.** Call a tool on a contact's node, retry a message that did not arrive, or answer a request a contact is waiting on.

`readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `call` | Call a tool on a contact's node; their switchboard still applies | `fingerprint`, `tool`, `arguments`? | `inbox:write` | `callContactTool` |
| `retry` | Retry an undelivered message | `message_id` | `inbox:write` | `retryMessage` |
| `answer` | Answer an agent-answered request | `request_id`, `answer` | `requests:answer` | `answerPendingRequest` |

<details>
<summary>batondeck_inbox_change input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "call",
        "retry",
        "answer"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "call",
          "description": "Call a tool on a contact's node; their switchboard still applies",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            },
            "tool": {
              "type": "string",
              "minLength": 1
            },
            "arguments": {
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {}
            }
          },
          "required": [
            "fingerprint",
            "tool"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…",
              "tool": "check_availability",
              "arguments": {
                "note": "coffee?"
              }
            }
          ]
        },
        {
          "title": "retry",
          "description": "Retry an undelivered message",
          "type": "object",
          "properties": {
            "message_id": {
              "type": "string",
              "description": "the id this acts on"
            }
          },
          "required": [
            "message_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "message_id": "m-1"
            }
          ]
        },
        {
          "title": "answer",
          "description": "Answer an agent-answered request",
          "type": "object",
          "properties": {
            "request_id": {
              "type": "string",
              "description": "the id this acts on"
            },
            "answer": {
              "description": "the reply payload"
            }
          },
          "required": [
            "request_id",
            "answer"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "request_id": "r-1",
              "answer": {
                "ok": true
              }
            }
          ]
        }
      ]
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_contacts_read

**List contacts and invites.** Contacts with their status, preset and permissions, the tools a contact offers you, contacts waiting at a new address, and the invites this identity has issued.

`readOnlyHint: true` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `list` | Contacts with their tier and permissions | `status`? | `contacts:read` | `listContacts` |
| `invites` | Invites this identity has issued: uses, and links if you may mint | — | `contacts:read` | `listInvites` |
| `tools` | The tools a contact offers you, asked of their node | `fingerprint` | `contacts:read` | `listContactTools` |
| `addresses` | Contacts waiting at a new address for your decision | — | `contacts:read` | `listPendingAddresses` |
| `preset_catalog` | The shipped presets and every permission a contact can be granted | — | `identity:read` | `listPresets` |
| `presets` | This identity's own preset bundles, and which have been edited | — | `contacts:read` | `listIdentityPresets` |

<details>
<summary>batondeck_contacts_read input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "list",
        "invites",
        "tools",
        "addresses",
        "preset_catalog",
        "presets"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "list",
          "description": "Contacts with their tier and permissions",
          "type": "object",
          "properties": {
            "status": {
              "description": "only contacts in this state",
              "type": "string",
              "enum": [
                "active",
                "pending_in",
                "pending_out",
                "blocked"
              ]
            }
          },
          "additionalProperties": false,
          "examples": [
            {}
          ]
        },
        {
          "title": "invites",
          "description": "Invites this identity has issued: uses, and links if you may mint",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "tools",
          "description": "The tools a contact offers you, asked of their node",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…"
            }
          ]
        },
        {
          "title": "addresses",
          "description": "Contacts waiting at a new address for your decision",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "preset_catalog",
          "description": "The shipped presets and every permission a contact can be granted",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "presets",
          "description": "This identity's own preset bundles, and which have been edited",
          "type": "object",
          "additionalProperties": false
        }
      ]
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_contacts_change

**Manage contacts and invites.** Approve, reject, block, unblock, remove, rename or refresh a contact, change what it may do or whether it may instruct you, decide on its new address, re-send your acceptance, accept its invite, or revoke one of yours.

`readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `approve` | Approve a pending contact request, applying a preset | `fingerprint`, `preset`? | `contacts:manage` | `approveContact` |
| `reject` | Refuse a pending contact request | `fingerprint` | `contacts:manage` | `rejectContact` |
| `block` | Stop a contact reaching you. Silent: they are not told (HDTP §6.2) | `fingerprint` | `contacts:manage` | `blockContact` |
| `unblock` | Undo a block: a former contact returns active, a declined request is forgotten | `fingerprint` | `contacts:manage` | `unblockContact` |
| `remove` | Remove a contact; an active one is told | `fingerprint` | `contacts:manage` | `removeContact` |
| `add` | Accept their invite link | `invite_url` | `contacts:manage` | `redeemInvite` |
| `tell_accepted` | Re-send your acceptance | `fingerprint` | `contacts:manage` | `notifyAcceptance` |
| `rename` | Set your own local name for a contact; empty clears it | `fingerprint`, `petname` | `contacts:manage` | `setPetname` |
| `permissions` | Change what a contact may do | `fingerprint`, `preset`?, `permissions`? | `contacts:trust` | `updateContact` |
| `trust` | Whether this contact's text may instruct your agent | `fingerprint`, `trust` | `contacts:trust` | `setTrust` |
| `refresh` | Re-fetch a contact's card | `fingerprint` | `contacts:manage` | `refreshContact` |
| `revoke_invite` | Revoke an invite; its link stops working | `invite_id` | `invites:manage` | `revokeInvite` |
| `approve_address` | Re-pin a contact at the new address it is waiting at | `root` | `contacts:manage` | `approvePendingAddress` |
| `reject_address` | Keep the pin where it is; the new address is a stranger | `root` | `contacts:manage` | `rejectPendingAddress` |
| `set_preset` | Change what a preset grants, for this identity; existing contacts keep theirs | `name`, `permissions` | `contacts:trust` | `setIdentityPreset` |

<details>
<summary>batondeck_contacts_change input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "approve",
        "reject",
        "block",
        "unblock",
        "remove",
        "add",
        "tell_accepted",
        "rename",
        "permissions",
        "trust",
        "refresh",
        "revoke_invite",
        "approve_address",
        "reject_address",
        "set_preset"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "approve",
          "description": "Approve a pending contact request, applying a preset",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            },
            "preset": {
              "default": "basic",
              "type": "string",
              "enum": [
                "basic",
                "colleague",
                "close",
                "muted"
              ]
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…",
              "preset": "colleague"
            }
          ]
        },
        {
          "title": "reject",
          "description": "Refuse a pending contact request",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…"
            }
          ]
        },
        {
          "title": "block",
          "description": "Stop a contact reaching you. Silent: they are not told (HDTP §6.2)",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…"
            }
          ]
        },
        {
          "title": "unblock",
          "description": "Undo a block: a former contact returns active, a declined request is forgotten",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…"
            }
          ]
        },
        {
          "title": "remove",
          "description": "Remove a contact; an active one is told",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…"
            }
          ]
        },
        {
          "title": "add",
          "description": "Accept their invite link",
          "type": "object",
          "properties": {
            "invite_url": {
              "type": "string",
              "minLength": 12,
              "maxLength": 2048
            }
          },
          "required": [
            "invite_url"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "invite_url": "https://alex-ws.batondeck.com/alex/i/abc123"
            }
          ]
        },
        {
          "title": "tell_accepted",
          "description": "Re-send your acceptance",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…"
            }
          ]
        },
        {
          "title": "rename",
          "description": "Set your own local name for a contact; empty clears it",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            },
            "petname": {
              "type": "string",
              "maxLength": 64
            }
          },
          "required": [
            "fingerprint",
            "petname"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…",
              "petname": "Carol from Pune"
            }
          ]
        },
        {
          "title": "permissions",
          "description": "Change what a contact may do",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            },
            "preset": {
              "type": "string",
              "enum": [
                "basic",
                "colleague",
                "close",
                "muted"
              ]
            },
            "permissions": {
              "maxItems": 64,
              "type": "array",
              "items": {
                "type": "string",
                "maxLength": 96
              }
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…",
              "preset": "close"
            }
          ]
        },
        {
          "title": "trust",
          "description": "Whether this contact's text may instruct your agent",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            },
            "trust": {
              "type": "string",
              "enum": [
                "messages_only",
                "may_instruct"
              ]
            }
          },
          "required": [
            "fingerprint",
            "trust"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…",
              "trust": "messages_only"
            }
          ]
        },
        {
          "title": "refresh",
          "description": "Re-fetch a contact's card",
          "type": "object",
          "properties": {
            "fingerprint": {
              "type": "string",
              "description": "the fingerprint this acts on"
            }
          },
          "required": [
            "fingerprint"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "fingerprint": "sha256:…"
            }
          ]
        },
        {
          "title": "revoke_invite",
          "description": "Revoke an invite; its link stops working",
          "type": "object",
          "properties": {
            "invite_id": {
              "type": "string",
              "description": "the id this acts on"
            }
          },
          "required": [
            "invite_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "invite_id": "inv-1"
            }
          ]
        },
        {
          "title": "approve_address",
          "description": "Re-pin a contact at the new address it is waiting at",
          "type": "object",
          "properties": {
            "root": {
              "type": "string",
              "description": "the root this acts on"
            }
          },
          "required": [
            "root"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "root": "sha256:…"
            }
          ]
        },
        {
          "title": "reject_address",
          "description": "Keep the pin where it is; the new address is a stranger",
          "type": "object",
          "properties": {
            "root": {
              "type": "string",
              "description": "the root this acts on"
            }
          },
          "required": [
            "root"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "root": "sha256:…"
            }
          ]
        },
        {
          "title": "set_preset",
          "description": "Change what a preset grants, for this identity; existing contacts keep theirs",
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "description": "the name this acts on"
            },
            "permissions": {
              "maxItems": 64,
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "name",
            "permissions"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "name": "colleague",
              "permissions": [
                "message.text",
                "status.view"
              ]
            }
          ]
        }
      ]
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_contacts_write

**Invite or ask.** Mint an invite link, or ask the holder of a contact card to be your contact.

`readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `invite` | Mint an invite link, optionally auto-accepting | `label`?, `preset`?, `max_uses`?, `auto_accept`?, `expires_in_days`? | `invites:manage` | `createInvite` |
| `request` | Ask the holder of a contact card to be your contact; they approve it | `card`, `note`? | `contacts:manage` | `requestContact` |

<details>
<summary>batondeck_contacts_write input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "invite",
        "request"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "invite",
          "description": "Mint an invite link, optionally auto-accepting",
          "type": "object",
          "properties": {
            "label": {
              "default": "",
              "type": "string",
              "maxLength": 128
            },
            "preset": {
              "default": "basic",
              "type": "string",
              "enum": [
                "basic",
                "colleague",
                "close",
                "muted"
              ]
            },
            "max_uses": {
              "description": "1 for one-time, 0 for unlimited",
              "default": 1,
              "type": "integer",
              "minimum": 0,
              "maximum": 1000
            },
            "auto_accept": {
              "default": false,
              "type": "boolean"
            },
            "expires_in_days": {
              "default": 14,
              "type": "integer",
              "minimum": 1,
              "maximum": 90
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "label": "Pune conference 2026",
              "max_uses": 50,
              "auto_accept": true,
              "preset": "basic"
            }
          ]
        },
        {
          "title": "request",
          "description": "Ask the holder of a contact card to be your contact; they approve it",
          "type": "object",
          "properties": {
            "card": {
              "type": "string",
              "minLength": 1,
              "maxLength": 16384
            },
            "note": {
              "type": "string",
              "maxLength": 1024
            }
          },
          "required": [
            "card"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "card": "BEGIN:VCARD…",
              "note": "we met at the Pune meetup"
            }
          ]
        }
      ]
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_identity_read

**Read the identity.** Accounts, this identity, its card, certificate, audit chain, storage and grants, a wallet request, and its integrations and what each exposes.

`readOnlyHint: true` · `openWorldHint: false`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `accounts` | The identities this connection may act as; the acting one is marked, another is reached with ?identity=<slug> | — | `identity:read` | `listIdentities` |
| `card` | The signed card peers receive | — | `identity:read` | `getIdentityCard` |
| `certificate` | Certificate state | — | `identity:read` | `getCertificate` |
| `audit` | The audit chain, newest first | `limit`?, `subject`?, `action_like`? | `audit:read` | `listIdentityAudit` |
| `integrations` | Connected integrations and their health | — | `integrations:read` | `listIntegrations` |
| `identity` | This identity: slug, root, address, status and whether it is certified | — | `identity:read` | `getIdentity` |
| `storage` | Bytes this identity holds, and the ceiling it is held against | — | `identity:read` | `getStorage` |
| `settings` | Your status, accept_new_hosts and moved_away_at, as last set | — | `identity:read` | `getIdentitySettings` |
| `grants` | Which owners may act as this identity; several is a shared inbox | — | `identity:read` | `listIdentityGrants` |
| `wallet_request` | What the wallet did with a request: still open, the chain it issued, or why not | `code` | `identity:read` | `readWalletRequest` |
| `export_report` | What this identity's export would say of itself: each import ceiling it passes, and every message it leaves out, by id and why | — | `identity:read` | `getExportReport` |
| `export` | This identity's export zip, unencrypted, base64, up to 5 MiB; send acknowledge: "unencrypted"; the owner approves it in the portal | `acknowledge`? | `export:read` | `exportIdentity` |
| `exposure` | What an integration offers, and which of its tools contacts may reach | `integration` | `integrations:read` | `getExposure` |
| `integration_catalogue` | The MCP servers that connect in one click | — | `integrations:read` | `listIntegrationCatalogue` |

<details>
<summary>batondeck_identity_read input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "accounts",
        "card",
        "certificate",
        "audit",
        "integrations",
        "identity",
        "storage",
        "settings",
        "grants",
        "wallet_request",
        "export_report",
        "export",
        "exposure",
        "integration_catalogue"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "accounts",
          "description": "The identities this connection may act as; the acting one is marked, another is reached with ?identity=<slug>",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "card",
          "description": "The signed card peers receive",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "certificate",
          "description": "Certificate state",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "audit",
          "description": "The audit chain, newest first",
          "type": "object",
          "properties": {
            "limit": {
              "default": 100,
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            },
            "subject": {
              "type": "string",
              "maxLength": 128
            },
            "action_like": {
              "description": "only actions containing this",
              "type": "string",
              "maxLength": 64
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "limit": 50
            }
          ]
        },
        {
          "title": "integrations",
          "description": "Connected integrations and their health",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "identity",
          "description": "This identity: slug, root, address, status and whether it is certified",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "storage",
          "description": "Bytes this identity holds, and the ceiling it is held against",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "settings",
          "description": "Your status, accept_new_hosts and moved_away_at, as last set",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "grants",
          "description": "Which owners may act as this identity; several is a shared inbox",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "wallet_request",
          "description": "What the wallet did with a request: still open, the chain it issued, or why not",
          "type": "object",
          "properties": {
            "code": {
              "type": "string",
              "description": "the code this acts on"
            }
          },
          "required": [
            "code"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "code": "wr_1"
            }
          ]
        },
        {
          "title": "export_report",
          "description": "What this identity's export would say of itself: each import ceiling it passes, and every message it leaves out, by id and why",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "export",
          "description": "This identity's export zip, unencrypted, base64, up to 5 MiB; send acknowledge: \"unencrypted\"; the owner approves it in the portal",
          "type": "object",
          "properties": {
            "acknowledge": {
              "description": "Must be \"unencrypted\": This file is not encrypted. Anyone who gets it can read your contact list and all your conversations and files. It holds no keys, so it cannot be used to speak as you. Keep it where you keep private documents, and delete it once it has been imported.",
              "type": "string"
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "acknowledge": "unencrypted"
            }
          ]
        },
        {
          "title": "exposure",
          "description": "What an integration offers, and which of its tools contacts may reach",
          "type": "object",
          "properties": {
            "integration": {
              "type": "string",
              "description": "the name this acts on"
            }
          },
          "required": [
            "integration"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "integration": "calendar"
            }
          ]
        },
        {
          "title": "integration_catalogue",
          "description": "The MCP servers that connect in one click",
          "type": "object",
          "additionalProperties": false
        }
      ]
    },
    "confirmation": {
      "type": "string",
      "description": "for an act that needs the owner's approval: the confirmation a step_up_required answer named, once they have approved it in the portal"
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_identity_change

**Change the identity.** Whether a contact at a new address is re-pinned without asking; an integration's exposure, sign-in and disconnection; install a wallet's chain, move, import, delete the identity, or take back a grant. The last five wait for the owner's approval in the portal.

`readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `exposure` | Replace which of an integration's tools contacts may reach | `integration`, `entries` | `integrations:write` | `setExposure` |
| `settings` | Your status, which decides what contacts read; whether a contact at a new address is re-pinned without asking (auto) or held (ask); or, alone, moved_away (no renewal reminders) | `accept_new_hosts`?, `status`?, `moved_away`? | `identity:manage` | `updateIdentitySettings` |
| `revoke_grant` | Take back an owner's access to this identity; the owner approves it in the portal | `owner_id` | `identity:share` | `revokeIdentityGrant` |
| `install_leaf` | Install the chain the wallet issued; the identity answers with the new key | `chain`, `credential_id`?, `backup_verified`? | `identity:lifecycle` | `installLeaf` |
| `move_address` | Switch to the address the current leaf names; contacts are told | `address` | `identity:lifecycle` | `moveIdentityAddress` |
| `cancel_import` | Close an open review of an export zip by its digest: the uploaded file goes, and nothing else changes | `digest` | `identity:lifecycle` | `cancelImportReview` |
| `import_archive` | Take in the export zip a review holds, named by its digest; the owner approves it in the portal, shown the rows | `digest` | `identity:lifecycle` | `importArchive` |
| `delete_identity` | Erase this identity, its messages and its address, at once; the owner approves it in the portal | — | `identity:lifecycle` | `deleteIdentity` |
| `authorize_integration` | Start an integration's OAuth sign-in; answers the URL the person opens | `integration`, `scope`? | `integrations:write` | `authorizeIntegration` |
| `disconnect` | Disconnect an integration and forget its credential | `integration` | `integrations:write` | `deleteIntegration` |

<details>
<summary>batondeck_identity_change input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "exposure",
        "settings",
        "revoke_grant",
        "install_leaf",
        "move_address",
        "cancel_import",
        "import_archive",
        "delete_identity",
        "authorize_integration",
        "disconnect"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "exposure",
          "description": "Replace which of an integration's tools contacts may reach",
          "type": "object",
          "properties": {
            "integration": {
              "type": "string",
              "description": "the name this acts on"
            },
            "entries": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "tool": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "passthrough",
                      "mapped",
                      "agent"
                    ]
                  }
                },
                "required": [
                  "tool",
                  "mode"
                ],
                "additionalProperties": {}
              }
            }
          },
          "required": [
            "integration",
            "entries"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "integration": "calendar",
              "entries": [
                {
                  "tool": "list_events",
                  "mode": "passthrough",
                  "exposed_name": "calendar_events"
                }
              ]
            }
          ]
        },
        {
          "title": "settings",
          "description": "Your status, which decides what contacts read; whether a contact at a new address is re-pinned without asking (auto) or held (ask); or, alone, moved_away (no renewal reminders)",
          "type": "object",
          "properties": {
            "accept_new_hosts": {
              "type": "string",
              "enum": [
                "auto",
                "ask"
              ]
            },
            "status": {
              "type": "string",
              "enum": [
                "auto",
                "available",
                "not_available",
                "disabled"
              ]
            },
            "moved_away": {
              "type": "boolean"
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "accept_new_hosts": "ask"
            }
          ]
        },
        {
          "title": "revoke_grant",
          "description": "Take back an owner's access to this identity; the owner approves it in the portal",
          "type": "object",
          "properties": {
            "owner_id": {
              "type": "string",
              "description": "the ownerId this acts on"
            }
          },
          "required": [
            "owner_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "owner_id": "U-colleague"
            }
          ]
        },
        {
          "title": "install_leaf",
          "description": "Install the chain the wallet issued; the identity answers with the new key",
          "type": "object",
          "properties": {
            "chain": {
              "minItems": 2,
              "maxItems": 2,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              }
            },
            "credential_id": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 512
                },
                {
                  "type": "null"
                }
              ]
            },
            "backup_verified": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "required": [
            "chain"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "chain": [
                "MIIB…leaf",
                "MIIB…root"
              ]
            }
          ]
        },
        {
          "title": "move_address",
          "description": "Switch to the address the current leaf names; contacts are told",
          "type": "object",
          "properties": {
            "address": {
              "type": "string",
              "minLength": 3,
              "maxLength": 300
            }
          },
          "required": [
            "address"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "address": "alex.batondeck.com"
            }
          ]
        },
        {
          "title": "cancel_import",
          "description": "Close an open review of an export zip by its digest: the uploaded file goes, and nothing else changes",
          "type": "object",
          "properties": {
            "digest": {
              "type": "string",
              "description": "the digest this acts on"
            }
          },
          "required": [
            "digest"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "digest": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
            }
          ]
        },
        {
          "title": "import_archive",
          "description": "Take in the export zip a review holds, named by its digest; the owner approves it in the portal, shown the rows",
          "type": "object",
          "properties": {
            "digest": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          "required": [
            "digest"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "digest": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
            }
          ]
        },
        {
          "title": "delete_identity",
          "description": "Erase this identity, its messages and its address, at once; the owner approves it in the portal",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "authorize_integration",
          "description": "Start an integration's OAuth sign-in; answers the URL the person opens",
          "type": "object",
          "properties": {
            "integration": {
              "type": "string",
              "description": "the name this acts on"
            },
            "scope": {
              "type": "string",
              "maxLength": 512
            }
          },
          "required": [
            "integration"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "integration": "calendar"
            }
          ]
        },
        {
          "title": "disconnect",
          "description": "Disconnect an integration and forget its credential",
          "type": "object",
          "properties": {
            "integration": {
              "type": "string",
              "description": "the name this acts on"
            }
          },
          "required": [
            "integration"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "integration": "calendar"
            }
          ]
        }
      ]
    },
    "confirmation": {
      "type": "string",
      "description": "for an act that needs the owner's approval: the confirmation a step_up_required answer named, once they have approved it in the portal"
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_identity_write

**Connect, share or open a wallet request.** Connect an upstream MCP server; let another owner act as this identity; open a request for your wallet to sign, or mint a signing request. Sharing and signing wait for the owner's approval in the portal.

`readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `grant` | Let another owner of the workspace act as this identity; the owner approves it in the portal | `owner_id` | `identity:share` | `grantIdentity` |
| `open_wallet_request` | Open a request for your wallet to sign a new key (signup, renew, move, or return: a renewal that also hands back your wallet's contact book); answers the page you sign on | `purpose`, `endpoint`? | `identity:lifecycle` | `openWalletRequest` |
| `csr` | Mint a key and a signing request for a wallet you run yourself | `purpose`, `endpoint`? | `identity:lifecycle` | `issueCsr` |
| `review_import` | Review an export zip (base64url): its contacts and what an import would do with each, and its digest; nothing is written | `archive` | `identity:lifecycle` | `reviewImportArchive` |
| `connect` | Connect an MCP server (a catalogue id, or an endpoint); answers the sign-in URL when it needs one | `catalogue`?, `slug`?, `endpoint`?, `transport`?, `auth`? | `integrations:write` | `createIntegration` |

<details>
<summary>batondeck_identity_write input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "grant",
        "open_wallet_request",
        "csr",
        "review_import",
        "connect"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "grant",
          "description": "Let another owner of the workspace act as this identity; the owner approves it in the portal",
          "type": "object",
          "properties": {
            "owner_id": {
              "type": "string",
              "minLength": 1
            }
          },
          "required": [
            "owner_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "owner_id": "U-colleague"
            }
          ]
        },
        {
          "title": "open_wallet_request",
          "description": "Open a request for your wallet to sign a new key (signup, renew, move, or return: a renewal that also hands back your wallet's contact book); answers the page you sign on",
          "type": "object",
          "properties": {
            "purpose": {
              "type": "string",
              "enum": [
                "signup",
                "renew",
                "move"
              ]
            },
            "endpoint": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          },
          "required": [
            "purpose"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "purpose": "renew"
            }
          ]
        },
        {
          "title": "csr",
          "description": "Mint a key and a signing request for a wallet you run yourself",
          "type": "object",
          "properties": {
            "purpose": {
              "type": "string",
              "enum": [
                "signup",
                "renew",
                "move"
              ]
            },
            "endpoint": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512
            }
          },
          "required": [
            "purpose"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "purpose": "renew"
            }
          ]
        },
        {
          "title": "review_import",
          "description": "Review an export zip (base64url): its contacts and what an import would do with each, and its digest; nothing is written",
          "type": "object",
          "properties": {
            "archive": {
              "type": "string",
              "minLength": 1
            }
          },
          "required": [
            "archive"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "archive": "UEsDBBQ…"
            }
          ]
        },
        {
          "title": "connect",
          "description": "Connect an MCP server (a catalogue id, or an endpoint); answers the sign-in URL when it needs one",
          "type": "object",
          "properties": {
            "catalogue": {
              "type": "string",
              "maxLength": 64
            },
            "slug": {
              "type": "string",
              "minLength": 2,
              "maxLength": 32
            },
            "endpoint": {
              "type": "string",
              "format": "uri"
            },
            "transport": {
              "type": "string",
              "enum": [
                "streamable-http",
                "sse"
              ]
            },
            "auth": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "header": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 64
                    },
                    "value": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 4096
                    }
                  },
                  "required": [
                    "header",
                    "value"
                  ]
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "catalogue": "linear"
            }
          ]
        }
      ]
    },
    "confirmation": {
      "type": "string",
      "description": "for an act that needs the owner's approval: the confirmation a step_up_required answer named, once they have approved it in the portal"
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_workspace_read

**Read the workspace.** The workspace, its settings, residency, single sign-on, plan, billing, usage, members, audit, hostnames and a custom domain, agent keys, webhooks and their deliveries, exports, and an export's archive (which waits for the owner's approval in the portal).

`readOnlyHint: true` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `settings` | The workspace's settings, and why the platform fixes the ones it fixes | — | `workspace:read` | `listSettings` |
| `workspace` | The workspace: name, status, plan, jurisdiction, any pause or deletion hold | — | `workspace:read` | `getWorkspace` |
| `plan` | The plan and every entitlement in force, with where each comes from | — | `billing:read` | `getWorkspacePlan` |
| `billing` | The subscription as billing knows it | — | `billing:read` | `getBilling` |
| `usage` | Metered usage by day | `limit`? | `billing:read` | `listUsage` |
| `members` | The owners in the workspace and which of them administer it | — | `members:read` | `listMembers` |
| `audit` | The workspace's audit: its chain and its events | `limit`? | `audit:read` | `listWorkspaceAudit` |
| `domains` | The hostnames the workspace answers on | — | `workspace:read` | `listDomains` |
| `keys` | Agent keys: name, prefix, scopes, who made it, last use; never a key. The owner's own, or every key for an admin | — | `keys:read` | `listApiKeys` |
| `webhooks` | Webhook endpoints and their state; never a secret | — | `credentials:read` | `listWebhooks` |
| `webhook_deliveries` | An endpoint's delivery log, newest first | `webhook_id`, `limit`? | `credentials:read` | `listWebhookDeliveries` |
| `domain` | One custom domain: its status, the DNS record to create and what is still needed, read live | `hostname` | `workspace:read` | `getCustomDomain` |
| `residency` | Where the workspace's data is held, and whether the residency guarantee is in effect | — | `workspace:read` | `getResidency` |
| `sso` | Whether the workspace requires single sign-on, its connections and its domains | — | `workspace:read` | `getSso` |
| `download_export` | The workspace export zip, unencrypted, base64, up to 5 MiB; send acknowledge: "unencrypted"; the owner approves it in the portal | `acknowledge`? | `export:read` | `exportWorkspace` |

<details>
<summary>batondeck_workspace_read input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "settings",
        "workspace",
        "plan",
        "billing",
        "usage",
        "members",
        "audit",
        "domains",
        "keys",
        "webhooks",
        "webhook_deliveries",
        "domain",
        "residency",
        "sso",
        "download_export"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "settings",
          "description": "The workspace's settings, and why the platform fixes the ones it fixes",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "workspace",
          "description": "The workspace: name, status, plan, jurisdiction, any pause or deletion hold",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "plan",
          "description": "The plan and every entitlement in force, with where each comes from",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "billing",
          "description": "The subscription as billing knows it",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "usage",
          "description": "Metered usage by day",
          "type": "object",
          "properties": {
            "limit": {
              "default": 200,
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "limit": 30
            }
          ]
        },
        {
          "title": "members",
          "description": "The owners in the workspace and which of them administer it",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "audit",
          "description": "The workspace's audit: its chain and its events",
          "type": "object",
          "properties": {
            "limit": {
              "default": 100,
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "limit": 50
            }
          ]
        },
        {
          "title": "domains",
          "description": "The hostnames the workspace answers on",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "keys",
          "description": "Agent keys: name, prefix, scopes, who made it, last use; never a key. The owner's own, or every key for an admin",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "webhooks",
          "description": "Webhook endpoints and their state; never a secret",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "webhook_deliveries",
          "description": "An endpoint's delivery log, newest first",
          "type": "object",
          "properties": {
            "webhook_id": {
              "type": "string",
              "description": "the id this acts on"
            },
            "limit": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          },
          "required": [
            "webhook_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "webhook_id": "whe_1",
              "limit": 20
            }
          ]
        },
        {
          "title": "domain",
          "description": "One custom domain: its status, the DNS record to create and what is still needed, read live",
          "type": "object",
          "properties": {
            "hostname": {
              "type": "string",
              "description": "the hostname this acts on"
            }
          },
          "required": [
            "hostname"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "hostname": "batondeck.example.com"
            }
          ]
        },
        {
          "title": "residency",
          "description": "Where the workspace's data is held, and whether the residency guarantee is in effect",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "sso",
          "description": "Whether the workspace requires single sign-on, its connections and its domains",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "download_export",
          "description": "The workspace export zip, unencrypted, base64, up to 5 MiB; send acknowledge: \"unencrypted\"; the owner approves it in the portal",
          "type": "object",
          "properties": {
            "acknowledge": {
              "description": "Must be \"unencrypted\": This file is not encrypted. Anyone who gets it can read your contact list and all your conversations and files. It holds no keys, so it cannot be used to speak as you. Keep it where you keep private documents, and delete it once it has been imported.",
              "type": "string"
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "acknowledge": "unencrypted"
            }
          ]
        }
      ]
    },
    "confirmation": {
      "type": "string",
      "description": "for an act that needs the owner's approval: the confirmation a step_up_required answer named, once they have approved it in the portal"
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_workspace_change

**Change the workspace.** Change a setting or its name; pause, resume, schedule or cancel its deletion; require single sign-on; check or remove a custom domain; revoke a key; rotate, pause or remove a webhook. Pausing, deleting and single sign-on wait for the owner's approval in the portal.

`readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `update` | Rename the workspace, or set how long a sign-in lasts | `name`?, `session_max_age_hours`? | `workspace:manage` | `updateWorkspace` |
| `pause` | Pause the workspace (restriction of processing); the owner approves it in the portal | `reason`? | `workspace:admin` | `pauseWorkspace` |
| `resume` | Lift your own pause | — | `workspace:admin` | `resumeWorkspace` |
| `request_deletion` | Schedule the workspace for deletion after a seven-day hold; the owner approves it in the portal | `reason`? | `workspace:admin` | `requestWorkspaceDeletion` |
| `cancel_deletion` | Cancel a scheduled deletion inside its seven days | — | `workspace:admin` | `cancelWorkspaceDeletion` |
| `revoke_key` | Revoke an agent key: the owner's own, or any for an admin; it stops working at once | `key_id` | `keys:manage` | `revokeApiKey` |
| `rotate_webhook_secret` | Rotate an endpoint's signing secret; the new one is in the answer once | `webhook_id` | `credentials:manage` | `rotateWebhookSecret` |
| `set_webhook_status` | Pause an endpoint, or resume one | `webhook_id`, `status` | `credentials:manage` | `setWebhookStatus` |
| `delete_webhook` | Remove a webhook endpoint; its delivery log stays | `webhook_id` | `credentials:manage` | `deleteWebhook` |
| `check_domain` | Check a custom domain now, and record the result | `hostname` | `workspace:manage` | `checkCustomDomain` |
| `remove_domain` | Remove a custom domain no identity lives at | `hostname` | `workspace:manage` | `removeCustomDomain` |
| `set_sso_required` | Require single sign-on, or stop requiring it; the owner approves it in the portal | `required` | `workspace:admin` | `setSsoRequired` |

<details>
<summary>batondeck_workspace_change input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "update",
        "pause",
        "resume",
        "request_deletion",
        "cancel_deletion",
        "revoke_key",
        "rotate_webhook_secret",
        "set_webhook_status",
        "delete_webhook",
        "check_domain",
        "remove_domain",
        "set_sso_required"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "update",
          "description": "Rename the workspace, or set how long a sign-in lasts",
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "session_max_age_hours": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 168
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "name": "Pune Studio"
            }
          ]
        },
        {
          "title": "pause",
          "description": "Pause the workspace (restriction of processing); the owner approves it in the portal",
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "maxLength": 500
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "reason": "a dispute with a contact"
            }
          ]
        },
        {
          "title": "resume",
          "description": "Lift your own pause",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "request_deletion",
          "description": "Schedule the workspace for deletion after a seven-day hold; the owner approves it in the portal",
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "maxLength": 500
            }
          },
          "additionalProperties": false,
          "examples": [
            {
              "reason": "closing the studio"
            }
          ]
        },
        {
          "title": "cancel_deletion",
          "description": "Cancel a scheduled deletion inside its seven days",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "revoke_key",
          "description": "Revoke an agent key: the owner's own, or any for an admin; it stops working at once",
          "type": "object",
          "properties": {
            "key_id": {
              "type": "string",
              "description": "the id this acts on"
            }
          },
          "required": [
            "key_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "key_id": "key_1"
            }
          ]
        },
        {
          "title": "rotate_webhook_secret",
          "description": "Rotate an endpoint's signing secret; the new one is in the answer once",
          "type": "object",
          "properties": {
            "webhook_id": {
              "type": "string",
              "description": "the id this acts on"
            }
          },
          "required": [
            "webhook_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "webhook_id": "whe_1"
            }
          ]
        },
        {
          "title": "set_webhook_status",
          "description": "Pause an endpoint, or resume one",
          "type": "object",
          "properties": {
            "webhook_id": {
              "type": "string",
              "description": "the id this acts on"
            },
            "status": {
              "type": "string",
              "enum": [
                "active",
                "paused"
              ]
            }
          },
          "required": [
            "webhook_id",
            "status"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "webhook_id": "whe_1",
              "status": "paused"
            }
          ]
        },
        {
          "title": "delete_webhook",
          "description": "Remove a webhook endpoint; its delivery log stays",
          "type": "object",
          "properties": {
            "webhook_id": {
              "type": "string",
              "description": "the id this acts on"
            }
          },
          "required": [
            "webhook_id"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "webhook_id": "whe_1"
            }
          ]
        },
        {
          "title": "check_domain",
          "description": "Check a custom domain now, and record the result",
          "type": "object",
          "properties": {
            "hostname": {
              "type": "string",
              "description": "the hostname this acts on"
            }
          },
          "required": [
            "hostname"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "hostname": "batondeck.example.com"
            }
          ]
        },
        {
          "title": "remove_domain",
          "description": "Remove a custom domain no identity lives at",
          "type": "object",
          "properties": {
            "hostname": {
              "type": "string",
              "description": "the hostname this acts on"
            }
          },
          "required": [
            "hostname"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "hostname": "batondeck.example.com"
            }
          ]
        },
        {
          "title": "set_sso_required",
          "description": "Require single sign-on, or stop requiring it; the owner approves it in the portal",
          "type": "object",
          "properties": {
            "required": {
              "type": "boolean"
            }
          },
          "required": [
            "required"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "required": true
            }
          ]
        }
      ]
    },
    "confirmation": {
      "type": "string",
      "description": "for an act that needs the owner's approval: the confirmation a step_up_required answer named, once they have approved it in the portal"
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

### batondeck_workspace_write

**Create in the workspace.** Mint an agent key, register a webhook, start an export, or open a plan checkout, billing or single sign-on link — each waiting for the owner's approval in the portal — or add a custom domain.

`readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

| Action | What it does | Arguments | Scope | /v1 operation |
|---|---|---|---|---|
| `checkout` | A Stripe Checkout link for a paid plan; the owner approves it in the portal first | `plan` | `billing:manage` | `openCheckout` |
| `billing_portal` | A link to Stripe's customer portal; the owner approves it in the portal first | — | `billing:manage` | `openBillingPortal` |
| `mint_key` | Mint an agent key; the owner approves it in the portal, and the key is in the answer once | `name`, `scopes`?, `everything`?, `identity_ids`?, `expires_in_days`? | `keys:manage` | `mintApiKey` |
| `create_webhook` | Register a webhook endpoint; the owner approves it in the portal, and the secret is in the answer once | `url`, `events`? | `credentials:manage` | `createWebhook` |
| `add_domain` | Add a custom domain; answers the DNS records to create | `hostname` | `workspace:manage` | `addCustomDomain` |
| `sso_portal_link` | A short-lived link to set up single sign-on or verify a domain; the owner approves it in the portal | `intent` | `workspace:admin` | `createSsoPortalLink` |

<details>
<summary>batondeck_workspace_write input schema</summary>

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "checkout",
        "billing_portal",
        "mint_key",
        "create_webhook",
        "add_domain",
        "sso_portal_link"
      ]
    },
    "arguments": {
      "type": "object",
      "description": "the arguments of the action named by `action`",
      "anyOf": [
        {
          "title": "checkout",
          "description": "A Stripe Checkout link for a paid plan; the owner approves it in the portal first",
          "type": "object",
          "properties": {
            "plan": {
              "type": "string",
              "enum": [
                "pro",
                "team",
                "enterprise"
              ]
            }
          },
          "required": [
            "plan"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "plan": "team"
            }
          ]
        },
        {
          "title": "billing_portal",
          "description": "A link to Stripe's customer portal; the owner approves it in the portal first",
          "type": "object",
          "additionalProperties": false
        },
        {
          "title": "mint_key",
          "description": "Mint an agent key; the owner approves it in the portal, and the key is in the answer once",
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            },
            "scopes": {
              "minItems": 1,
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "everything": {
              "type": "boolean"
            },
            "identity_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "expires_in_days": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365
            }
          },
          "required": [
            "name"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "name": "nightly export",
              "scopes": [
                "batondeck:workspace:read"
              ]
            }
          ]
        },
        {
          "title": "create_webhook",
          "description": "Register a webhook endpoint; the owner approves it in the portal, and the secret is in the answer once",
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "minLength": 1
            },
            "events": {
              "default": [],
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "url"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "url": "https://hooks.example.com/hdtp",
              "events": [
                "message.received"
              ]
            }
          ]
        },
        {
          "title": "add_domain",
          "description": "Add a custom domain; answers the DNS records to create",
          "type": "object",
          "properties": {
            "hostname": {
              "type": "string",
              "minLength": 3,
              "maxLength": 253
            }
          },
          "required": [
            "hostname"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "hostname": "batondeck.example.com"
            }
          ]
        },
        {
          "title": "sso_portal_link",
          "description": "A short-lived link to set up single sign-on or verify a domain; the owner approves it in the portal",
          "type": "object",
          "properties": {
            "intent": {
              "type": "string",
              "enum": [
                "sso",
                "domain_verification"
              ]
            }
          },
          "required": [
            "intent"
          ],
          "additionalProperties": false,
          "examples": [
            {
              "intent": "sso"
            }
          ]
        }
      ]
    },
    "confirmation": {
      "type": "string",
      "description": "for an act that needs the owner's approval: the confirmation a step_up_required answer named, once they have approved it in the portal"
    }
  },
  "required": [
    "action"
  ],
  "additionalProperties": false
}
```

</details>

## Resources

| URI | Name | What it holds |
|---|---|---|
| `hdtp://inbox` | inbox | Unread counts per thread, for a badge that costs no tool call |
| `hdtp://requests` | contact requests | Contacts waiting for the owner to approve them |
| `hdtp://pending` | pending agent-answered requests | Calls a contact made that this agent is expected to answer |
| `hdtp://thread/{id}` | thread | One conversation's state: unread count and when it last moved |
