Octopus.do

Leia e edite sitemaps visuais do octopus.do: páginas, blocos e notas de revisão, como o usuário conectado.

Documentação

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:

  1. Add Octopus as a remote MCP server / connector using the endpoint above.
  2. Choose OAuth as the authorization type — most clients detect this automatically.
  3. Sign in (or register) with your Octopus.do account when the browser opens.
  4. 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)

  1. Go to Settings → Connectors → Add custom connector.
  2. Enter https://mcp.octopus.do/mcp as the URL and click Add.
  3. 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 Octopus.do to Cursor — opens Cursor with the server already filled in.

Or add it by hand to ~/.cursor/mcp.json (use .cursor/mcp.json to scope it to one 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:

  1. Go to Settings → Apps & Connectors → Create (or Add connector).
  2. Set the MCP server URL to https://mcp.octopus.do/mcp and authentication to OAuth.
  3. Complete the Octopus.do sign-in when prompted.

Windsurf

Add to ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "octopus": {
      "serverUrl": "https://mcp.octopus.do/mcp"
    }
  }
}

Grok

  1. Open grok.com → Connectors, click New Connector, then Custom.
  2. Enter https://mcp.octopus.do/mcp as the MCP server URL.
  3. Complete the Octopus.do sign-in when prompted.

Custom connectors are a paid-plan feature in Grok. On Business and Enterprise plans a team admin has to provision the connector in the xAI cloud console before members can add it.

Gemini

  1. Open gemini.google.com → Settings & help → Connected Apps.
  2. Under Custom apps for Spark, add https://mcp.octopus.do/mcp as the MCP server URL.
  3. Complete the Octopus.do sign-in when prompted.

Custom apps require Gemini Spark on a personal Google Account — Workspace accounts cannot add them — and Google currently limits the feature to users in the United States, in English, from the Gemini web app. Once connected, it works in the mobile apps as well.

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.

FieldRequiredTypeDescription
qNostringOnly projects whose title contains this, case-insensitively
workspace_uuidNostringOnly this workspace. Use the literal "my" for your personal workspace
limitNonumberHow many to return, 1–200 (default 50)
offsetNonumberHow 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.

FieldRequiredTypeDescription
uuidYesstringWorkspace 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. A page’s notes come back in camelCase (seoTitle, pageIntent) although writes name them snake_case — see Notes field names.

On the Team plan a workspace can keep a shared symbol library, and projects reference symbols from it rather than owning them. Those references come back resolved — the symbol reads like any other, with an extra libraryId marking where its title, wireframes and default content live. Such a symbol cannot be edited through symbols.update (the call is refused): it is shared by every project of the workspace and is edited in the library itself, in the editor. Deleting it from one project is allowed and leaves the library untouched.

