Once connected, your agent can:
- See your work — list your projects and workspaces, and read the full structure of a sitemap (pages, blocks, colors, tags, symbols).
- Build and edit sitemaps — create and update pages, add content blocks with wireframes, restructure the page tree, apply several changes at once as a single atomic update.
- Read and leave review notes — read the comments on pages and blocks and the threads pinned to the canvas, add notes and replies, and resolve or reopen a thread. A note left by an agent is authored by your account and looks exactly like one you typed, and posting it notifies nobody.
- Manage projects — create, duplicate, archive, move projects; transfer ownership; update project settings like theme and layout. Projects cannot be deleted over MCP — archiving is as far as an agent goes, and archives are reversible.
Every action runs as you — the agent only ever sees and changes what your Octopus.do account has access to.
Connecting
Endpoint
https://mcp.octopus.do/mcp
The general flow is the same in every client:
- Add Octopus as a remote MCP server / connector using the endpoint above.
- Choose OAuth as the authorization type — most clients detect this automatically.
- Sign in (or register) with your Octopus.do account when the browser opens.
- Ask your agent to do something — e.g. “show my Octopus projects” or “add a pricing page to my sitemap.”
No API keys to copy or manage — authorization happens through your normal Octopus.do login. Client-specific instructions below.
Claude (web and desktop)
- Go to Settings → Connectors → Add custom connector.
- Enter
https://mcp.octopus.do/mcpas the URL and click Add. - Click Connect and sign in with your Octopus.do account.
Claude Code
Run in your terminal:
claude mcp add --transport http octopus https://mcp.octopus.do/mcp
Then run /mcp inside Claude Code to complete the sign-in.
Cursor
Add to ~/.cursor/mcp.json (or .cursor/mcp.json in your project):
{
"mcpServers": {
"octopus": {
"url": "https://mcp.octopus.do/mcp"
}
}
}
Cursor will prompt you to authenticate the first time the server is used.
VS Code (GitHub Copilot)
Run MCP: Add Server from the Command Palette and choose HTTP, or add to .vscode/mcp.json:
{
"servers": {
"octopus": {
"type": "http",
"url": "https://mcp.octopus.do/mcp"
}
}
}
ChatGPT
Octopus.do is published in the ChatGPT app directory: search for Octopus.do under Settings → Apps & Connectors and press Connect. Requires ChatGPT Plus or higher. Full walkthrough: Octopus.do for ChatGPT.
To add the MCP endpoint by hand instead — for a dev environment, or a workspace where the app isn't available — turn on developer mode (Settings → Apps & Connectors → Advanced settings) and:
- Go to Settings → Apps & Connectors → Create (or Add connector).
- Set the MCP server URL to
https://mcp.octopus.do/mcpand authentication to OAuth. - Complete the Octopus.do sign-in when prompted.
Windsurf
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"octopus": {
"serverUrl": "https://mcp.octopus.do/mcp"
}
}
}
Other MCP clients
Any client that supports remote MCP servers (Streamable HTTP) with OAuth works: point it at https://mcp.octopus.do/mcp, pick OAuth, and sign in with your Octopus.do account.
Authentication
Octopus MCP uses OAuth 2.1, the standard MCP clients use for secure, per-user authorization. Your agent requests access on your behalf, you approve it once during sign-in, and access can be revoked from your Octopus.do account at any time. The MCP server itself never sees or stores your password.
Available tools
Your agent picks these tools on its own — you don’t call them directly. The reference below is useful when you want to know exactly what the agent can (and can’t) do, or to phrase a request precisely.
Most write tools accept an optional idempotency_key (string): a unique key that makes retries safe — a repeated request with the same key is applied only once. It’s omitted from the tables below.
Destructive actions (like deleting a page, a block or a comment) are flagged to your AI client, which will typically confirm with you before running them.
Reading
get_me
Returns your account name. No parameters.
list_projects
Lists the projects you can access, across every workspace (personal and teams), most recently updated first. Each item includes the workspace it belongs to. One page at a time: total is how many matched and has_more whether any were left out. To find one project by name use q rather than paging — it searches every project on the account, not just the current page.
| Field | Required | Type | Description |
|---|---|---|---|
q | No | string | Only projects whose title contains this, case-insensitively |
workspace_uuid | No | string | Only this workspace. Use the literal "my" for your personal workspace |
limit | No | number | How many to return, 1–200 (default 50) |
offset | No | number | How many to skip; for paging |
list_workspaces
Lists your workspaces (personal + teams) with their folders and the 10 most recently updated projects in each — project_count gives the real total, and list_projects searches and pages through the rest. No parameters.
get_workspace
Returns one workspace: info, folders, and projects.
| Field | Required | Type | Description |
|---|---|---|---|
uuid | Yes | string | Workspace uuid. Use the literal "my" for your personal workspace. |
get_project
Returns the state of one project: the entities (tabs, sections, nodes = pages, blocks, colors, tags, symbols, arrows, …) and the parent/child tree linking them. A whole sitemap is a large read, so start with view: "outline" and narrow further with scope_id when you only need one branch.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid. uuid is accepted as a synonym, so a call written either way works |
view | No | enum | outline (default) — the page tree with titles, urls and ids, an order of magnitude cheaper; full — page content as well |
scope_id | No | string | Read one branch instead of the project: a tab, section or page id. The response carries the chain of ancestors, so the branch keeps its context |
exclude | No | array | Content groups to drop — blocks, content, notes, styling, tags, estimates, overlays, meta |
format | No | enum | nested (default) — the tree; plain — flat collections plus the relations graph |
list_comments
Reads the review notes on a project. There are two kinds and one call returns both: flat comments left on pages and on content blocks (thread replies are comments too), and canvas threads — the pins on the sitemap canvas, which carry a position and an open/resolved state. The result has a comments array and a threads array; whichever was not asked for comes back empty. Threads default to the open ones, so the default answer is what is still outstanding rather than everything ever written.
No access shows up differently depending on what was asked for. A default call reads both halves, and the threads half is refused outright, so the whole call fails and says so. A comments-only call (a kind of page, block or reply) gets an empty list instead — the underlying endpoint answers 200 with nothing rather than a 403, and no caller can tell that from a project that simply has no notes.
An anchor.type of null on a thread means the page or tab it was pinned to has been deleted and the pin outlived it — orphaned pins are normal. content comes back exactly as it was written: a Plate/Slate document serialized to a JSON string, or plain text. author is an opaque user uuid — never a name, an email or an avatar — and null when the note was left by an anonymous commenter.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
kind | No | enum | page | block | reply | thread. Omit for comments of every kind and threads together; thread returns threads only |
target_id | No | string | Only notes on this id — a page id, a block id, or a thread id, which returns that thread's replies |
resolved | No | boolean | Threads only. Omit for the open threads, true for the resolved ones |
include_replies | No | boolean | Embed each thread's replies in the thread (default false) |
Editing
apply_changes
Applies several editing operations to a project as one atomic change — the preferred way to build or restructure a sitemap. Operations run in order and each sees the effect of the previous ones. Give a created entity a ref and reference it in later operations via parent_ref / node_ref instead of an id.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
operations | Yes | array | At least one operation (see below) |
dry_run | No | boolean | Validate and preview only — nothing is changed |
idempotency_key | No | string | Repeating a call with the same key applies it once |
Each operation:
| Field | Required | Type | Description |
|---|---|---|---|
operation | Yes | string | Operation name, e.g. "nodes.create" |
ref | No | string | Local name for the created entity, referenced by later operations via *_ref fields |
data | No | object | Operation payload — same fields as the corresponding standalone tool |
A successful response may also carry warnings — advisory notes such as a page whose url does not sit under its parent's path. Nothing was blocked and no retry is needed; they are there to be read, not acted on automatically.
Operation names follow group.action:
| Group | Actions |
|---|---|
nodes | create, update, move, delete, clone, collapse, replace_url_prefix |
sections | create, update, delete, clone, collapse, move_up, move_down |
tabs | create, update, delete, clone |
blocks | create, update, move, delete, clone |
tags | create, update, delete, assign_node, unassign_node |
colors | create, update, delete |
symbols | create, update, delete |
sticky_notes | create, update, delete, clone |
arrows | create, update, delete |
settings | update |
Both halves of the name are snake_case — tags.assign_node, sections.move_up, nodes.replace_url_prefix. The REST endpoint for the same operation spells its path with hyphens (/tags/assign-node); the two forms are not interchangeable, and an operation named in path form is rejected.
The REST API has a few operations the MCP server deliberately does not offer — estimates (money), tab reordering, symbol linking and external links. They stay available over the Public API.
create_node
Creates a page (node) in the sitemap.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
parent_id | Yes | string | Parent id — a tab, section, or another node (ids come from get_project) |
title | No | string | Page title |
color_id | No | string | Project color id; defaults to the project default |
url | No | string | Page URL/slug shown on the node |
variant | No | enum | default | frame | ghost | stack |
after_id | No | string | Place it directly after this sibling — name a neighbour instead of counting positions |
before_id | No | string | Place it directly before this sibling |
index | No | number | Absolute position among the parent’s children; omit to append |
update_node
Updates page fields.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
id | Yes | string | Node id |
title | No | string | New title |
color_id | No | string | Project color id |
url | No | string | null | Page URL/slug; null clears it |
variant | No | enum | default | frame | ghost | stack |
notes | No | object | SEO/notes fields to merge: note, keywords, page_intent, seo_title, seo_description |
move_node
Moves a page with its whole subtree to another parent.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
id | Yes | string | Node id to move |
parent_id | Yes | string | New parent (tab, section, or node) — not the node itself or its subtree |
after_id | No | string | Place it directly after this sibling — name a neighbour instead of counting positions |
before_id | No | string | Place it directly before this sibling |
index | No | number | Position among the new parent’s children |
delete_node
Deletes a page with its whole subtree and all blocks inside.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
id | Yes | string | Node id |
create_block
Creates a content block inside a page.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
node_id | Yes | string | Page (node) id the block is created in |
title | No | string | Block title |
content | No | string | Block text content |
wireframes | No | string[] | Wireframe names to render, e.g. ["header"], ["text"], ["footer"] |
color_id | No | string | Project color id |
index | No | number | Position within the page; omit to append |
update_block
Updates block fields.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
id | Yes | string | Block id |
title | No | string | New title |
content | No | string | Block text content |
color_id | No | string | Project color id |
wireframes | No | string[] | Wireframe names to render |
collapsed | No | boolean | Collapse/expand the block |
completed | No | boolean | Mark the block done/not done |
move_block
Moves a block to another page.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
id | Yes | string | Block id to move |
node_id | Yes | string | Destination page (node) id |
index | No | number | Position within the destination page |
delete_block
Deletes a block from its page.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
id | Yes | string | Block id |
manage_comment
Adds, deletes, resolves and reopens comments and canvas threads. A note added this way is authored by the account the connection is signed in as, with no marker of any kind — in the editor it is indistinguishable from one that person typed — and it notifies nobody, because no mentions are sent.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
action | Yes | enum | add | delete | resolve | reopen |
target | Yes (add) | object | add: what the note hangs off — { "type": "page" | "block" | "tab" | "thread", "id": "…" } |
content | Yes (add) | string | add: the note body; must not be empty. Send plain text — it is wrapped into an editor document, one paragraph per line, and renders the way a person typing it would expect. Do not build that document yourself; one you already have is stored untouched |
position | No | object | add: the pin position, { "x": 0, "y": 0 } — supplying it asks for a pin rather than a plain note. See the table below |
id | Yes (delete, resolve, reopen) | string | The comment id or thread id to act on |
What add creates depends on the target type and on whether a position is given:
| Target type | position | What is created |
|---|---|---|
page | omitted | A note on the page |
page | given | A canvas pin on that page — a thread |
block | omitted | A note on the content block |
block | given | Refused — a block can never hold a canvas pin. Drop position, or pin to the page instead |
tab | either | A canvas pin on the empty canvas of that tab — a thread |
thread | omitted | A reply on that thread |
thread | given | Refused — a reply has no position of its own |
Every id is checked against the project before anything is written — on add, on delete, and on resolve and reopen alike — so an id the project does not contain fails rather than creating a note attached to nothing or closing a thread somewhere else. An id of the wrong kind, a block id given as a page for instance, is refused with the id's real kind named. Neither is worth a retry: the fix is to re-read the ids.
Only canvas threads have a resolved state, so resolve and reopen take a thread id — a page or block comment cannot be resolved at all. delete takes either a comment id or a thread id and works out which it is, so the caller does not have to know; deleting a thread removes the pin but leaves its replies behind, since they are comments in their own right.
The optional idempotency_key makes a retry safe — the same key sent twice adds one note, not two. It does not make add idempotent: two add calls with different keys add two notes. On delete the key is accepted and ignored: there is nothing to protect, because deleting the same id twice removes one note and then reports it gone.
Project management
create_project
Creates a new project in the target workspace. May fail when your plan’s project limit is reached.
| Field | Required | Type | Description |
|---|---|---|---|
workspace_id | Yes | string | Destination workspace — a team workspace uuid, or the literal "my" for your personal space. Required; an empty or omitted value is rejected with an error. |
folder_id | No | string | Folder id within the team workspace |
manage_project
Project lifecycle actions: duplicate, archive/unarchive, move, transfer.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
action | Yes | enum | duplicate | archive | unarchive | move | transfer |
workspace_id | Yes (move) | string | move: destination workspace — a team workspace uuid, or the literal "my" for your personal space. Required for move; an empty value is rejected with an error. |
folder_id | No | string | move: destination folder id |
email | No | string | transfer: recipient email — must be a registered, non-free-plan user |
update_project_settings
Updates project-level settings.
| Field | Required | Type | Description |
|---|---|---|---|
project_uuid | Yes | string | Project uuid |
title | No | string | Project title |
theme | No | enum | blueprint | bold | dark | light |
tree | No | enum | Tree layout: map | matrix |
frame | No | enum | Node frame style: mobile | neutral | web |
mobile | No | boolean | Mobile mode |
image_mode | No | boolean | Show images on nodes |
legend_position | No | enum | bottom | none | top |
default_color_id | No | string | Default color for new pages |
Learn more
- Octopus.do: https://octopus.do/
- Model Context Protocol: https://modelcontextprotocol.io/