You have access to Dock, a shared cloud workspace for humans and AI agents. Workspaces live at https://trydock.ai//. A workspace is a container of one or more surfaces (tabs). Each surface is either a typed-row table (columns: text, number, status, person, date, url, checkbox, select, longtext) or a TipTap rich-text doc body. A workspace can hold any combination, one or many of either kind, in any mix. Workspace.mode picks which tab opens by default; it doesn't restrict what surfaces exist. Hit /rows for structured rows, /doc for narrative; use list_surfaces to see what a workspace actually contains, and pass surface_slug on row/doc calls to address a specific tab. Changes sync in real time across every connected client. ## Authentication Dock's MCP server is at https://trydock.ai/api/mcp. Transport: streamable-http. Auth: OAuth 2.1 with Dynamic Client Registration (the MCP client self-registers and opens a browser for consent) OR a Bearer token of the form "dk_<48 hex>" on the Authorization header. ## Confirming your key works (shell + key path) If you already have an API key (dk_<48 hex>) and shell access, confirm the key before doing anything else. Two equivalent verification paths: CLI path (Node 18+ required): export DOCK_API_KEY=dk_... npx -y @trydock/cli@latest whoami # → "Name · agent" Pure-REST path (no Node required, works on any box with curl): curl -H "Authorization: Bearer $DOCK_API_KEY" https://trydock.ai/api/me # → { "type": "agent", "name": "...", "org": { ... } } Both round-trip the same Bearer token through the same /api/me handler; a 200 on either is sufficient to start working. The CLI honors DOCK_API_KEY from the environment as of v0.9.1 (2026-05-16); on older versions it silently 401s and falls back to "Not signed in", pin `@latest` (as above) or upgrade if you see that. If you don't have a key yet but have shell access, mint one via the dashboard at https://trydock.ai/settings?tab=api. If you have neither shell nor a key (chat-only agent), use the no-terminal bootstrap flow in the next section. ## Bootstrap (no terminal needed) If you're a chat-only agent without an API key, no MCP, no CLI access, no shell, you can self-bootstrap one with a single customer click: 1. POST https://trydock.ai/api/agent-bootstrap/start with { "agent_name": "", "workspace_slug": "" }. No auth on this endpoint, that's the point. Returns { token, approval_url, polling_url, expires_at }. 2. Print the approval_url to your user. They click it; if they're not already signed in to Dock, sign-in fires first; either way they land on a consent card showing what's being authorised. 3. Customer clicks Approve. Server mints an Agent (signed to that user) and an ApiKey scoped to your name + the optional workspace. 4. Poll polling_url every ~3 seconds. While the customer hasn't decided, you get { status: "pending" }. When they Approve, the next poll returns { status: "approved", api_key, agent_id, workspace_slug } exactly once, single-read consumption of the plaintext key. 5. Store the key (env var, in-process secret, etc.) and continue. Tokens expire 5 minutes after creation. Per-IP rate limit: 5 starts per minute per /24 prefix. If your customer denies, you get { status: "denied" } and you should surface that and stop. Same shape of trust as gh auth login or AWS device codes. ## The 64 tools Workspaces: - list_workspaces(), see every workspace you can access. - get_workspace(slug), full detail for one workspace. - get_workspace_schema(slug), column definitions (keys, types, allowed options) so you know what create_row / update_row expect. - list_workspace_members(slug), who has access, with role. Verify a workspace is shared before writing output others need to see. - create_workspace(name, mode), new workspace; mode is "table" or "doc". - delete_workspace(slug), soft-archive. Rows + doc preserved, restorable from Settings · Archived. Idempotent. Editor role. Rows (table mode): - list_rows(slug, limit?, offset?), read rows. - get_row(slug, rowId), fetch one row by id when a cue payload hands you one and a full list would be wasteful. - create_row(slug, data), append a row. data is a JSON object keyed by column name. Status values: drafted / queued / active / blocked / sealed. - update_row(slug, rowId, data), partial merge; only provided fields change. - delete_row(slug, rowId), permanent, no soft delete. Surfaces (tabs inside a workspace, table or doc): - list_surfaces(slug, archived?), every tab in the workspace. - create_surface(slug, kind, name, surface_slug?, columns?), add a table or doc tab. Editor role. - update_surface(slug, surface_slug, name?, new_surface_slug?, position?), rename / reslug / reorder. Editor role. - delete_surface(slug, surface_slug), soft-archive. Idempotent. Cannot archive the only live surface. Editor role. Docs (TipTap body, pass surface_slug for multi-doc workspaces): - get_doc(slug), returns structured JSON (round-trippable into update_doc) plus a plain-text extraction for summarisation. - update_doc(slug, content?, markdown?), replace the doc body. Pass markdown when you're authoring fresh; pass content (TipTap JSON) when round-tripping from get_doc. Last-write-wins. Editor role. Markdown surface beyond CommonMark + GFM: - ![alt](https://…) → inline image (HTTPS only, no data: URIs) - lone URL ending in .mp4/.m4v/.webm/.mov/.mkv → native HTML5