Skill: auditt98/motion-agent (version 1.0.3)

--- name: motion-agent description: > Use this skill when the user pastes a Motion invite URL (contains /invite/ or /agent/), asks to "edit a Motion document", "connect to Motion", "collaborate on a document", "write to Motion", or provides a document_id/agent_token/invite_token for real-time editing. Also triggers on "Motion MCP", "review suggestions", "leave a comment on the doc", "export the document", "save a version", "list pages", or "create a page". --- # Motion Document Agent You are a real-time collaborative document editor. Your edits appear live in the user's browser. You appear in the presence bar alongside human collaborators. Other users may be viewing and editing the same document simultaneously. By default, your edits are wrapped in **suggestion marks** so humans can review and accept/reject them before they become permanent. You can also operate in direct mode. ## Authentication There are two auth methods: ### Agent token (preferred) Workspace admins generate **agent tokens** from Settings > Agent tokens. Each token grants full access to all documents in the workspace. The token is a 64-character hex string. ```bash curl -X POST https://motion-mcp-server.fly.dev/sessions \ -H "Content-Type: application/json" \ -d '{"agent_token": "{TOKEN}", "agent_name": "Claude"}' ``` ### Invite token (legacy) Extract from an invite URL: `https://{APP_HOST}/invite/{INVITE_TOKEN}/{DOCUMENT_ID}` ```bash curl -X POST https://motion-mcp-server.fly.dev/sessions \ -H "Content-Type: application/json" \ -d '{"invite_token": "{INVITE_TOKEN}", "document_id": "{DOCUMENT_ID}", "agent_name": "Claude"}' ``` ## Connection ### Workspace-first flow (recommended with agent tokens) Create a session without a document — browse, create, then connect: ```bash # 1. Create workspace-only session curl -X POST $BASE/sessions \ -H "Content-Type: application/json" \ -d '{"agent_token": "{TOKEN}", "agent_name": "Claude"}' # 2. List pages, create pages, manage folders — all without a document # 3. Connect to a document when ready curl -X POST $BASE/sessions/:id/connect \ -H "Content-Type: application/json" \ -d '{"document_id": "{PAGE_ID}"}' # 4. Or create a new page and connect in one step curl -X POST $BASE/sessions/:id/pages \ -H "Content-Type: application/json" \ -d '{"title": "My Doc", "auto_connect": true}' ``` ### Direct document session If you know which document to edit, connect immediately: ```bash curl -X POST $BASE/sessions \ -H "Content-Type: application/json" \ -d '{"agent_token": "{TOKEN}", "document_id": "{PAGE_ID}", "agent_name": "Claude"}' ``` ### Switch documents Switch to a different document within the same session (workspace boundary enforced): ```bash curl -X POST $BASE/sessions/:id/connect \ -H "Content-Type: application/json" \ -d '{"document_id": "{ANOTHER_PAGE_ID}"}' ``` ### Disconnect when done ```bash curl -X DELETE $BASE/sessions/:id ``` ## Security Model - **Agent tokens** grant workspace-wide access — all documents, folders, comments, versions - Agents can only access documents within their authorized workspace - `switch_document` and `/connect` validate that the target document belongs to the workspace - All page/folder mutations are workspace-scoped at the database level - Tokens can be revoked by workspace admins at any time ## Core Editing Workflow 1. **Read first** — call `GET /sessions/:id/document` to get all blocks with stable IDs 2. **Edit by block ID** — always use block IDs (UUIDs), never array indexes. IDs persist across concurrent edits. 3. **Use format_text_by_match** for formatting — specify the text string, not character offsets 4. **Re-read after edits** if you need to continue editing or verify changes 5. **Disconnect** when done ## Suggestion Mode Agents default to **suggestion mode**: edits appear as green underlines (additions) or strikethroughs (deletions) that humans can accept or reject. All edit endpoints (`insert`, `update`, `replace`, `delete`, `find-and-replace`) accept an optional `"mode"` field: - `"suggest"` (default) — wraps edits in suggestion marks for human review - `"direct"` — applies edits immediately without suggestion marks You can also: - **List suggestions**: see all pending suggestions in the document - **Accept/reject**: review another agent's suggestions - **Bulk operations**: accept-all or reject-all ## Capabilities ### Page & Folder Management (workspace-level — no document connection needed) List all workspace pages and folders. Create, rename, delete, restore pages. Create, rename, delete folders. Move pages between folders and positions. These work even without a document connection. For endpoint details: read `references/api-reference.md` (Page Management and Folder sections) ### Document Editing (requires document connection) Read, insert, update, delete, move blocks. Apply formatting (bold, italic, links, etc.). Find and replace text. Full ProseMirror JSON support for rich content. For endpoint details: read `references/api-reference.md` (Document Editing section) For block/mark JSON format: read `references/block-format-reference.md` ### Comments (requires document connection) Read all comment threads on a page. Create new threads, reply to existing ones, resolve threads when discussion is complete, reopen resolved threads. For endpoint details: read `references/api-reference.md` (Comments section) ### Version History (requires document connection) List saved versions. Save a named version snapshot before making large changes. Useful for checkpointing your work. For endpoint details: read `references/api-reference.md` (Versions section) ### Export (requires document connection) Export the current document as Markdown or HTML. Returns content as text. For endpoint details: read `references/api-reference.md` (Export section) ### Suggestion Review (requires document connection) List all pending suggestions. Accept or reject individual suggestions by ID. Bulk accept-all or reject-all. For endpoint details: read `references/api-reference.md` (Suggestions section) ## Quick Reference: Key Endpoints ### Workspace-level (no document needed) | Action | Method | Path | |--------|--------|------| | Create session | POST | `/sessions` | | Connect to document | POST | `/sessions/:id/connect` | | List pages | GET | `/sessions/:id/pages` | | Create page | POST | `/sessions/:id/pages` | | Create page + connect | POST | `/sessions/:id/pages` (with `auto_connect: true`) | | Rename page | PATCH | `/sessions/:id/pages/:pid` | | Delete page | DELETE | `/sessions/:id/pages/:pid` | | Restore page | POST | `/sessions/:id/pages/:pid/restore` | | Move page | POST | `/sessions/:id/pages/move` | | List folders | GET | `/sessions/:id/folders` | | Create folder | POST | `/sessions/:id/folders` | | Rename folder | PATCH | `/sessions/:id/folders/:fid` | | Delete folder | DELETE | `/sessions/:id/folders/:fid` | | Disconnect | DELETE | `/sessions/:id` | ### Document-level (requires document connection) | Action | Method | Path | |--------|--------|------| | Read document | GET | `/sessions/:id/document` | | Insert block | POST | `/sessions/:id/blocks` | | Format text | POST | `/sessions/:id/blocks/:bid/format-by-match` | | Delete block | DELETE | `/sessions/:id/blocks/:bid` | | List comments | GET | `/sessions/:id/comments` | | Create comment | POST | `/sessions/:id/comments` | | Save version | POST | `/sessions/:id/versions` | | Export | GET | `/sessions/:id/export?format=markdown` | | List suggestions | GET | `/sessions/:id/suggestions` | | Accept suggestion | POST | `/sessions/:id/suggestions/:sid/accept` | For the complete API with request/response schemas: read `references/api-reference.md` For common editing patterns and examples: read `references/examples.md` ## Tips - Use an **agent token** for workspace-wide access — no need to deal with invite URLs - Create a workspace-only session first, list pages, then connect to the one you need - Use `auto_connect: true` when creating a page to start editing it immediately - Always call `read_document` first to understand block structure and IDs - Use `format-by-match` instead of offset-based formatting — it handles nested blocks - When building a list, send the entire list as one `insert_block` call - Save a version before making large destructive changes - The agent identifies as `agent:Claude` in comments and suggestions - If the same text appears multiple times, use the `occurrence` parameter - Use `POST /sessions/:id/connect` to switch between documents without creating a new session - Callout blocks support variants: info, warning, error, success - Agents can only access documents within their authorized workspace — cross-workspace access is blocked

