{"serverId":"mcp-registry/ai.trydock/dock","tools":[{"name":"add_column","fixed":{"name":"add_column","description":"Append a single column to a workspace's table schema. Position is auto-computed as next-after-max so the contiguity invariant holds. Key collision (409) if a column with the same key already exists. Editor role required. Use this for per-column additions; use get_workspace_schema + update_surface with `columns` for full schema replacement or reordering. Multi-surface workspaces accept `surface_slug` to target a specific table sheet (use `list_surfaces` to enumerate); omit to fall through to the ","inputSchema":{"type":"object","required":["slug","key","label","type"],"properties":{"key":{"type":"string","description":"Field name in row.data. Lowercase + underscores recommended; 1-64 chars."},"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"type":{"enum":["text","longtext","number","status","person","date","url","checkbox","select"],"type":"string","description":"Column type. See get_workspace_schema for examples."},"label":{"type":"string","description":"Human-readable header shown in the sheet."},"width":{"type":"number","description":"Optional. Initial column width in px."},"options":{"type":"array","items":{"type":"object","properties":{"color":{"type":"string"},"label":{"type":"string"},"value":{"type":"string"}}},"description":"Required for `status` + `select` types. The allowed values shown in the dropdown."},"description":{"type":"string","description":"Optional. Human-readable tooltip shown in the column header. Up to 4000 characters (the same limit update_surface applies)."},"surface_slug":{"type":"string","description":"Optional. The slug of the specific table surface to add the column to. Omit on single-table workspaces; required on multi-table workspaces if you don't want the primary table surface (lowest position)."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":549,"tokensAfter":549},{"name":"add_comment","fixed":{"name":"add_comment","description":"Post a new comment on any target in a workspace: a row, a cell, a doc text range, an html element, an entire surface, or the workspace itself. Polymorphic target shape mirrors the REST POST /api/workspaces/:slug/comments. For threading, pass `parentId` to hang the new comment as a reply (the server flattens nested replies to single depth and auto-unresolves a resolved parent). Mentions are an array of `{ kind: 'user'|'agent', id, label }` triples; the server validates each mention's access to th","inputSchema":{"type":"object","required":["slug","target","body"],"properties":{"body":{"type":"string","description":"Comment body (plain text or markdown). 1-5000 chars."},"slug":{"type":"string","description":"The workspace slug ('my-workspace' or 'my-org/my-workspace')."},"target":{"type":"object","description":"Polymorphic target. Shapes:\n  { type: 'row',           rowId: '<cuid>' }\n  { type: 'cell',          rowId: '<cuid>', columnKey: '<key>' }\n  { type: 'doc_range',     surfaceSlug: '<slug>', anchor: { from: <number>, to: <number>, text: '<plain>' } }\n  { type: 'html_element',  surfaceSlug: '<slug>', anchor: { selector: '<css>', text?: '<plain>' } }\n  { type: 'video_frame',   surfaceSlug: '<slug>', anchor: { videoId: '<id from list_videos>', t: <seconds>, box?: { x, y, w, h } } }  (box in fractions of the frame, 0-1 from the top-left)\n  { type: 'surface',       surfaceSlug: '<slug>' }\n  { type: 'workspace' }","additionalProperties":true},"mentions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Optional `[{ kind, id, label }]` mentions. Each mention's principal must have workspace access. Fires inbox + email + webhook fan-out for newly-mentioned recipients only."},"parentId":{"type":"string","description":"Optional parent comment id. When passed, this comment becomes a reply in the thread. Nested replies flatten to single-depth (reply-to-reply re-points at the root). Re-opens a resolved parent."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":560,"tokensAfter":560},{"name":"add_video","fixed":{"name":"add_video","description":"Add a video to the end of a video surface, from an https URL to an mp4, webm, mov or m4v file. A surface holds several videos (variants, cuts, a short playlist) that people flip through; give each a short `label` like \"Variant B\" so they can tell them apart. Create the surface first with `create_surface` and `kind: \"video\"`. Pass `content_type` when the URL has no file extension. For a file on your own machine, get a link with `create_video_upload` first. Dock downloads the file and keeps its OW","inputSchema":{"type":"object","required":["slug","url"],"properties":{"url":{"type":"string","description":"https URL of the video file. Dock stores its own copy."},"name":{"type":"string","description":"Optional file or clip name. Max 200 chars."},"slug":{"type":"string","description":"The workspace slug. Accepts bare or org-prefixed form."},"label":{"type":"string","description":"Optional short label shown in the switcher, e.g. \"Variant B\". Max 60 chars."},"content_type":{"type":"string","description":"Optional: video/mp4, video/webm, video/quicktime or video/x-m4v. Inferred from the URL's extension when omitted."},"surface_slug":{"type":"string","description":"Optional video surface slug. Omit for the first video surface."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":357,"tokensAfter":357},{"name":"address_book","fixed":{"name":"address_book","description":"Lists the agents you can reach — your own agents AND agents shared to you from other owners. To MESSAGE any of them, use the `message_teammate` tool (it delivers AND wakes the recipient). Do NOT use send_message for an agent — that path does not wake them and the message can strand. Each entry has three identity fields, and it matters which you use:\n• `name` — the agent's DISPLAY NAME, for reading only. Never address by it (names are not unique and change).\n• `address` (`<name>@<owner>.<org>`) —","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":172,"tokensAfter":172},{"name":"bulk_create_rows","fixed":{"name":"bulk_create_rows","description":"Append many rows to a workspace's table surface in ONE call — the bulk version of create_row. Use this instead of looping create_row when ingesting more than a few rows (lower latency + token cost). Pass `rows` as an array of `{ data: {...} }` objects, each `data` a column-name → value map (same shape create_row takes). Up to 500 rows per call. WRITE AS YOU GO on long jobs (scraping, enrichment, any list you build over many steps): send each batch of about 25-50 rows as soon as it is ready, neve","inputSchema":{"type":"object","required":["slug","rows"],"properties":{"rows":{"type":"array","items":{"type":"object"},"description":"Rows to create. Each item is an object with a `data` field: `{ \"data\": { \"title\": \"...\", \"status\": \"drafted\" } }`. 1-500 items."},"slug":{"type":"string","description":"The workspace slug (bare or org-prefixed)."},"surface_slug":{"type":"string","description":"Optional target table surface slug (use list_surfaces). Omit to use the workspace's primary table surface. The whole batch lands on this one surface."},"auto_create_columns":{"type":"boolean","description":"When true, append a text column for every key (across all rows) that doesn't map to an existing column, in one schema extension. Default false."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":371,"tokensAfter":371},{"name":"bulk_update_rows","fixed":{"name":"bulk_update_rows","description":"Update many existing rows in ONE call — the bulk version of update_row. Use this instead of looping update_row when changing more than a few rows (much lower latency + token cost for sheet edits). Pass `updates` as an array of `{ id, data }`: `id` is the row id (from list_rows / get_row), `data` is a column-name → value map of just the cells to change. Each row's data is MERGED into the existing row (last-write-wins per field), so you only send the cells you're changing. Up to 500 rows per call.","inputSchema":{"type":"object","required":["slug","updates"],"properties":{"slug":{"type":"string","description":"The workspace slug (bare or org-prefixed)."},"updates":{"type":"array","items":{"type":"object"},"description":"Rows to update. Each item is `{ \"id\": \"<row id>\", \"data\": { \"status\": \"done\" } }` — `data` holds only the cells to change (merged into the existing row). 1-500 items."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":273,"tokensAfter":273},{"name":"check_workspace_private","fixed":{"name":"check_workspace_private","description":"Check, on the server, whether a workspace is private to your owner right now: private, every member is your owner or one of your owner's agents, no open invite, no active file share link. Call it BEFORE writing anything owner-private (Voice profile, Second Brain, personal notes) and before every such write session. Returns { ok, reasons, private, member_count, non_owner_members, pending_invites, active_file_shares }, counts only. If ok is not true, or the call errors, times out or is unavailable","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":234,"tokensAfter":234},{"name":"create_file_upload","fixed":{"name":"create_file_upload","description":"Get a one-time upload link for a file that is on YOUR machine (a PDF, image, spreadsheet, audio, archive, anything over ~3 MB), so you can put it into Dock without a public host. Returns a ready `curl` command: run it with your file path in place of `<path to your file>`; its JSON response has a `url`. Then call `upload_file` with that `url` (plus `surface_slug` and `filename`). The link accepts ONE upload of an allowed file type, up to your plan's per-file cap: 5 MB on a trial or Starter, and 1","inputSchema":{"type":"object","required":["slug","filename"],"properties":{"slug":{"type":"string","description":"The workspace slug (bare or org-prefixed)."},"filename":{"type":"string","description":"The file's name, e.g. \"q3-report.pdf\". Its extension sets the type unless you pass `content_type`."},"content_type":{"type":"string","description":"Optional MIME type, e.g. application/pdf. Must be a type Dock accepts."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":271,"tokensAfter":271},{"name":"create_row","fixed":{"name":"create_row","description":"Append a new row to a workspace's table surface. The data field is a JSON object with column-name keys. Status column accepts: drafted, queued, sealed, active, blocked. Works on any workspace; columns auto-seed on the first row if the table surface is empty. Multi-surface workspaces accept `surface_slug` to target a specific sheet (use `list_surfaces` to enumerate); omit it to fall through to the workspace's primary table surface.\n\n**Unmapped data fields:** Keys in `data` that don't match any ex","inputSchema":{"type":"object","required":["slug","data"],"properties":{"data":{"type":"object","description":"Row data as a JSON object (e.g. {\"title\": \"My post\", \"status\": \"drafted\", \"notes\": \"Initial draft\"})","additionalProperties":true},"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"surface_slug":{"type":"string","description":"Optional table surface slug for multi-surface workspaces. Omit to write to the workspace's primary table surface. 400 if the slug is a doc surface, archived, or doesn't exist."},"auto_create_columns":{"type":"boolean","description":"When true, the server auto-creates a text column for every key in `data` that doesn't already exist on the surface, then writes the row in the same call. Returns `created_columns` in the response listing the new column defs. Default false: unmapped keys are still stored on the row but won't render in the UI until you `add_column` them yourself."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":458,"tokensAfter":458},{"name":"create_support_ticket","fixed":{"name":"create_support_ticket","description":"File a support ticket. For agents on the Dock team only: any other caller is refused. If you are not on the Dock team, email agent-support@trydock.ai with the problem yourself if you have your own email address (send_email on cloud, email_owner on desktop); otherwise ask your human to (they can also file from Settings → Support). Prefer request_limit_increase when the user is simply hitting a plan cap.","inputSchema":{"type":"object","required":["kind","title","body"],"properties":{"body":{"type":"string","description":"Detailed description (5-10000 chars). For bugs: include what you did, what happened, what you expected. For feature requests: the use case."},"kind":{"enum":["bug","feature","billing","question","other"],"type":"string","description":"Ticket category."},"title":{"type":"string","description":"Short headline (3-200 chars). Be specific: 'Table view loses focus on cell edit' beats 'broken'."},"context":{"type":"object","description":"Optional structured metadata echoed into the GitHub issue (workspace slug, URL, error trace, etc)."},"attachmentUrls":{"type":"array","items":{"type":"string"},"description":"Optional list of screenshot/attachment URLs to embed in the issue. URLs must be on Dock's file store: for a file on your machine, call `create_file_upload` (on any workspace you can edit), run the curl it returns and pass the `url` from its response. Max 4."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":402,"tokensAfter":402},{"name":"create_surface","fixed":{"name":"create_surface","description":"Create a new surface inside a workspace. `kind` picks `table`, `doc`, `html`, `files`, or `video`. Optional `slug` (lowercase kebab-case, 3-64 chars); when omitted the server slugifies `name` and appends a numeric suffix on collision. Optional `columns` sets a `table`'s starting columns; without it the table starts EMPTY and your first row write creates the columns its data needs. Ignored for other kinds. `html` surfaces start with an empty body — write content via `update_html`. `files` surface","inputSchema":{"type":"object","required":["slug","kind","name"],"properties":{"kind":{"enum":["table","doc","html","files","video"],"type":"string","description":"Surface kind. `table` for rows + columns, `doc` for TipTap body, `html` for a sandboxed HTML page, `files` for uploaded files + folders (put a local file in with `create_file_upload` then `upload_file`), `video` for a surface of videos people flip through (a local video goes in with `create_video_upload` then `add_video`)."},"name":{"type":"string","description":"Display name of the surface. 1-64 chars."},"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"columns":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Optional initial columns for `table` kind. Same shape as get_workspace_schema returns. Omit for an empty table."},"surface_slug":{"type":"string","description":"Optional URL-friendly slug for the surface (lowercase kebab-case, 3-64 chars). Auto-derived from `name` when omitted."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":477,"tokensAfter":477},{"name":"create_video_upload","fixed":{"name":"create_video_upload","description":"Get a one-time upload link for a video file that is on YOUR machine (one you rendered, edited or downloaded), so you can put it on a video surface. `add_video` needs an https URL, and inline base64 is unreliable for video, so use this instead. Returns a ready `curl` command: run it with your file path in place of `<path to your file>`; its JSON response has a `url`. Pass that `url` to `add_video`. The link accepts ONE upload of an mp4, webm, mov or m4v file up to 100 MB and expires in 15 minutes","inputSchema":{"type":"object","required":["slug","filename"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts bare or org-prefixed form."},"filename":{"type":"string","description":"The file's name, e.g. \"founder-leads-v3.mp4\". Its extension sets the type unless you pass `content_type`."},"content_type":{"type":"string","description":"Optional: video/mp4, video/webm, video/quicktime or video/x-m4v."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":275,"tokensAfter":275},{"name":"create_webhook","fixed":{"name":"create_webhook","description":"Register a new webhook endpoint on an org. The URL must be public (loopback / private ranges / cloud metadata are blocked at create-time AND re-validated by DNS at delivery-time). Events array filters which event kinds the endpoint receives: pick from row.* / comment.* / member.* / workspace.* / doc.*; an empty array means \"none\" so always pass at least one. Returns the signing `secret` exactly once (whsec_… prefixed); store it on the receiver to verify HMAC signatures on incoming requests.","inputSchema":{"type":"object","required":["org_slug","url","events"],"properties":{"url":{"type":"string","description":"Public HTTPS URL to POST events to. Loopback (127.0.0.0/8, ::1), RFC1918 private ranges, link-local, and cloud-metadata addresses (169.254.169.254, etc.) are rejected. Max 2048 chars."},"events":{"type":"array","items":{"type":"string"},"description":"Event kinds to subscribe to. Pick from: row.created, row.updated, row.deleted, row.sealed, comment.added, comment.deleted, member.invited, member.joined, member.removed, member.role_changed, workspace.created, workspace.renamed, workspace.columns_updated, workspace.visibility_changed, workspace.archived, doc.created, doc.updated, doc.heading_added, doc.mention_added."},"org_slug":{"type":"string","description":"Org slug"}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":373,"tokensAfter":373},{"name":"create_workspace","fixed":{"name":"create_workspace","description":"Create a new workspace in the caller's org. Works for both user and agent callers; agent-created workspaces attribute to the agent and enroll the agent's owning user as a co-owner so the human sees it in their dashboard. The new workspace is seeded with one primary surface matching `mode`: `doc` → a Notes surface (for prose), `table` → a Sheet surface (for records), `html` → a Mockup surface (sandboxed HTML preview). Decide the surface before you create: prose (briefs, notes, summaries, drafts) ","inputSchema":{"type":"object","required":["name"],"properties":{"mode":{"enum":["table","doc","html"],"type":"string","description":"Kind of the seeded primary surface — choose by what you're about to write. `doc` mints a Notes surface: use it for PROSE (briefs, notes, summaries, drafts, status reports). `table` mints a Sheet surface: use it for RECORDS (tasks, leads, rows, anything with shared columns). `html` mints a Mockup surface (sandboxed HTML preview): opt in by passing it explicitly whenever the deliverable IS html — a landing-page mockup, a dashboard, a design preview, any page meant to be looked at — and write it with `update_html`; it is never inferred for ambiguous content. Pass this explicitly: when omitted, `initial_markdown` resolves the surface to a `doc`; with neither, an agent caller gets a guided error asking it to choose (no silent default to a Sheet, which would be the wrong surface if you meant prose). Add more surfaces of any kind via `create_surface` later."},"name":{"type":"string","description":"The workspace name. Required. Used to derive a slug if you don't pass one."},"slug":{"type":"string","description":"Optional URL-friendly slug (lowercase, kebab-case, 3-64 chars). Auto-derived from `name` if omitted; if the derived slug collides within your org, a -N suffix is appended."},"initial_markdown":{"type":"string","description":"Optional Markdown body to seed the workspace's doc surface on create. CommonMark + GFM (tables, task lists, strikethrough). When provided AND mode is omitted, mode defaults to 'doc'. Skips the empty default-column scaffolding too. Ignored when mode='html' (no markdown equivalent for HTML surfaces — use `update_html` after create). Use this for any prose-shaped output (briefs, summaries, status updates, changelog entries) instead of create + update_doc with hand-built JSON. `markdown` is accepted as an alias. The reply reports the stored doc body length, so check it is not 0."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":697,"tokensAfter":697},{"name":"delete_file","fixed":{"name":"delete_file","description":"Delete a file by id. It moves to a 30-day trash, and its old link stops working immediately. Restorable during those 30 days via the REST PATCH endpoint (`PATCH /api/workspaces/{slug}/files/{id} body: {restore:true}`); after that the cleanup deletes it for good and refunds the storage quota. If the delete can't complete, the file is left as it was and the call fails, so retry. Editor role required.","inputSchema":{"type":"object","required":["slug","file_id"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL."},"file_id":{"type":"string","description":"The file cuid (from list_files)."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":213,"tokensAfter":213},{"name":"delete_row","fixed":{"name":"delete_row","description":"Permanently delete a row from a workspace. This action cannot be undone.","inputSchema":{"type":"object","required":["slug","rowId"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"rowId":{"type":"string","description":"The row ID to delete"}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":131,"tokensAfter":131},{"name":"delete_surface","fixed":{"name":"delete_surface","description":"Archive a surface (soft-delete). Rows + doc body are preserved: restore it with `update_surface` and `archived: false` (archived surfaces are listed by `list_surfaces archived: true`). Idempotent: calling on an already-archived surface returns its current archivedAt unchanged. Cannot archive the only live surface in a workspace; create another first. Editor role required. Emits `surface.archived`.","inputSchema":{"type":"object","required":["slug","surface_slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"surface_slug":{"type":"string","description":"The slug of the surface to archive."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":232,"tokensAfter":232},{"name":"delete_webhook","fixed":{"name":"delete_webhook","description":"Permanently delete a webhook endpoint. The URL stops receiving events immediately and the secret is destroyed; recreate from scratch if you need to re-add it. To pause without losing config, use update_webhook with active:false instead.","inputSchema":{"type":"object","required":["org_slug","webhook_id"],"properties":{"org_slug":{"type":"string","description":"Org slug"},"webhook_id":{"type":"string","description":"Webhook id (from list_webhooks)"}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":138,"tokensAfter":138},{"name":"delete_workspace","fixed":{"name":"delete_workspace","description":"Archive a workspace. Soft-delete: rows, doc body, and activity history are preserved, and the workspace can be restored from the Archived filter on the Workspaces page. Every member loses access immediately. Idempotent: calling on an already-archived workspace returns its current archivedAt without changing anything. Requires the owner role on the workspace. Pass `mode: \"web\"` to surface a click-to-approve URL for the human (recommended for any non-trivial workspace); the first call returns { st","inputSchema":{"type":"object","required":["slug"],"properties":{"mode":{"enum":["immediate","web"],"type":"string","description":"Consent surface. 'immediate' (default) executes on the agent's role. 'web' returns an approval_url the user clicks in a browser; recommended for any workspace your user might miss."},"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":302,"tokensAfter":302},{"name":"get_billing","fixed":{"name":"get_billing","description":"When your human's access stops (`yourAccess`: their trial end or plan state; the ONLY date to quote for that), plus the caller's org resource caps and usage: active counts and caps for agents, members, workspaces, rows per workspace, API calls, webhooks, and the message bundle, plus card-on-file and next invoice date. The org caps are not your human's plan or trial. These caps are live and enforced — use them. This tool does NOT price anything: Dock sells Dock Tokens, per user. Any price-shaped ","inputSchema":{"type":"object","properties":{}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":164,"tokensAfter":164},{"name":"get_comment_thread","fixed":{"name":"get_comment_thread","description":"Fetch a single comment with its replies + reactions in one round trip. Pass any comment id in the thread (root or reply). Returns `{ comment, replies }` where each entry includes aggregated reactions (`emoji`, `count`, `mine`). Use this when an agent receives a `comment.added` webhook with a `parentId` and needs full context before composing a reply.","inputSchema":{"type":"object","required":["comment_id"],"properties":{"comment_id":{"type":"string","description":"Comment id (any node in the thread)."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":155,"tokensAfter":155},{"name":"get_doc","fixed":{"name":"get_doc","description":"Read a workspace's doc (TipTap rich-text) body. Format is negotiable via `format`: `markdown` (default — CommonMark + GFM, ready to feed to an LLM or render in a non-ProseMirror surface), `content` (TipTap JSON, round-trippable into update_doc for structural edits), `text` (plain text, best for search, summarisation, word-count heuristics), or `all` for the legacy three-in-one shape. Default is `markdown` because it's the slice agents need 95% of the time and the JSON form on a long doc can blow","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"format":{"enum":["markdown","content","text","all"],"type":"string","description":"Which serialization to return. Default `markdown`. Use `content` to round-trip TipTap JSON back into update_doc for structural edits. Use `all` for the legacy three-in-one shape (heavier; only do this when you genuinely need every form in the same call)."},"heading":{"type":"string","description":"Optional. Return only the section under this heading (exact heading text): the heading through the next heading of the same or higher level, the same range update_doc_section writes. Use it to read a large doc a section at a time. A missing heading returns the headings that exist; a heading used more than once is refused rather than guessed."},"surface_slug":{"type":"string","description":"Optional doc surface slug for multi-doc workspaces. Omit to read the primary doc surface. Use list_surfaces to see available slugs."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":482,"tokensAfter":482},{"name":"get_file","fixed":{"name":"get_file","description":"Fetch metadata + a download URL for a single file by id. The `download_url` field is a Dock URL (not a storage URL): GET it with your Dock API key and access is re-checked on every request. Useful for an agent reading file contents server-side (HTTP GET the URL) or surfacing a download link in a reply.","inputSchema":{"type":"object","required":["slug","file_id"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL."},"file_id":{"type":"string","description":"The file cuid (from list_files). Surface + workspace are derived from the file row, so no surface_slug arg is needed."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":208,"tokensAfter":208},{"name":"get_html","fixed":{"name":"get_html","description":"Read an HTML surface's body. HTML surfaces (Surface.kind=\"html\") store mockup or full-page content as three text fields (html, css, js) rendered together inside a sandboxed iframe. Use `list_surfaces` to enumerate html surfaces in a workspace. Omit `surface_slug` to read the primary html surface; pass it to target a specific html surface. Empty (never-written) html surfaces return { html:\"\", css:\"\", js:\"\" }. 404 when `surface_slug` doesn't match a live html surface. Requires viewer role.","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts bare or org-prefixed form."},"surface_slug":{"type":"string","description":"Optional html surface slug. Omit to read the primary html surface."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":227,"tokensAfter":227},{"name":"get_recent_events","fixed":{"name":"get_recent_events","description":"Get recent activity events for a workspace. Who did what, when. Useful for understanding what's happened since you last looked.","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"limit":{"type":"number","description":"Max events to return (default 20)"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":150,"tokensAfter":150},{"name":"get_row","fixed":{"name":"get_row","description":"Fetch a single row by id without listing the full table. Useful when a cue payload carries a row id and the agent only needs that one record. Returns the same row shape as list_rows.","inputSchema":{"type":"object","required":["slug","rowId"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"rowId":{"type":"string","description":"The row id"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":158,"tokensAfter":158},{"name":"get_video_transcript","fixed":{"name":"get_video_transcript","description":"What is said in a video in a video surface, as timestamped segments. Transcribed on first use, then cached on the video until its file changes. Pass `start`/`end` for a span. Viewer role required.","inputSchema":{"type":"object","required":["slug"],"properties":{"end":{"type":["string","number"],"description":"Optional end of the span. As a person writes it (\"1:34\", \"1:34.5\", \"01:02:03\") or as seconds (94)."},"slug":{"type":"string","description":"The workspace slug. Accepts bare or org-prefixed form."},"start":{"type":["string","number"],"description":"Optional start of the span. As a person writes it (\"1:34\", \"1:34.5\", \"01:02:03\") or as seconds (94)."},"refresh":{"type":"boolean","description":"Transcribe again instead of using the cached transcript."},"video_id":{"type":"string","description":"The video's id, from list_videos. Optional when the surface holds a single video."},"surface_slug":{"type":"string","description":"Optional video surface slug. Omit for the first video surface."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":296,"tokensAfter":296},{"name":"get_workspace","fixed":{"name":"get_workspace","description":"Get details about a specific workspace by its slug, including columns of its primary table surface, member count, and row count. A workspace contains one or more surfaces of any kind, in any combination. Use `list_surfaces` to enumerate every surface, then that surface's own tools (`list_rows`, `get_doc`, `get_html`, ...) to read or write it.","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug, e.g. 'reddit-tracker'. Accepts either the bare slug or the org-prefixed form ('my-org/reddit-tracker') as shown in the dashboard URL."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":180,"tokensAfter":180},{"name":"get_workspace_schema","fixed":{"name":"get_workspace_schema","description":"Return a table surface's column definitions so an agent knows what keys create_row/update_row will accept. Each column has `key` (the field name in row.data), `label` (human-readable), `type` (text | longtext | number | status | person | date | url | checkbox | select), `position`, and, for status/select columns, the allowed `options`. Empty array on doc-only workspaces; callers should still be able to write rows (columns auto-seed on first write). Multi-surface workspaces accept `surface_slug` ","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"surface_slug":{"type":"string","description":"Optional. The slug of the specific table surface to read columns from. Omit on single-table workspaces; required on multi-table workspaces if you don't want the primary table surface (lowest position)."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":303,"tokensAfter":303},{"name":"list_api_keys","fixed":{"name":"list_api_keys","description":"List API keys. Agent callers see only the key they're authenticated with (a one-row response: id, prefix, lastUsedAt, the workspace it's bound to). User callers (cookie session) see every key for every agent they own. Plaintext is never returned; the key body is shown only once at create/rotate time.","inputSchema":{"type":"object","properties":{}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":109,"tokensAfter":109},{"name":"list_capabilities","fixed":{"name":"list_capabilities","description":"List the provider API keys your owner has stored in their Vault (e.g. Gemini, ElevenLabs, OpenAI) so you can use them in a task. Returns `capabilities`: the exact NAMES of the keys your owner has vaulted. Pass one of these names verbatim to `pull_capability` — do NOT guess or normalize it (a key may be vaulted as \"Gemini\", not \"GEMINI_API_KEY\"). Names only, never secrets, so this is safe to call freely. IMPORTANT: this lists what EXISTS in the Vault — it is discovery, NOT authorization to use a ","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":174,"tokensAfter":174},{"name":"list_comments","fixed":{"name":"list_comments","description":"List comments in a workspace. Filter by `target_type` (row, cell, doc_range, html_element, surface, workspace), `target_id`, `surface` (returns every comment anchored to any element of one surface, useful for 'open threads on this surface'), `status` (open | resolved | all, default open), `mentioning_me: true` for comments that @-mention the caller, or `author: <principalId>` for comments by a specific user/agent. Returns up to 200 comments per call ordered by `createdAt` asc, with `surfaceSlug`","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug."},"limit":{"type":"number","description":"Max results (1-200, default 50)."},"author":{"type":"string","description":"Filter by author principal id. Useful for 'comments by Argus on this workspace' agent loops."},"offset":{"type":"number","description":"Number of comments to skip for pagination."},"status":{"enum":["open","resolved","all"],"type":"string","description":"Resolution state filter. Default `open`."},"surface":{"type":"string","description":"Surface slug filter. Returns every comment anchored anywhere inside this surface (doc_range / html_element / surface scope, plus row + cell comments on rows that live on the surface). 404 silently if the surface is archived (returns empty list)."},"target_id":{"type":"string","description":"Filter by exact target id. For cells the id is `<rowId>:<columnKey>`; for doc_range/html_element/surface it's the Surface cuid. Combine with target_type for unambiguous filtering."},"target_type":{"enum":["row","cell","doc_range","html_element","video_frame","surface","workspace"],"type":"string","description":"Filter by comment target type."},"mentioning_me":{"type":"boolean","description":"When true, only return comments that @-mention the calling principal. Equivalent to REST `?mentioning=me`."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":534,"tokensAfter":534},{"name":"list_files","fixed":{"name":"list_files","description":"List the folder + file children of a Files surface (kind='files'). Folders sorted first by position then name; files sorted by name. Returns folders[], files[] with cuids agents can pass to `get_file` / `delete_file`. `parent_folder_id` defaults to null (= root of the surface); pass a folder id to descend into a sub-folder.","inputSchema":{"type":"object","required":["slug","surface_slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL."},"surface_slug":{"type":"string","description":"Files-kind surface slug within the workspace. Use list_surfaces to enumerate; the Files surface kind is 'files'."},"parent_folder_id":{"type":"string","nullable":true,"description":"Folder id to descend into. Omit (or pass null) for the surface root."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":255,"tokensAfter":255},{"name":"list_my_support_tickets","fixed":{"name":"list_my_support_tickets","description":"List the support tickets YOU filed (READ-ONLY) — the same tickets `create_support_ticket` creates, so you can check on one after filing it. Scoped exactly like the dashboard at /settings/support: you see tickets you filed yourself, and — if you are a human caller — tickets filed by agents you own. You never see another person's tickets, and never another org's. Results are for your CURRENT org: a ticket you filed in a different org shows up when you are in that org, not here. Newest first, cappe","inputSchema":{"type":"object","properties":{"limit":{"type":"number","description":"Max tickets to return, 1-100. Default 30."},"status":{"enum":["open","in_progress","closed"],"type":"string","description":"Optional. Only return tickets in this lifecycle state. Omit to return every state."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":236,"tokensAfter":236},{"name":"list_recent_files","fixed":{"name":"list_recent_files","description":"List the 50 most recently updated files in a Files surface, sorted by `updatedAt` descending. Flat surface-wide list; ignores folder structure. Useful for an agent answering 'what changed lately' or 'show me yesterday's uploads' without paging through the folder tree. Folders are omitted from this view.","inputSchema":{"type":"object","required":["slug","surface_slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL."},"surface_slug":{"type":"string","description":"Files-kind surface slug within the workspace."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":194,"tokensAfter":194},{"name":"list_rows","fixed":{"name":"list_rows","description":"List rows in a workspace's table surface. Returns rows with their data (a JSON object of column-name to value), creation time, the principal who created/updated each row, AND the row's `surface_slug` (the sheet it lives on). Empty array if no rows have been added yet. Multi-surface workspaces: pass `surface_slug` to scope to one sheet; omit to return rows from every surface in the workspace (back-compat: pre-multi-surface clients keep working).","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"limit":{"type":"number","description":"Max rows to return (default 100, max 1000)"},"offset":{"type":"number","description":"Number of rows to skip (for pagination)"},"surface_slug":{"type":"string","description":"Optional table surface slug for multi-surface workspaces. Filter rows to one sheet. Omit to return rows from every surface (legacy single-sheet clients see no change). 400 if the slug is a doc surface, archived, or doesn't exist."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":340,"tokensAfter":340},{"name":"list_sheet_functions","fixed":{"name":"list_sheet_functions","description":"List the Dock Sheets formula functions an agent can use in a cell carrier. Returns the canonical name, signature, one-sentence description, category (Math/Logic/Text/Date/Lookup/Predicates), rollout slice (v1/v2/v3/v4), and at least one worked example per function. Use this before writing a formula via update_row / create_row so you only reference functions that actually exist (no #NAME? errors). Also returns the alias map (e.g. CONCAT → CONCATENATE) so you can pick the canonical name even when ","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Optional case-insensitive substring filter; matches function name, description, and signature."},"slice":{"enum":["v1","v2","v3","v4"],"type":"string","description":"Optional rollout-slice filter."},"category":{"enum":["Math","Logic","Text","Date","Lookup","Predicates","Other"],"type":"string","description":"Optional category filter."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":270,"tokensAfter":270},{"name":"list_surfaces","fixed":{"name":"list_surfaces","description":"List the surfaces inside a workspace. A workspace can hold surfaces of any kind (see `create_surface`), in any combination, one or many of each; this tool tells you exactly what it has. Each surface has its own slug used in surface-scoped tool calls. Order matches the on-screen surface order. Archived surfaces are hidden by default; pass `archived: true` to include them.","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"archived":{"type":"boolean","description":"Include archived surfaces too. Default false (live surfaces only)."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":227,"tokensAfter":227},{"name":"list_videos","fixed":{"name":"list_videos","description":"List the videos in a video surface, in play order: id, position, label, name, url, content type, size and duration. Omit `surface_slug` for the workspace's first video surface. Viewer role required.","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts bare or org-prefixed form."},"surface_slug":{"type":"string","description":"Optional video surface slug. Omit for the first video surface."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":143,"tokensAfter":143},{"name":"list_webhooks","fixed":{"name":"list_webhooks","description":"List webhook endpoints registered on an org. Returns each webhook's id, url, subscribed events, active flag, and an 8-char `secretPreview` of the signing secret (full secret is only returned at create / rotate-secret time). Any org member (user or agent) can list. Use to audit what's subscribed before adding or removing endpoints.","inputSchema":{"type":"object","required":["org_slug"],"properties":{"org_slug":{"type":"string","description":"Org slug. The webhook collection is org-scoped, not workspace-scoped; one URL receives events from every workspace in the org."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":172,"tokensAfter":172},{"name":"list_workspace_members","fixed":{"name":"list_workspace_members","description":"List principals with explicit access to a workspace. Returns users (id, name, email; email visible only when the caller is in the same org) and agents (id, name, brandKey) along with their role (owner | editor | commenter | viewer). Used by agents to verify a workspace is actually shared before writing output the team is expected to see. It cannot tell you whether a workspace is private to your owner (it does not show whose agent each agent is, open invites or file share links): use check_worksp","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":234,"tokensAfter":234},{"name":"list_workspaces","fixed":{"name":"list_workspaces","description":"List all workspaces the authenticated principal has access to. Returns workspace name (slug), mode (the default-view preference for the first surface), and creation date. A workspace is a container of one or more surfaces of any kind (see `create_surface`), in any combination, one or many of each. Use `list_surfaces` to see what a given workspace actually contains.","inputSchema":{"type":"object","properties":{}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":128,"tokensAfter":128},{"name":"move_rows","fixed":{"name":"move_rows","description":"Atomically move N rows from their current sheet(s) to a target sheet inside the same workspace. Use for programmatic data migration: dropping a batch of agent-produced drafts onto the right sheet, reorganizing content across LinkedIn / Twitter / Substack sheets, etc. All-or-nothing: if any rowId doesn't belong to this workspace, the entire batch fails before any write fires. Idempotent: rows already on the target sheet are skipped (returns `skipped` count). Rows land at the destination sheet's t","inputSchema":{"type":"object","required":["slug","rowIds","target_surface_slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"rowIds":{"type":"array","items":{"type":"string"},"maxItems":500,"minItems":1,"description":"Row IDs to move (1-500). Order is preserved at the destination: first id lands at the lowest position, last id at the highest."},"target_surface_slug":{"type":"string","description":"Slug of the destination table surface. Use list_surfaces to enumerate. 400 if the slug is a doc surface, archived, or not in this workspace."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":355,"tokensAfter":355},{"name":"remove_video","fixed":{"name":"remove_video","description":"Remove one video from a video surface. Get `video_id` from `list_videos`. Editor role required. Emits `video.updated`.","inputSchema":{"type":"object","required":["slug","video_id"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts bare or org-prefixed form."},"video_id":{"type":"string","description":"The video's id, from list_videos."},"surface_slug":{"type":"string","description":"Optional video surface slug. Omit for the first video surface."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":146,"tokensAfter":146},{"name":"remove_workspace_member","fixed":{"name":"remove_workspace_member","description":"Remove a workspace member. Editor role required; owner-tier removals require an owner caller. Sole-owner removal is blocked; promote someone else first. Note: if the workspace visibility is `org`, removing an explicit member of the same org leaves them with virtual editor access via the org-membership branch. Consent-gated for agents: the FIRST call returns { status: 'confirmation_required', confirm_token, message, expires_in }. Surface the message to your user and, if they say yes, re-call this","inputSchema":{"type":"object","required":["slug","member_id"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"member_id":{"type":"string","description":"The WorkspaceMember id to remove. Get this from list_workspace_members."},"confirm_token":{"type":"string","description":"The token returned by the first call as `confirm_token`. Omit on the first call; include on the second call to execute the removal. Single-use, 60s TTL. Agents only; user callers don't need this."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":339,"tokensAfter":339},{"name":"request_revoke_agent_key","fixed":{"name":"request_revoke_agent_key","description":"Ask the human owner to revoke ANOTHER agent's active API key (sibling agent). The MCP `revoke_api_key` tool is self-only by design; this is the cross-agent escalation path. Returns { status: 'approval_required', approval_url, polling_url, expires_in }: print approval_url in chat for the target agent's owner to click; poll polling_url for the result. Approval gate: the approving user must be the target agent's owner (Agent.ownerUserId match). Use this when you've spotted credential leakage, misbe","inputSchema":{"type":"object","required":["target_agent_id"],"properties":{"reason":{"type":"string","description":"1-2 sentences on why you're asking. Surfaces verbatim on the consent card so the owner knows what they're saying yes to. Capped at 500 chars."},"target_agent_id":{"type":"string","description":"The id of the sibling agent whose key should be revoked. Get it from list_workspace_members: each agent row carries its id."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":276,"tokensAfter":276},{"name":"resolve_comment","fixed":{"name":"resolve_comment","description":"Mark a comment thread resolved. Idempotent: calling on an already-resolved thread returns the existing `resolvedAt` unchanged. Fires `comment.resolved`. Pair with `unresolve_comment` for the reverse. Used by agents to close a feedback thread once they've iterated on the change the reviewer asked for.","inputSchema":{"type":"object","required":["comment_id"],"properties":{"comment_id":{"type":"string","description":"Comment id to resolve (use the thread root, resolving a reply targets the reply itself, not the thread)."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":159,"tokensAfter":159},{"name":"revoke_api_key","fixed":{"name":"revoke_api_key","description":"Revoke an API key (soft-delete via `revokedAt`). Subsequent requests with the key return 401. Agents may revoke ONLY their own key; calling this is effectively a self-destruct, the response itself completes but the very next request will fail. Users may revoke any key they own. To swap creds without going dark in the gap, use `rotate_api_key` instead.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"API key id to revoke. Omit when called by an agent; defaults to the agent's own current key."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":160,"tokensAfter":160},{"name":"revoke_file_share","fixed":{"name":"revoke_file_share","description":"Soft-revoke a share token minted via `share_file`. The public `/share/files/<token>` URL stops resolving immediately. Idempotent: revoking an already-revoked token returns `alreadyRevoked: true` without error. Editor role required.","inputSchema":{"type":"object","required":["slug","file_id","token_id"],"properties":{"slug":{"type":"string","description":"The workspace slug."},"file_id":{"type":"string","description":"The file cuid."},"token_id":{"type":"string","description":"The `token_id` returned by `share_file` (NOT the token in the `url`)."}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":167,"tokensAfter":167},{"name":"search","fixed":{"name":"search","description":"Search across everything the caller can already touch: workspace names, row cell values, and doc sections/paragraphs. Returns ranked hits (score 0-1) with a navigable URL per hit so the agent can open the exact row or doc section. Access-gated; never returns hits from workspaces the caller can't open. Use when the user references something by keyword (\"find my launch-plan workspace\", \"which row mentions Redis?\"). Faster than listing workspaces and iterating.","inputSchema":{"type":"object","required":["q"],"properties":{"q":{"type":"string","description":"Search query. Case-insensitive substring match."},"kind":{"enum":["all","workspace","row","doc-section"],"type":"string","description":"Narrow to one surface. 'all' (default) searches workspace names + row cells + doc sections. 'workspace' is fastest when the user is naming something, 'row' targets table data, 'doc-section' targets headings and paragraphs in doc-mode."},"limit":{"type":"number","description":"Max hits to return (default 20, max 100)."},"offset":{"type":"number","description":"Hits to skip for pagination (default 0)."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":317,"tokensAfter":317},{"name":"send_message","fixed":{"name":"send_message","description":"Send a direct message to another agent or human in the messaging substrate. Wires through cue.dock.svc, the same path the /live UI uses, so the recipient sees this message in their drawer (and, once they have a Dock-connected agent worker running, their agent harness's inbox). Address format is `<agent_slug>@<user_slug>`: `flint@socrates` targets the `flint` agent owned by user `socrates`; `self@<user_slug>` targets a human's synthetic self-agent (use this to message a human directly when you do","inputSchema":{"type":"object","required":["to","body"],"properties":{"to":{"type":"string","description":"Recipient address in the form `<agent_slug>@<user_slug>`. Examples: `flint@socrates` (agent), `self@govind` (human's self-agent — use to DM a person directly). You may also pass an agent's stable id (`agt_...`) exactly as returned in `address_book`'s `agentId`. PREFER the id: an address is derived from the agent's slug, which does not follow a rename, so an address built from an agent's current display name can fail to resolve. The id never changes."},"body":{"type":"string","description":"Message text. Plain string, 1-32000 chars. `@<slug>` mentions inside the body CC the named agent on the message."},"replyTo":{"type":"string","description":"Optional cue message id to thread under. When set, the recipient's drawer renders this as a reply with an inline parent-preview. Get the id from a prior `send_message` response or from the recipient's inbox listing."},"send_at":{"type":"string","description":"Optional ISO-8601 UTC timestamp to schedule the message for future delivery (e.g. `2026-06-04T15:00:00Z`). Omit to send now. A past timestamp is treated as send-now. Honored only where scheduled send is enabled; otherwise ignored. `scheduled_at` is accepted as an alias."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":510,"tokensAfter":510},{"name":"update_doc","fixed":{"name":"update_doc","description":"Replace a workspace's doc body. Takes EITHER TipTap JSON (`content`) OR Markdown (`markdown`): pass markdown when you're producing prose from scratch (CommonMark + GFM is the format every LLM emits natively), pass TipTap JSON when you need structural edits to an existing doc (round-trip from get_doc, mutate, write back). Beyond CommonMark + GFM, the markdown layer recognizes:\n\n- **![alt text](https://…)** → inline image. Use an HTTPS URL (HTTP fires browser mixed-content warnings; data: URIs are","inputSchema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"content":{"type":"object","description":"TipTap document JSON: `{ type: 'doc', content: [ ... ] }`. Use this when round-tripping from get_doc to preserve formatting. Mutually exclusive with `markdown` (content wins if both are passed).","additionalProperties":true},"markdown":{"type":"string","description":"Markdown body (CommonMark + GFM). Converted server-side to TipTap JSON via the same converter that powers PUT /api/workspaces/:slug/doc. Use this when authoring prose from scratch; no need to hand-build ProseMirror nodes."},"surface_slug":{"type":"string","description":"Optional doc surface slug for multi-doc workspaces. Omit to write the primary doc surface. Use list_surfaces to see available slugs."},"if_unmodified_since":{"type":"string","description":"Optional precondition. ISO 8601 timestamp (typically the `updatedAt` you read via `get_doc`). When set and the doc has changed since this cutoff, the write is rejected with `code: -32602`, message describing the conflict, and `data: { conflict: true, currentUpdatedAt, precondition }` so your agent can refetch + merge instead of silently clobbering a concurrent write. Without this, two agents PUTting near-simultaneously will both succeed and the last write wins (the previous content vanishes). Use this in multi-agent co-authoring flows; skip it for greenfield writes where you know you're the only writer."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":615,"tokensAfter":615},{"name":"update_doc_section","fixed":{"name":"update_doc_section","description":"Replace a single section of a workspace's doc body, identified by its heading text. The targeted edit complement to `update_doc` (full replacement) and `append_doc_section` (append-only at the end). Use this when the agent maintains a recurring section (e.g., a 'Status' block in a launch-prep doc, an 'Outcomes' block in a meeting note) and only needs to refresh that one piece. Without it, agents are forced into 'GET → splice → PUT' which costs tokens, costs latency, and races against any concurr","inputSchema":{"type":"object","required":["slug","heading","markdown"],"properties":{"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace')."},"heading":{"type":"string","description":"Plain text of the heading to find (case-sensitive, trimmed). For `## Outcomes`, pass `Outcomes`. Hash marks and surrounding whitespace are stripped from the comparison automatically by the markdown converter. Use `get_doc` first if you need to enumerate the headings actually present."},"markdown":{"type":"string","description":"FULL replacement markdown for the section, including the heading line if you want to keep / rename / restructure it. Empty string deletes the section."},"surface_slug":{"type":"string","description":"Optional doc surface slug for multi-doc workspaces. Omit to target the primary doc surface."},"allowBlockLoss":{"type":"boolean","description":"Confirm a replacement that removes content. Omit it normally: the call is refused (with block counts, any `removedHeadings` and any `removedBlocks`) when it would destroy the bulk of the doc or of the section, delete a heading inside the replaced range that your markdown does not carry back, or drop a callout, quote, code block, table, image, embed, diagram, math or details block in the range. Set true only when the deletion is what you intend."},"if_section_hash":{"type":"string","description":"Optional precondition scoped to THIS section: the `sectionHash` from your last update_doc_section response for this section (the hash AFTER that write; `prior.contentHash` is the hash before it and would always conflict). When the section has changed since, the write is rejected with `code: -32602` and `data: { conflict: true, currentSectionHash }`; edits to other sections don't trip it. Never retried; re-read, merge, and resend with the new hash."},"if_unmodified_since":{"type":"string","description":"Optional precondition. ISO 8601 timestamp (typically the `updatedAt` you read via `get_doc`, or the `updatedAt` of your last write). When set and the doc has changed since, the write is rejected with `code: -32602` and `data: { conflict: true, currentUpdatedAt, precondition }` so you can refetch and rebuild the section instead of overwriting someone else's edit with a stale copy. Compared to the millisecond when the value carries milliseconds. Concurrent writes to OTHER sections are always safe without it; pass it whenever your replacement was built from content you read earlier."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":863,"tokensAfter":863},{"name":"update_html","fixed":{"name":"update_html","description":"Write an HTML surface's body. Pass any of `html` / `css` / `js`; omitted fields stay unchanged. Pass empty string to clear. The surface renders in a sandboxed iframe on a separate origin (`render.trydock.ai`) with no access to Dock cookies, storage, or parent DOM — you have free rein inside that boundary. Use any web technology the browser supports: external CDN fonts and CSS (Google Fonts, Tailwind CDN, Fontsource), JS libraries (three.js, GSAP, Chart.js, anime.js), inline `<script>`, Web Worke","inputSchema":{"type":"object","required":["slug"],"properties":{"js":{"type":"string","description":"JS source. Stored alongside html/css; at v2 you'll typically inline `<script>` in the html field instead (same execution context, fewer round-trips). 200 KB cap (matches the top-level `update_html` description; the server enforces `HTML_SURFACE_LIMITS.jsBytes` = 200_000)."},"css":{"type":"string","description":"CSS source. Applied inside the sandbox iframe. At v2 you can also `<link rel=\"stylesheet\">` external stylesheets from any HTTPS CDN — useful for Tailwind CDN, Google Fonts, icon kits."},"html":{"type":"string","description":"HTML body. Sanitized server-side (smells stripped, but `<script>` and `<link>` allowed at v2 — load any CDN, write inline scripts, dynamic DOM). Use `validate_html` first for a pre-flight check."},"slug":{"type":"string","description":"The workspace slug."},"surface_slug":{"type":"string","description":"Optional html surface slug. Omit to write the primary html surface."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":433,"tokensAfter":433},{"name":"update_row","fixed":{"name":"update_row","description":"Update specific fields of an existing row. Only the fields provided in `data` are updated; others are preserved. Setting `surface_slug` to a different sheet than the row currently lives on MOVES the row to that sheet (position recomputes to the new sheet's tail unless `position` is also set). Same surface as current → no-op move.\n\n**Unmapped data fields:** Keys in `data` that don't match any existing column on the row's surface are still STORED on the row, but they won't render in the table UI u","inputSchema":{"type":"object","required":["slug","rowId","data"],"properties":{"data":{"type":"object","description":"Partial row data with fields to update (e.g. {\"status\": \"sealed\"}). Pass an empty object {} when the call is purely a move (surface_slug change with no field updates).","additionalProperties":true},"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"rowId":{"type":"string","description":"The row ID to update"},"position":{"type":"number","description":"Optional. Override the row's position. When moving across surfaces, omit to land at the new surface's tail; pass a number to land at a specific slot."},"surface_slug":{"type":"string","description":"Optional. When set to a different surface than the row currently lives on, moves the row to that surface and emits a `row.moved_surface` event. Same-surface is a no-op. 400 if the slug is a doc surface, archived, or not in this workspace."},"auto_create_columns":{"type":"boolean","description":"When true, the server auto-creates a text column for every key in `data` that doesn't already exist on the surface, then applies the update in the same call. Returns `created_columns` in the response. Default false: unmapped keys are still merged into row.data but won't render in the UI until you `add_column` them yourself."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":560,"tokensAfter":560},{"name":"update_surface","fixed":{"name":"update_surface","description":"Rename, reslug, reorder, restore, OR replace the column schema of a surface. Pass any subset of `name`, `new_surface_slug`, `position`, `columns`, `archived`. **Restore**: pass `archived: false` with the archived surface's `surface_slug` (find it with `list_surfaces archived: true`) to unarchive it; its rows and doc body come back, it is placed last among the workspace's surfaces, and it emits `surface.unarchived`. Refused if another live surface now owns that slug. To archive, use `delete_surfa","inputSchema":{"type":"object","required":["slug","surface_slug"],"properties":{"name":{"type":"string","description":"New display name. 1-64 chars."},"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"columns":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Optional. Full replacement ColumnDef[] for the surface's table schema. Table surfaces only — doc/html surfaces 400 with a table-only error. Each item: `{ key, label, type, position, width?, hidden?, description?, options? }`. type ∈ text|longtext|number|status|person|date|url|checkbox|select. Existing row.data keys not present in the new schema are preserved on disk but stop rendering in the UI (they'll surface as `unmapped_fields` on the next row write). Use get_workspace_schema → mutate → send the full array back; this is a REPLACE, not a merge."},"archived":{"type":"boolean","description":"Pass `false` to restore (unarchive) an archived surface. No-op on a live surface. Only `false` is accepted; archive with `delete_surface`."},"position":{"type":"number","description":"0-based position among the workspace's surfaces. Other surfaces shift to keep positions contiguous."},"surface_slug":{"type":"string","description":"The current slug of the surface to update."},"new_surface_slug":{"type":"string","description":"New slug for the surface (lowercase kebab-case, 3-64 chars). Must be unique within the workspace."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":595,"tokensAfter":595},{"name":"update_video","fixed":{"name":"update_video","description":"Change one video in a video surface: swap its file (`url`), rename it or relabel it. Get `video_id` from `list_videos`. Omitted fields stay as they are. Editor role required. Emits `video.updated`.","inputSchema":{"type":"object","required":["slug","video_id"],"properties":{"url":{"type":"string","description":"Optional new https URL (mp4, webm, mov or m4v)."},"name":{"type":"string","description":"Optional new name. Max 200 chars."},"slug":{"type":"string","description":"The workspace slug. Accepts bare or org-prefixed form."},"label":{"type":"string","description":"Optional new label. Max 60 chars."},"video_id":{"type":"string","description":"The video's id, from list_videos."},"content_type":{"type":"string","description":"Optional content type for a new `url` without an extension."},"surface_slug":{"type":"string","description":"Optional video surface slug. Omit for the first video surface."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":264,"tokensAfter":264},{"name":"update_webhook","fixed":{"name":"update_webhook","description":"Toggle a webhook's `active` flag on or off. Inactive webhooks are skipped at delivery time (no retry queue, no log row) but the endpoint config is preserved so flipping back is one call. Use to silence a noisy receiver during maintenance without losing its URL + secret + event subscription.","inputSchema":{"type":"object","required":["org_slug","webhook_id","active"],"properties":{"active":{"type":"boolean","description":"true to enable delivery, false to silence."},"org_slug":{"type":"string","description":"Org slug"},"webhook_id":{"type":"string","description":"Webhook id (from list_webhooks)"}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":180,"tokensAfter":180},{"name":"update_workspace","fixed":{"name":"update_workspace","description":"Rename a workspace, change its slug, switch its default-view mode, or change its visibility (private | org). Pass any subset of `name`, `new_slug`, `mode`, `visibility`; fields you omit are left unchanged. Slug renames preserve old URLs via WorkspaceSlugAlias so previously-shared links keep resolving. Visibility flips disconnect every live SSE subscriber so reconnects re-authenticate against the new visibility. Editor role required; only the workspace owner can change visibility. Emits `workspac","inputSchema":{"type":"object","required":["slug"],"properties":{"mode":{"enum":["table","doc","html"],"type":"string","description":"New default-view preference for the surface that opens first. Optional. Doesn't add or remove surfaces; use `create_surface` / `delete_surface` to change the actual surfaces."},"name":{"type":"string","description":"New display name. Optional."},"slug":{"type":"string","description":"The current workspace slug"},"new_slug":{"type":"string","description":"New URL slug (lowercase kebab-case, 3-64 chars). Optional. Must be unique within the org. Old slug stays redirectable via the alias table."},"visibility":{"enum":["private","org"],"type":"string","description":"New visibility. Optional. `private` = explicit members only; `org` = every org member gets virtual editor. Only these two can be set. Widening (private → org) is consent-gated; see `consent_mode`."},"consent_mode":{"enum":["web"],"type":"string","description":"Required when `visibility` widens audience. Pass 'web' to surface a click-to-approve URL the user opens in their browser; first call returns { status: 'approval_required', approval_url, polling_url }, you print approval_url in chat and poll polling_url for the result."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":495,"tokensAfter":495},{"name":"update_workspace_member","fixed":{"name":"update_workspace_member","description":"Change an existing workspace member's role. Editor role required to caller. Owner-tier transitions (promoting to or demoting from owner) require an owner caller. Demoting the sole owner is blocked; promote someone else to owner first. No-op when the role is unchanged. Emits `member.role_changed` with from/to roles.","inputSchema":{"type":"object","required":["slug","member_id","role"],"properties":{"role":{"enum":["owner","editor","commenter","viewer"],"type":"string","description":"New role."},"slug":{"type":"string","description":"The workspace slug. Accepts either the bare slug ('my-workspace') or the org-prefixed form ('my-org/my-workspace') as shown in the dashboard URL; both resolve to the same workspace."},"member_id":{"type":"string","description":"The WorkspaceMember id to update. Get this from list_workspace_members."}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":248,"tokensAfter":248},{"name":"upload_file","fixed":{"name":"upload_file","description":"Upload a file into a Files surface (kind='files'). For a file over ~3 MB, or any file on your own machine, call `create_file_upload` first: run the curl it returns, then pass the `url` from its response here. You never need a public host to put your own file into Dock. Small files can instead go inline as base64 in `content_base64` (a bare base64 string or a data: URL), capped ~3 MB, the request-body limit. You can also pass any public https `url` and the server fetches the file for you, with th","inputSchema":{"type":"object","required":["slug","surface_slug","filename"],"properties":{"url":{"type":"string","description":"The `url` a `create_file_upload` link returned, or any public https URL the server fetches the file from (large-file path; no request-body limit). Provide this OR `content_base64`, not both."},"mime":{"type":"string","description":"Optional MIME hint. The server still sniffs magic bytes; this only helps for extension-less or ambiguous content."},"slug":{"type":"string","description":"The workspace slug (bare or org-prefixed)."},"filename":{"type":"string","description":"The file's name, including extension (e.g. \"report.pdf\")."},"surface_slug":{"type":"string","description":"The Files surface slug to upload into (required)."},"content_base64":{"type":"string","description":"The file bytes, base64-encoded. Accepts a bare base64 string or a \"data:<mime>;base64,...\" data URL. ~3 MB cap. Provide this OR `url`, not both."},"parent_folder_id":{"type":"string","description":"Optional folder id (from list_files) to place the file in. Defaults to the surface root."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":466,"tokensAfter":466},{"name":"validate_doc_markdown","fixed":{"name":"validate_doc_markdown","description":"Pre-flight check on markdown BEFORE writing it via update_doc / append_doc_section. Returns { ok, errors, warnings, parsed } with parsed counts per format type (imageCount, videoCount, mermaidCount, mathCount, svgCount, calloutCount, crossRefCount, mentionCount, embedCount, detailsCount, headingCount, byteSize, nodeCount, depth) plus structured DocGuardError-equivalent errors (cap breaches) and non-blocking warnings (cross-refs that don't resolve, mention ids that don't resolve, oversize sources","inputSchema":{"type":"object","required":["markdown"],"properties":{"markdown":{"type":"string","description":"Markdown body to validate. Same surface as update_doc: CommonMark + GFM plus mermaid / math / callouts / svg / details / cross-refs / embeds."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":225,"tokensAfter":225},{"name":"validate_formula","fixed":{"name":"validate_formula","description":"Parse-check a formula expression server-side without writing anything. Returns { ok, error?, rewrittenFormula?, referencedFunctions, unknownFunctions }. Use BEFORE update_row / create_row when the formula references functions or syntax you're not 100% sure of: a `=SUMIFS(...)` with the wrong arg order or a misspelled `=AVERAG(...)` will round-trip into the cell as a stored carrier with no value, and the user will see #NAME? or #VALUE? on next view. Catch it here. `unknownFunctions` flags any ide","inputSchema":{"type":"object","required":["formula"],"properties":{"formula":{"type":"string","description":"Formula expression to validate, including the leading '='. Example: '=SUMIF(B2:B10, \">0\")'. Max 4000 chars."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":214,"tokensAfter":214},{"name":"validate_html","fixed":{"name":"validate_html","description":"Pre-flight check on html / css / js BEFORE writing via update_html. Returns { ok, errors, warnings, parsed } where parsed has byte counts per field and `htmlDropped` (true if the sanitizer would strip anything from `html`) plus `htmlRemoved`, the list of tags (`{ tag, count }`) and attributes (`{ tag, attribute, count }`) it would remove (capped at 25; `htmlRemovedTruncated` flags the cut). The same list appears in the `html_sanitized` warning. Errors cover cap breaches (`html_too_large`, `css_t","inputSchema":{"type":"object","properties":{"js":{"type":"string","description":"JS to validate (optional)."},"css":{"type":"string","description":"CSS to validate (optional)."},"html":{"type":"string","description":"HTML to validate (optional)."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":220,"tokensAfter":220}],"toolsChanged":64,"tokensSaved":0,"todo":[],"note":"Deterministic fixes. Changes marked verify need a human look before shipping (renames, inferred annotations, inferred types, removed text). Parameter descriptions in `todo` need to be written by someone who knows the API."}