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_live_<48 hex>" on the Authorization header. ## 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, magic-link sign-in fires; 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 37 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) — replace the doc body. Last-write-wins; round-trip from get_doc, mutate, then update. Editor role. Awareness: - get_recent_events(slug, limit?) — what happened, in order. Use when picking up a workspace after time away. - search(q, kind?, limit?, offset?) — find workspaces / rows / doc sections by keyword. kind: all (default) / workspace / row / doc-section. Access-gated. Prefer this over list_workspaces + iterate when the user names something ("find my launch plan"). Self-service: - get_billing() — plan, caps, current usage, Stripe state. - upgrade_plan(plan) — "pro" or "scale". Returns a Checkout URL if no card on file; otherwise swaps the subscription price. - downgrade_plan() — schedule downgrade to Free at period end. - request_limit_increase(kind, desiredValue?, reason?) — ask for a cap past Scale without filing a support ticket. kind: agents / workspaces / rows / other. - create_support_ticket(kind, title, body, context?, attachmentUrls?) — kind: bug / feature / billing / question / other. Credentials (agent-self): - list_api_keys() — agent sees only its own key; user sees every key for every agent they own. Plaintext is never returned. - rotate_api_key(id?) — atomically mint a replacement and revoke the old one. Agents may rotate only their own (omit id to default). Returns the new plaintext exactly once. - revoke_api_key(id?) — soft-delete a key. Agents may revoke only their own (effectively a self-destruct). Webhooks (org-scoped, any org member): - list_webhooks(org_slug) — id, url, events, active flag, 8-char secret preview. - create_webhook(org_slug, url, events) — register a new endpoint. URL must be public. Returns signing secret exactly once. - update_webhook(org_slug, webhook_id, active) — toggle active flag. - rotate_webhook_secret(org_slug, webhook_id) — mint a fresh signing secret. Returned exactly once. - delete_webhook(org_slug, webhook_id) — permanent removal. ## Decision rules - When the user says "save this" or "write it down", check if there's a workspace already in use; if yes, create_row there. If no, ask which workspace or list_workspaces first. - When the user says "send it", "finalize", or "wrap this up", update the row's status to "sealed". - When you hit a cap: the error payload includes details.upgrade (for in-plan upgrades) and details.increase (for past-Scale asks). Call the named tool. Don't ask the user to "email support" — Dock doesn't do that. - When you hit a bug: file it via create_support_ticket with the x-request-id in the context field. That's the fastest path to a fix. - Before deleting a row, confirm with the user. Deletes are permanent. ## Workspace naming Workspace URLs are //. Slugs are lowercase kebab-case, unique within an org. "content-pipeline" in org "vector" resolves to /vector/content-pipeline. When the user refers to a workspace by a fuzzy name, list_workspaces and disambiguate. ## Columns + types Table-mode workspaces have a column schema. Each column has key, label, type, and optionally options (for status/select). Read the schema with get_workspace before writing if you're unsure. Common column names: title, status, owner, notes, due, url. ## Events + webhooks Every mutation emits an event. Users can subscribe via HMAC-signed webhooks (one per org, fans out to every workspace). If the user asks "ping Slack when X happens" or "sync to my DB when rows change", point them at /docs/webhooks. ## What Dock is not Not a Notion/Airtable replacement. No attachments, formulas, relational joins, or branching. It's a shared state primitive; keep your prose short and your rows atomic.