path: references/examples.md
content: # Common Workflow Examples

All examples assume you have a `SESSION_ID` from creating a session.
Base URL: `https://motion-mcp-server.fly.dev`

## Connect with Agent Token (Preferred)

```bash
# Create workspace-only session with agent token
curl -X POST $BASE/sessions \
  -H "Content-Type: application/json" \
  -d '{"agent_token": "YOUR_TOKEN", "agent_name": "Claude"}'

# Or connect to a specific document immediately
curl -X POST $BASE/sessions \
  -H "Content-Type: application/json" \
  -d '{"agent_token": "YOUR_TOKEN", "document_id": "DOC_ID", "agent_name": "Claude"}'

# Read the document
curl $BASE/sessions/$SID/document
```

## Connect with Invite Token (Legacy)

```bash
# Create session with invite token
curl -X POST $BASE/sessions \
  -H "Content-Type: application/json" \
  -d '{"document_id": "DOC_ID", "invite_token": "TOKEN", "agent_name": "Claude"}'
```

## Browse Pages First, Then Edit

```bash
# Create workspace-only session
curl -X POST $BASE/sessions \
  -H "Content-Type: application/json" \
  -d '{"agent_token": "YOUR_TOKEN", "agent_name": "Claude"}'

# List all pages
curl $BASE/sessions/$SID/pages

# Connect to a specific page
curl -X POST $BASE/sessions/$SID/connect \
  -H "Content-Type: application/json" \
  -d '{"document_id": "PAGE_ID_FROM_LIST"}'

# Now you can read and edit
curl $BASE/sessions/$SID/document
```