FieldRequiredTypeDescription
project_uuidYesstringProject uuid. uuid is accepted as a synonym, so a call written either way works
viewNoenumoutline (default) — the page tree with titles, urls and ids, an order of magnitude cheaper; full — page content as well
scope_idNostringRead 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
excludeNoarrayContent groups to drop — blocks, content, notes, styling, tags, estimates, overlays, meta
includeNoarrayContent groups to add back to the default outline view, from the same list as excludeinclude: ["estimates"] returns the page tree with the estimate rate card (the lineItems ids needed to write values) without paying for view: "full". Ignored when view is full; exclude wins on a group named in both
formatNoenumnested (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.

Both halves name what they point at: a comment’s target and a thread’s anchor carry a title beside the id, so a note can be read without pulling the whole project down to look the id up. For a page or a tab that is its title; for a block it is the block’s name. title is null in four ordinary cases, none of them an error: the pin is an orphan (below); the note is a reply, whose target is a thread and threads have no title of their own; the page is simply untitled; or the project tree could not be read, which costs the titles and nothing else — a comments-only call on a project you cannot read still answers with an empty list rather than an error, exactly as before.

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 is markdown, whatever format the editor stored the note in, so the assistant reads a review note as text rather than as an editor document. author is { id, name, avatar } — the writer's name and photo beside a stable id, and null when the note was left by an anonymous commenter; the avatar url is public. On an anonymous comment, guest_name is the name the visitor typed for themselves — unverified, so the assistant is told to say "someone calling themselves X" rather than "X" — or null when they gave none. A thread carries it on the same terms as a comment — the visitor is asked for a name when they drop the pin — and it is null on pins dropped before the editor started asking, which reads the same as a visitor who gave none.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
kindNãoenumpage | block | reply | thread. Omita para comentários de todos os tipos e threads juntos; thread retorna apenas threads
target_idNãostringApenas notas neste id — um id de página, um id de bloco ou um id de thread, que retorna as respostas dessa thread
resolvedNãobooleanApenas threads. Omita para threads abertas, true para as resolvidas
include_repliesNãobooleanIncorpora as respostas de cada thread na própria thread (padrão falso)

Edição

apply_changes

Aplica várias operações de edição a um projeto como uma única alteração atômica — a forma preferida de construir ou reestruturar um sitemap. As operações são executadas em ordem e cada uma vê o efeito das anteriores. Dê a uma entidade criada um ref e faça referência a ela em operações posteriores via parent_ref / node_ref em vez de um id.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
operationsSimarrayPelo menos uma operação (veja abaixo)
dry_runNãobooleanApenas validar e visualizar — nada é alterado
idempotency_keyNãostringRepetir uma chamada com a mesma chave aplica-a apenas uma vez

Cada operação:

CampoObrigatórioTipoDescrição
operationSimstringNome da operação, ex.: "nodes.create"
refNãostringNome local para a entidade criada, referenciado por operações posteriores via campos *_ref
dataNãoobjectPayload da operação — mesmos campos da ferramenta autônoma correspondente

Há dois limites em um lote, e apenas um deles é nosso. A API limita uma chamada a 100 operações. Separadamente, o cliente de IA que hospeda a conexão limita o tamanho dos argumentos de uma única chamada de ferramenta, e acima de aproximadamente 15–20 KB de JSON alguns clientes os truncam. Uma chamada truncada não chega como um lote menor — chega como JSON quebrado, e o erro diz que os argumentos não puderam ser analisados como JSON. Isso não é um erro de sintaxe no que você enviou, e tentar novamente sem alterações não ajudará: divida o trabalho em vários lotes menores por tamanho, não apenas por contagem de operações. Não há limite de bytes no lado do Octopus, então o teto exato depende do cliente.

Uma resposta bem-sucedida também pode conter warnings — notas consultivas, como uma página cuja url não está sob o caminho do pai. Nada foi bloqueado e nenhuma nova tentativa é necessária; elas estão lá para serem lidas, não para ação automática.

Os nomes das operações seguem group.action:

GrupoAções
nodescreate, update, move, delete, clone, collapse, replace_url_prefix
sectionscreate, update, delete, clone, collapse, move_up, move_down
tabscreate, update, delete, clone
blockscreate, update, replace_text, move, delete, clone
tagscreate, update, delete, assign_node, unassign_node
colorscreate, update, delete
symbolscreate, update, delete
sticky_notescreate, update, delete, clone
arrowscreate, update, delete
estimatesadd_line_item, update_line_item, delete_line_item, set_value, update_settings
settingsupdate

Ambas as metades do nome estão em snake_case — tags.assign_node, sections.move_up, nodes.replace_url_prefix. O endpoint REST para a mesma operação escreve seu caminho com hífens (/tags/assign-node); as duas formas não são intercambiáveis, e uma operação nomeada na forma de caminho é rejeitada.

Duas delas editam muitas entidades a partir de uma única operação, o que vale saber antes de escrever oitenta de algo. nodes.replace_url_prefix {from, to, scope_id?} reescreve o início de cada url de página correspondente; blocks.replace_text {from, to, scope_id?} substitui uma string literal dentro do conteúdo do bloco sem reenviar o conteúdo — a forma de corrigir uma palavra em todo um site. Ambas correspondem literalmente e com distinção entre maiúsculas e minúsculas, em vez de por padrão, ambas aceitam um scope_id (uma aba, seção ou página — e para blocks.replace_text, um único bloco) que padroniza para o projeto inteiro, e ambas falham se nada corresponder em vez de responder sucesso: uma reescrita que não atingiu nada é algo para saber, não para seguir em frente. Elas respondem com count e uma lista do que alteraram, limitada a 50 entradas e informando quando trunca. blocks.replace_text ignora instâncias de símbolo, cujo conteúdo pertence ao símbolo, e informa quantas ignorou.

Estimativas são editáveis via MCP: estimates.add_line_item e estimates.update_line_item constroem a tabela de preços, estimates.set_value coloca uma quantidade de unidades em uma página para uma de suas linhas (a primeira chamada para um par página/linha cria o valor, as posteriores o atualizam), estimates.delete_line_item remove uma linha com os valores por página, e estimates.update_settings define moeda, unidades, imposto e visibilidade. Todas as cinco precisam do proprietário do projeto no plano Pro ou superior, e hidden — uma estimativa privada — precisa do Team.

A API REST ainda tem algumas operações que o servidor MCP deliberadamente não oferece — reordenação de abas, vinculação de símbolos e links externos. Elas permanecem disponíveis via a API Pública.

create_node

Cria uma página (nó) no sitemap.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
parent_idSimstringId do pai — uma aba, seção ou outro nó (ids vêm de get_project)
titleNãostringTítulo da página
color_idNãostringId da cor do projeto; padroniza para o padrão do projeto
urlNãostringURL/slug da página mostrada no nó
variantNãoenumdefault | frame | ghost | stack
after_idNãostringColoque-o diretamente após este irmão — nomeie um vizinho em vez de contar posições
before_idNãostringColoque-o diretamente antes deste irmão
indexNãonumberPosição absoluta entre os filhos do pai; omita para anexar

update_node

Atualiza campos da página.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
idSimstringId do nó
titleNãostringNovo título
color_idNãostringId da cor do projeto; default redefine para o padrão do projeto
urlNãostring | nullURL/slug da página; null limpa — uma string vazia é rejeitada
variantNãoenumdefault | frame | ghost | stack
notesNãoobjectCampos de SEO/notas para mesclar: note, keywords, page_intent, seo_title, seo_description, seo_h1, seo_slug, seo_url. Um campo omitido mantém seu valor atual. Escrito em snake_case, lido de volta em camelCase — veja a tabela abaixo

Os campos de notas são escritos em snake_case e lidos de volta em camelCase. Os nomes à esquerda são o que update_node e a operação em lote nodes.update aceitam; os nomes à direita são o que get_project retorna dentro do notes de uma página:

EscritoLido de volta
notenote
keywordskeywords
page_intentpageIntent
seo_titleseoTitle
seo_descriptionseoDescription
seo_h1seoH1
seo_slugseoSlug
seo_urlseoUrl

Cada par é um campo sob duas grafias, não dois campos. Uma escrita bem-sucedida seguida de uma leitura que não mostra seo_title é este mapeamento e não dados perdidos — procure por seoTitle. Ambas as grafias permanecem como estão: normalizar qualquer lado quebraria os clientes que já leem o outro.

move_node

Move uma página com toda a sua subárvore para outro pai.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
idSimstringId do nó a mover
parent_idSimstringNovo pai (aba, seção ou nó) — não o próprio nó ou sua subárvore
after_idNãostringColoque-o diretamente após este irmão — nomeie um vizinho em vez de contar posições
before_idNãostringColoque-o diretamente antes deste irmão
indexNãonumberPosição entre os filhos do novo pai

delete_node

Exclui uma página com toda a sua subárvore e todos os blocos dentro.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
idSimstringId do nó

create_block

Cria um bloco de conteúdo dentro de uma página.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
node_idSimstringId da página (nó) em que o bloco é criado
titleNãostringTítulo do bloco
contentNãostringConteúdo de texto do bloco
wireframesNãostring[]Nomes de wireframe para renderizar, ex.: ["header"], ["text"], ["footer"]
color_idNãostringId da cor do projeto
indexNãonumberPosição dentro da página; omita para anexar

update_block

Atualiza campos do bloco.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
idSimstringId do bloco
titleNãostringNovo título
contentNãostringConteúdo de texto do bloco
color_idNãostringId da cor do projeto; default redefine para o padrão do projeto
wireframesNãostring[]Nomes de wireframe para renderizar
collapsedNãobooleanRecolher/expandir o bloco
completedNãobooleanMarcar o bloco como concluído/não concluído

move_block

Move um bloco para outra página.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
idSimstringId do bloco a mover
node_idSimstringId da página (nó) de destino
indexNãonumberPosição dentro da página de destino

delete_block

Exclui um bloco de sua página.

CampoObrigatórioTipoDescrição
project_uuidSimstringUUID do projeto
idSimstringId do bloco

manage_comment

Adiciona, exclui, resolve e reabre comentários e threads de canvas. Uma nota adicionada desta forma é de autoria da conta com a qual a conexão está conectada, sem nenhum marcador de qualquer tipo — no editor, é indistinguível de uma que essa pessoa digitou — e não notifica ninguém, porque nenhuma menção é enviada.

CampoObrigatórioTipoDescrição
project_uuidSimstringuuid do projeto
actionSimenumadd | delete | resolve | reopen
targetSim (add)objectadd: onde a nota se fixa — { "type": "page" | "block" | "tab" | "thread", "id": "…" }
contentSim (add)stringadd: o corpo da nota; não pode estar vazio. Envie texto simples — ele é envolvido em um documento de editor, um parágrafo por linha, e é renderizado da forma que uma pessoa digitando esperaria. Não construa esse documento você mesmo; um que você já tenha é armazenado intacto
positionNãoobjectadd: a posição do pino, { "x": 0, "y": 0 } — fornecê-lo solicita um pino em vez de uma nota simples. Veja a tabela abaixo
idSim (delete, resolve, reopen)stringO id do comentário ou id do tópico no qual agir

O que add cria depende do tipo de destino e se um position é fornecido:

Tipo de destinopositionO que é criado
pageomitidoUma nota na página
pagefornecidoUm pino de canvas nessa página — um tópico
blockomitidoUma nota no bloco de conteúdo
blockfornecidoRecusado — um bloco nunca pode conter um pino de canvas. Remova position, ou fixe na página em vez disso
tabqualquerUm pino de canvas no canvas vazio dessa aba — um tópico
threadomitidoUma resposta nesse tópico
threadfornecidoRecusado — uma resposta não tem posição própria

Cada id é verificado contra o projeto antes que qualquer coisa seja escrita — em add, em delete, e em resolve e reopen igualmente — então um id que o projeto não contém falha em vez de criar uma nota anexada a nada ou fechar um tópico em outro lugar. Um id do tipo errado, um id de bloco fornecido como página, por exemplo, é recusado com o tipo real do id nomeado. Nenhum dos dois vale uma nova tentativa: a correção é reler os ids.

Apenas tópicos de canvas têm um estado resolvido, então resolve e reopen aceitam um id de tópico — um comentário de página ou bloco não pode ser resolvido de forma alguma. delete aceita um id de comentário ou um id de tópico e descobre qual é, então o chamador não precisa saber; excluir um tópico remove o pino, mas deixa suas respostas para trás, já que são comentários por direito próprio.

O opcional idempotency_key torna uma nova tentativa segura — a mesma chave enviada duas vezes adiciona uma nota, não duas. Ele não torna add idempotente: duas chamadas add com chaves diferentes adicionam duas notas. Em delete a chave é aceita e ignorada: não há nada a proteger, porque excluir o mesmo id duas vezes remove uma nota e depois relata que ela se foi.

Gerenciamento de projetos

create_project

Cria um novo projeto no workspace de destino. Pode falhar quando o limite de projetos do seu plano é atingido.

CampoObrigatórioTipoDescrição
workspace_idSimstringWorkspace de destino — um uuid de workspace de equipe, ou o literal "my" para seu espaço pessoal. Obrigatório; um valor vazio ou omitido é rejeitado com um erro.
folder_idNãostringId da pasta dentro do workspace de equipe

manage_project

Ações do ciclo de vida do projeto: duplicar, arquivar/desarquivar, mover, transferir.

CampoObrigatórioTipoDescrição
project_uuidSimstringuuid do projeto
actionSimenumduplicate | archive | unarchive | move | transfer
workspace_idSim (move)stringmove: workspace de destino — um uuid de workspace de equipe, ou o literal "my" para seu espaço pessoal. Obrigatório para move; um valor vazio é rejeitado com um erro.
folder_idNãostringmove: id da pasta de destino
emailNãostringtransfer: email do destinatário — deve ser um usuário registrado, sem plano gratuito

update_project_settings

Atualiza as configurações de nível do projeto.

CampoObrigatórioTipoDescrição
project_uuidSimstringuuid do projeto
titleNãostringTítulo do projeto
themeNãoenumblueprint | bold | dark | light
treeNãoenumLayout da árvore: map | matrix
frameNãoenumEstilo do quadro do nó: mobile | neutral | web
mobileNãobooleanModo móvel
image_modeNãobooleanMostrar imagens nos nós
legend_positionNãoenumbottom | none | top
default_color_idNãostringCor padrão para novas páginas

Saiba mais