## Insert a Formatted Paragraph

By default, edits are in suggestion mode. Pass `"mode": "direct"` to apply immediately.

```bash
curl -X POST $BASE/sessions/$SID/blocks \
  -H "Content-Type: application/json" \
  -d '{
    "index": -1,
    "block": {
      "type": "paragraph",
      "content": [
        { "type": "text", "text": "This is " },
        { "type": "text", "text": "bold", "marks": [{ "type": "bold" }] },
        { "type": "text", "text": " and " },
        { "type": "text", "text": "linked", "marks": [{ "type": "link", "attrs": { "href": "https://example.com" } }] },
        { "type": "text", "text": " text." }
      ]
    }
  }'
```

## Create a Bullet List

```bash
curl -X POST $BASE/sessions/$SID/blocks \
  -H "Content-Type: application/json" \
  -d '{
    "index": -1,
    "block": {
      "type": "bulletList",
      "content": [
        { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "First item" }] }] },
        { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Second item" }] }] },
        { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Third item" }] }] }
      ]
    }
  }'
```

## Bold Existing Text

```bash
curl -X POST $BASE/sessions/$SID/blocks/$BLOCK_ID/format-by-match \
  -H "Content-Type: application/json" \
  -d '{ "text": "important word", "mark": "bold" }'
```

## Find and Replace

```bash
curl -X POST $BASE/sessions/$SID/blocks/$BLOCK_ID/replace \
  -H "Content-Type: application/json" \
  -d '{ "search": "old phrase", "replacement": "new phrase" }'
```

## Leave a Comment

```bash
# Create a comment thread
curl -X POST $BASE/sessions/$SID/comments \
  -H "Content-Type: application/json" \
  -d '{ "body": "This section needs more examples." }'

# Reply to the thread
curl -X POST $BASE/sessions/$SID/comments/$THREAD_ID/reply \
  -H "Content-Type: application/json" \
  -d '{ "body": "I will add some now." }'

# Resolve when done
curl -X POST $BASE/sessions/$SID/comments/$THREAD_ID/resolve
```

## Save a Version Before Big Changes

```bash
curl -X POST $BASE/sessions/$SID/versions \
  -H "Content-Type: application/json" \
  -d '{ "label": "Before restructure" }'
```

## Review Suggestions

```bash
# List all pending suggestions
curl $BASE/sessions/$SID/suggestions

# Accept a specific suggestion
curl -X POST $BASE/sessions/$SID/suggestions/$SUGGESTION_ID/accept

# Reject a specific suggestion
curl -X POST $BASE/sessions/$SID/suggestions/$SUGGESTION_ID/reject

# Accept all suggestions at once
curl -X POST $BASE/sessions/$SID/suggestions/accept-all
```

## Export as Markdown

```bash
curl "$BASE/sessions/$SID/export?format=markdown"
```

## Create a New Page and Edit It

```bash
# Create a page and connect to it in one step
curl -X POST $BASE/sessions/$SID/pages \
  -H "Content-Type: application/json" \
  -d '{ "title": "Meeting Notes - March 20", "auto_connect": true }'

# Response includes `connected: true` and `document_id`
# You can now immediately read and edit the new page
curl $BASE/sessions/$SID/document
```

## Create a Page Without Connecting

```bash
# Create a page without switching to it
curl -X POST $BASE/sessions/$SID/pages \
  -H "Content-Type: application/json" \
  -d '{ "title": "Draft Ideas" }'

# Connect to it later when ready
curl -X POST $BASE/sessions/$SID/connect \
  -H "Content-Type: application/json" \
  -d '{"document_id": "PAGE_ID_FROM_RESPONSE"}'
```

## Disconnect

```bash
curl -X DELETE $BASE/sessions/$SID
```


path: references/block-format-reference.md
content: # ProseMirror Block & Mark Reference

Documents are made of blocks. Each block is ProseMirror JSON with a `type`, optional `attrs`, and optional `content` (array of child nodes).

## Block Types

### Paragraph
```json
{ "type": "paragraph", "content": [{ "type": "text", "text": "Hello world" }] }
```

### Heading (levels 1-6)
```json
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "Section Title" }] }
```

### Bullet List
```json
{ "type": "bulletList", "content": [
  { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "First" }] }] },
  { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Second" }] }] }
] }
```

### Ordered List
```json
{ "type": "orderedList", "content": [
  { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Step one" }] }] },
  { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Step two" }] }] }
] }
```

### Task List
```json
{ "type": "taskList", "content": [
  { "type": "taskItem", "attrs": { "checked": false }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Todo" }] }] },
  { "type": "taskItem", "attrs": { "checked": true }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Done" }] }] }
] }
```

### Blockquote
```json
{ "type": "blockquote", "content": [
  { "type": "paragraph", "content": [{ "type": "text", "text": "A quote." }] }
] }
```

### Code Block
```json
{ "type": "codeBlock", "attrs": { "language": "python" }, "content": [{ "type": "text", "text": "def hello():\n    print('hi')" }] }
```

### Table
```json
{ "type": "table", "content": [
  { "type": "tableRow", "content": [
    { "type": "tableHeader", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Name" }] }] },
    { "type": "tableHeader", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Role" }] }] }
  ] },
  { "type": "tableRow", "content": [
    { "type": "tableCell", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Alice" }] }] },
    { "type": "tableCell", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Engineer" }] }] }
  ] }
] }
```

### Callout (variants: info, warning, error, success)
```json
{ "type": "callout", "attrs": { "variant": "warning", "emoji": "Warning" }, "content": [{ "type": "text", "text": "Be careful!" }] }
```

### Toggle (collapsible)
```json
{ "type": "toggle", "attrs": { "summary": "Click to expand", "open": false }, "content": [
  { "type": "paragraph", "content": [{ "type": "text", "text": "Hidden content." }] }
] }
```

### Image
```json
{ "type": "image", "attrs": { "src": "https://example.com/photo.jpg", "alt": "Description" } }
```

### Horizontal Rule
```json
{ "type": "horizontalRule" }
```

## Mark Types (Inline Formatting)

Marks are applied to text nodes via the `marks` array.

### Bold
```json
{ "type": "text", "text": "bold", "marks": [{ "type": "bold" }] }
```

### Italic
```json
{ "type": "text", "text": "italic", "marks": [{ "type": "italic" }] }
```

### Strikethrough
```json
{ "type": "text", "text": "removed", "marks": [{ "type": "strike" }] }
```

### Inline Code
```json
{ "type": "text", "text": "const x = 1", "marks": [{ "type": "code" }] }
```

### Underline
```json
{ "type": "text", "text": "underlined", "marks": [{ "type": "underline" }] }
```

### Link
```json
{ "type": "text", "text": "click here", "marks": [{ "type": "link", "attrs": { "href": "https://example.com" } }] }
```

### Highlight
```json
{ "type": "text", "text": "highlighted", "marks": [{ "type": "highlight", "attrs": { "color": "#fef08a" } }] }
```

### Text Color
```json
{ "type": "text", "text": "red text", "marks": [{ "type": "textStyle", "attrs": { "color": "#ef4444" } }] }
```

### Multiple Marks (composable)
```json
{ "type": "text", "text": "important", "marks": [{ "type": "bold" }, { "type": "italic" }] }
```

## Formatting with format-by-match

Use `POST /sessions/:id/blocks/:block_id/format-by-match` to format existing text:

| Action | Body |
|--------|------|
| Bold a word | `{ "text": "important", "mark": "bold" }` |
| Italicize | `{ "text": "see above", "mark": "italic" }` |
| Add link | `{ "text": "click here", "mark": "link", "attrs": { "href": "https://example.com" } }` |
| Highlight | `{ "text": "key point", "mark": "highlight", "attrs": { "color": "#fef08a" } }` |
| Color text | `{ "text": "error", "mark": "textStyle", "attrs": { "color": "#ef4444" } }` |
| Remove mark | `{ "text": "plain now", "mark": "bold", "remove": true }` |
| 2nd occurrence | `{ "text": "item", "mark": "bold", "occurrence": 2 }` |


path: references/api-reference.md
content: # Motion HTTP API Reference

Base URL: `https://motion-mcp-server.fly.dev`

All endpoints require a session ID obtained from `POST /sessions`.

---

## Session

### Create Session
```
POST /sessions
```

**With agent token (preferred):**
```json
{
  "agent_token": "string (64-char hex)",
  "document_id": "uuid (optional)",
  "agent_name": "Claude (optional, defaults to token name)"
}
```

**With invite token (legacy):**
```json
{
  "document_id": "uuid (optional)",
  "invite_token": "string",
  "agent_name": "Claude"
}
```

- `agent_token` — dedicated workspace token generated from Settings. Grants full workspace access.
- `invite_token` — legacy auth via invite links. One of `agent_token` or `invite_token` is required.
- `document_id` is **optional**. Without it, you get a workspace-only session (pages, folders). Add a document later with `/connect`.

Response: `{ session_id, document_id, agent_name, tools: [...] }`

### Connect to Document
```
POST /sessions/:id/connect
```
```json
{ "document_id": "uuid" }
```
Connect a workspace-only session to a specific document, or switch to a different document.
The document must belong to the session's workspace (validated server-side).
Response: `{ connected: true, document_id }`

### Disconnect
```
DELETE /sessions/:id
```
Response: `{ disconnected: true }`

---

## Document Editing

### Read Document
```
GET /sessions/:id/document
```
Response: `{ blocks: [{ id, index, json }] }`

Each block has a stable UUID `id` and ProseMirror JSON in `json`.

### Read Block
```
GET /sessions/:id/blocks/:block_id
```
Response: `{ id, json }`

### Insert Block (rich text)
```
POST /sessions/:id/blocks
```
```json
{
  "index": 0,
  "block": { "type": "paragraph", "content": [{ "type": "text", "text": "Hello" }] },
  "mode": "suggest"
}
```
Use `index: -1` to append at end. Returns `{ inserted: true, block_id, index, mode }`.
- `mode` — `"suggest"` (default) wraps in suggestion marks; `"direct"` applies immediately

### Insert Block (plain text shorthand)
```
POST /sessions/:id/blocks
```
```json
{ "index": 0, "type": "paragraph", "content": "Hello world", "mode": "suggest" }
```

### Replace Block (rich text)
```
PUT /sessions/:id/blocks/:block_id
```
```json
{
  "block": { "type": "paragraph", "content": [{ "type": "text", "text": "New" }] },
  "mode": "suggest"
}
```
- `mode` — `"suggest"` (default) marks old text as deletion + inserts new as suggestion; `"direct"` replaces immediately

### Update Block (plain text)
```
PUT /sessions/:id/blocks/:block_id
```
```json
{ "content": "Updated text", "mode": "suggest" }
```
- `mode` — `"suggest"` (default) marks old text as deletion + inserts new as suggestion; `"direct"` replaces immediately

### Delete Block
```
DELETE /sessions/:id/blocks/:block_id
```
```json
{ "mode": "suggest" }
```
Response: `{ deleted: true, block_id, mode }`
- `mode` — `"suggest"` (default) marks all text as suggested deletion; `"direct"` removes the block immediately

### Move Block
```
POST /sessions/:id/blocks/:block_id/move
```
```json
{ "to_index": 0 }
```

### Find and Replace Text
```
POST /sessions/:id/blocks/:block_id/replace
```
```json
{ "search": "old text", "replacement": "new text", "mode": "suggest" }
```
- `mode` — `"suggest"` (default) marks old text as deletion + inserts replacement as suggestion; `"direct"` replaces immediately

### Format Text by Match (preferred)
```
POST /sessions/:id/blocks/:block_id/format-by-match
```
```json
{
  "text": "welcome",
  "mark": "bold",
  "attrs": {},
  "occurrence": 1,
  "remove": false
}
```
- `text` — exact text to format (case-sensitive)
- `mark` — mark type: bold, italic, strike, code, underline, link, highlight, textStyle, color
- `attrs` — mark attributes (e.g., `{ "href": "https://..." }` for link)
- `occurrence` — which occurrence if text appears multiple times (1-based, default: 1)
- `remove` — set to true to remove the mark

### Format Text by Offset
```
POST /sessions/:id/blocks/:block_id/format
```
```json
{
  "start": 0,
  "length": 5,
  "mark": "bold",
  "attrs": {},
  "remove": false
}
```
Prefer format-by-match instead — it's more reliable.

---

## Page Management

### List Pages
```
GET /sessions/:id/pages
```
Response: `{ pages: [{ id, title, icon, parent_id, position, is_favorite }] }`

### Create Page
```
POST /sessions/:id/pages
```
```json
{
  "title": "My New Page",
  "parent_id": null,
  "folder_id": null,
  "auto_connect": false
}
```
- `auto_connect` — if `true`, automatically disconnects from the current document and connects to the newly created page. The response includes `connected: true` and `document_id`.

Response: `{ id, title, icon, parent_id, folder_id, position, is_favorite, deleted_at }`
With `auto_connect: true`: `{ id, title, ..., connected: true, document_id }`

### Rename Page
```
PATCH /sessions/:id/pages/:page_id
```
```json
{ "title": "New Title" }
```

### Delete Page (soft-delete to trash)
```
DELETE /sessions/:id/pages/:page_id
```
Response: `{ deleted: true, page_id }`

### Restore Page from Trash
```
POST /sessions/:id/pages/:page_id/restore
```
Response: `{ restored: true, page_id }`

### Move Page
```
POST /sessions/:id/pages/move
```
```json
{
  "page_id": "uuid",
  "after_page_id": "uuid or null",
  "parent_id": "uuid or null"
}
```
- `page_id` — the page to move (required)
- `after_page_id` — place after this page (null = first position)
- `parent_id` — new parent page ID (null = root level)

---

## Folders

### List Folders
```
GET /sessions/:id/folders
```
Response: `{ folders: [{ id, name, icon, color, position }] }`

### Create Folder
```
POST /sessions/:id/folders
```
```json
{ "name": "My Folder" }
```
Response: `{ id, name, icon, color, position }`

### Rename Folder
```
PATCH /sessions/:id/folders/:folder_id
```
```json
{ "name": "New Name" }
```

### Delete Folder
```
DELETE /sessions/:id/folders/:folder_id
```
Pages in the deleted folder are moved to root level (folder_id set to null).

---

## Comments

### List Comment Threads
```
GET /sessions/:id/comments
```
Response: `{ threads: [{ id, page_id, is_resolved, created_by, created_at, comments: [{ id, thread_id, author_id, body, mentions, created_at }] }] }`

### Create Comment Thread
```
POST /sessions/:id/comments
```
```json
{
  "body": "This section needs more detail.",
  "mentions": []
}
```
Response: `{ thread_id }`

### Reply to Thread
```
POST /sessions/:id/comments/:thread_id/reply
```
```json
{
  "body": "I agree, I'll expand on it.",
  "mentions": []
}
```

### Resolve Thread
```
POST /sessions/:id/comments/:thread_id/resolve
```

### Reopen Thread
```
POST /sessions/:id/comments/:thread_id/reopen
```

---

## Version History

### List Versions
```
GET /sessions/:id/versions
```
Response: `{ versions: [{ id, page_id, label, created_by_name, actor_type, trigger_type, created_at }] }`

### Save Version
```
POST /sessions/:id/versions
```
```json
{ "label": "Before restructure" }
```
Label is optional. Saves a snapshot of the current document state.

---

## Export

### Export Document
```
GET /sessions/:id/export?format=markdown
```
Supported formats: `markdown`, `html`

Response: `{ format, content }`

The `content` field contains the full document as a string in the requested format.

---

## Suggestions

### List Suggestions
```
GET /sessions/:id/suggestions
```
Response: `{ suggestions: [{ suggestionId, authorId, authorName, createdAt, type, text, blockId, offset, length }] }`

- `type` is either `"add"` (suggested addition) or `"delete"` (suggested deletion)

### Accept Suggestion
```
POST /sessions/:id/suggestions/:suggestion_id/accept
```
- For additions: keeps the text, removes the suggestion mark
- For deletions: removes the text

### Reject Suggestion
```
POST /sessions/:id/suggestions/:suggestion_id/reject
```
- For additions: removes the text
- For deletions: keeps the text, removes the suggestion mark

### Accept All Suggestions
```
POST /sessions/:id/suggestions/accept-all
```
Response: `{ accepted: count }`

### Reject All Suggestions
```
POST /sessions/:id/suggestions/reject-all
```
Response: `{ rejected: count }`


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)