Skip to content

MCP server

Corlecti Map is a Model Context Protocol server. Connect an assistant to it and that assistant can read your maps, build new ones, and answer questions about the shape of what you have written — using your maps as memory rather than being told about them.

It is your account and your maps only. A connection never sees more than you do, and it can see deliberately less: a key can be narrowed to one map when you create it.

Connecting

There are two ways in, and which one you use depends on your client rather than on any setting here.

A client with a place to paste a key

Claude Code, Cursor, the Claude Desktop config file, a script of your own.

  1. In Corlecti Map, open Settings → MCP Server and mint a key. It begins

mmap_live_ and is shown once.

  1. Point the client at https://map.corlecti.com/api/mcp and send the key as a

bearer token.

For Claude Code, that is one command:

claude mcp add --transport http corlecti-map https://map.corlecti.com/api/mcp \
  --header "Authorization: Bearer mmap_live_YOUR_KEY"

For a config file:

{
  "mcpServers": {
    "corlecti-map": {
      "type": "http",
      "url": "https://map.corlecti.com/api/mcp",
      "headers": { "Authorization": "Bearer mmap_live_YOUR_KEY" }
    }
  }
}

Narrowing a key when you create it

A key does not have to reach everything you can reach. On the mint form in Settings → MCP Server there is one more choice under the name:

  • Reaches — all your maps, or exactly one. A key bound to one map answers

"not found" for every other, and the tools that act on your whole account (create_skill, update_skill, delete_skill, list_notifications, mark_notifications_read) are not offered to it at all.

Coming: narrowing a key to one folder — every map nested inside it, and a new map created through that key would land there automatically — and seating a key on one branch of a map, so it would read and change only what hangs under a block you choose. Both are built and being tested; neither is on the mint form yet.

A key's reach is fixed when it is created. There is no way to move a key to another map afterwards — you create a new key with the reach you want and revoke the old one. The reach of every key you hold is written next to it in the list.

A client with no key field

claude.ai in a browser, and the Claude Desktop connector list. These have nowhere to paste a key, so they sign in instead: add https://map.corlecti.com/api/mcp as a connector, and you will be sent to Corlecti Map, asked to approve the connection while signed in, and sent back. Nothing is pasted anywhere.

You can see every connection you have approved, and withdraw any of them, in Settings → MCP Server. Withdrawing one takes effect immediately.

The transport is Streamable HTTP. Server-sent events are not offered.

Tools

Every tool below is scoped to your account — a map that is not yours does not exist as far as the connection is concerned — and narrowed further to a bound key's own map when it has one. Names, parameters and behaviour are what tools/list on the live endpoint returns; this page restates them for a person reading before they connect anything. Every parameter not marked required may be omitted.

Reading

list_boards

Your maps, nested — each is a map under the map it belongs to, with a name, a one-line description when its owner wrote one, and whether it is pinned. This is the first call when you do not know which map holds something.

  • search (string) — filter by name and description.
{ "name": "list_boards", "arguments": { "search": "roadmap" } }

get_board

Export a map you can reach as a full mmap.v1 document, plus the style and arrangement settings the server will apply to anything you send it. Call this before building anything, not only when editing what is already there.

  • boardId (uuid, required) — the map to read.
  • detail ("outline" | "style" | "full", default "full") — "full" is

every block's title, description and body; "outline" replaces each body with its character count, for a first look at a map you did not build; "style" is what every block wears (colour, variant) and nothing else.

  • branchOf (uuid) — read one block and everything hanging under it, instead

of the whole map. Cannot be combined with blockIds.

  • depth (integer, 0–20) — how many levels to include, from branchOf or

from the map's own roots when it is omitted.

  • blockIds (array of uuid, 1–50) — read just the blocks you name, wherever

they sit. Cannot be combined with branchOf.

  • format ("json" | "markdown" | "tree", default "json") — "json" is

the mmap.v1 document; "markdown" is the same map as nested prose, about half the tokens; "tree" is the same nesting as JSON, for acting on what you read rather than presenting it.

{
  "name": "get_board",
  "arguments": { "boardId": "b1a2c3d4-...", "format": "tree", "detail": "outline" }
}

search_blocks

Find blocks by meaning across the maps this connection reaches — the phrase does not have to match the wording in the block. Use it to find an existing note before creating a duplicate, or to answer a question from what is already written. Read-only, and free.

  • query (string, 2–500 characters, required).
  • boardId (uuid) — narrow to one map instead of every map you own. Not

offered at all to a key already bound to one map.

{ "name": "search_blocks", "arguments": { "query": "why we dropped the monthly window" } }

get_graph

Describe the shape of a map: how many blocks and links it has, how many separate islands it breaks into, which blocks are attached to nothing, how dense it is, and which blocks have the most links. Carries no block bodies. Read-only.

  • boardId (uuid, required).
  • detail ("summary" | "full") — "full" adds every link with its own id

and both ends, which is what you need before changing or cutting one.

{ "name": "get_graph", "arguments": { "boardId": "b1a2c3d4-...", "detail": "full" } }

find_missing_links

Pairs of blocks that are probably related and are not linked, in two sections that are not comparable: structural (blocks that share several neighbours) and semantic (blocks close in meaning, which structure alone cannot find). Read-only.

  • boardId (uuid, required).
  • limit (integer, 1–20, default 5) — pairs per section. The semantic section

returns at most 3 however large a limit you pass.

  • blockId (uuid) — narrow both sections to pairs involving one block.
{ "name": "find_missing_links", "arguments": { "boardId": "b1a2c3d4-...", "blockId": "9f0e..." } }

get_edge_signal

Read every link on a map through one lens at a time, each with both ends by id and by title. Read-only.

  • boardId (uuid, required).
  • signal ("weight" | "coEdit" | "semantic", required) — the declared

strength on the link itself, how often the two blocks were edited in the same sitting, or how close they are in meaning.

{ "name": "get_edge_signal", "arguments": { "boardId": "b1a2c3d4-...", "signal": "semantic" } }

list_docs

List the mmap.v1 rulebook sections available to you, readable with get_doc. The list is resolved per user.

  • boardId (uuid) — also lists the protocol of a scenario running there, if

one is.

{ "name": "list_docs", "arguments": {} }

get_doc

Read one or more rulebook sections by their exact id.

  • section (string, or array of strings, required) — call list_docs

first; do not guess an id.

  • boardId (uuid) — read the protocol of a scenario running there.
{ "name": "get_doc", "arguments": { "section": "blocks" } }

list_agents

The assistants this account has set up and where each one is pointed — a Corlecti Map key or a Telegram bot, and which map it reaches. Never returns a secret. Read-only, and free.

No parameters.

{ "name": "list_agents", "arguments": {} }

list_skills

The skills and scenarios you wrote — the ones you can edit or delete. A different question from list_docs, which is everything you may read.

  • kind ("skill" | "scenario") — filter.
  • detail ("list" | "full", default "list") — "full" includes each

body; the default gives its length instead.

{ "name": "list_skills", "arguments": { "kind": "skill" } }

list_notifications

Your notification inbox, newest first. Read-only, and marks nothing — call mark_notifications_read afterwards with the ids you actually showed.

  • unreadOnly (boolean) — only what has not been marked read.
  • limit (integer, 1–100, default 20).
  • boardId (uuid) — narrow to one map.
{ "name": "list_notifications", "arguments": { "unreadOnly": true, "limit": 10 } }

list_scenarios

The scenarios you may start: your own, plus the shared catalog. Does not include a scenario's protocol — that is read with get_doc once a run of it is active.

No parameters.

{ "name": "list_scenarios", "arguments": {} }

get_scenario

Read the scenario run on a map: which scenario it is, every answer collected so far, which checklist keys are outstanding, and the stage to act on next. Call this first whenever a person is in a scenario.

  • boardId (uuid, required).
{ "name": "get_scenario", "arguments": { "boardId": "b1a2c3d4-..." } }

review_map

Look at a map as it actually sits on the board and check the work — call this before telling somebody a map you built is finished. Reports overlapping blocks, stranded blocks, blocks connected to nothing, and separate pieces that never meet. Read-only, and free.

  • boardId (uuid, required).
  • format ("svg" | "png" | "html" | "none", default "svg") — "none" is

the findings alone, with no drawing; reach for it when checking your own work rather than showing somebody the map.

{ "name": "review_map", "arguments": { "boardId": "b1a2c3d4-...", "format": "none" } }

Writing

create_board

Create a new empty map, optionally nested under one you own. A new map arranges itself, so build it with structure alone — never send a position to a map you just made.

  • name (string, required).
  • metaDescription (string, nullable) — the one-line description shown on the

shelf.

  • parentBoardId (uuid, nullable) — nest under a map you own. Refused past a

bounded nesting depth.

{ "name": "create_board", "arguments": { "name": "Q3 planning" } }

update_board

Rename a map, or change its one-line description. Nothing inside the map — no block, no link, no position. Send only the field you are changing. You must own the map.

  • boardId (uuid, required).
  • name (string).
  • metaDescription (string, nullable) — null clears it.
{ "name": "update_board", "arguments": { "boardId": "b1a2c3d4-...", "name": "Q3 planning (final)" } }

apply_map

Insert blocks and links into a map you can write to, atomically — one mmap.v1 document, and nothing is written if any part of it is invalid. The full field reference and a worked "leads by id" example are on /docs/mmap; the shape here is the same one that page describes.

  • boardId (uuid, required).
  • document (mmap.v1 document, required) — version, kind

("fragment" to add to an existing map), nodes and edges. Send a title and let the map decide position, size and colour; add parent on a node instead of drawing an edge for ordinary tree structure.

  • offset ({dx, dy}) — where your own layout should land, ignored when

the map re-arranges what you sent (which is the default).

One call takes up to 500 nodes and 1000 edges; a document over either is refused whole. Send an Idempotency-Key header to retry safely after a lost connection — the same key within 5 minutes returns the original result rather than writing the blocks again.

{
  "name": "apply_map",
  "arguments": {
    "boardId": "b1a2c3d4-...",
    "document": {
      "version": "mmap.v1",
      "kind": "fragment",
      "nodes": [
        { "ref": "n1", "title": "Ship the beta" },
        { "ref": "n2", "title": "Write the changelog", "parent": "n1" }
      ],
      "edges": [
        { "ref": "e1", "source": "n1", "target": "n2", "kind": "goal", "label": "leads to" }
      ]
    }
  }
}

update_block

Update one or more fields on a single existing block you can write to.

  • id (uuid, required) — from get_board.
  • patch (object) — any of title, metaDescription, content,

positionX/positionY, width/height/maxHeight, color/bgColor (and their light/dark pairs), blockVariant ("default" | "parent" | "portal" | "image"), styleInherited, isActive, isTextExpanded, linkedBoardId, appendContent (add text to the end of the body without reading or resending what is already there), parentId (re-hang the block under another one, cutting its old parent link).

  • return ("minimal" | "id" | "full", default "minimal") — how much of

the patched block comes back.

{ "name": "update_block", "arguments": { "id": "9f0e...", "patch": { "appendContent": "\n\nDone 2026-09-10." } } }

update_blocks

Patch many blocks on one map in a single call — the batch form of update_block, for applying one convention (a colour, a status) across several blocks at once. appendContent and parentId are refused here.

  • boardId (uuid, required).
  • items (array of { id, patch }, 1–100, required) — one item per

block; naming the same id twice is refused.

  • return ("minimal" | "id" | "full", default "minimal").
{
  "name": "update_blocks",
  "arguments": {
    "boardId": "b1a2c3d4-...",
    "items": [
      { "id": "9f0e...", "patch": { "bgColor": "amber" } },
      { "id": "7c1a...", "patch": { "bgColor": "amber" } }
    ]
  }
}

delete_block

Delete a single existing block you can write to. Cascades to its edges; children are not deleted or re-hung — they lose their last link upward and become top-level, and the reply names them.

  • id (uuid, required).
{ "name": "delete_block", "arguments": { "id": "9f0e..." } }

create_edge

Connect two existing blocks you can write to, without recreating either. Choosing kind matters: "default" is a solid link and makes the source a parent of the target — use it only when adding a level to the tree. "goal" (dashed) is right for almost everything else: sibling to sibling, branch to branch, a reference back up.

  • sourceId (uuid, required).
  • targetId (uuid, required).
  • label (string, ≤255 chars).
  • kind ("default" | "goal", default "default").
  • color (string, nullable).
  • direction ("none" | "start" | "end" | "both", default "end").
  • weight (number, 0–10) — the declared strength; becomes the line's

thickness.

{
  "name": "create_edge",
  "arguments": { "sourceId": "9f0e...", "targetId": "7c1a...", "kind": "goal", "label": "depends on" }
}

update_edge

Update one or more fields on a single existing edge you can write to, without touching either block.

  • id (uuid, required).
  • patch (object) — any of label, kind, color, direction,

weight.

{ "name": "update_edge", "arguments": { "id": "e5b2...", "patch": { "weight": 3 } } }

delete_edge

Delete a single existing edge you can write to.

  • id (uuid, required).
{ "name": "delete_edge", "arguments": { "id": "e5b2..." } }

arrange_board

Lay the whole map out again in the shape its owner chose, and save where every block landed. The one repair for a map built over several apply_map calls, where a later branch was positioned against a structure that was still incomplete. Check arrange.keepsArranged from get_board first — when it is true the map has already re-arranged itself after every write and this will usually report nothing moved.

It is also the one way to put blocks that already exist in a chosen order. Order among siblings comes from where they sit, never from links — so a block re-parented later lands last, and a second link does not move it.

  • boardId (uuid, required).
  • siblingOrder (array of groups, 1–30, each an array of 2–50 block ids) —

blocks that share one parent (or several top-level blocks), in the order they must read. Every id must be on the map, or nothing moves. The order holds through every later arrange.

{ "name": "arrange_board", "arguments": { "boardId": "b1a2c3d4-..." } }

create_skill

Write a new private skill or scenario. A skill is standing instruction text that changes how your maps get built, and applies to every map of yours once written. A scenario is an interview that builds a map: its body is the protocol to follow when it runs, and it needs a checklist of keys to collect. Always private to you.

  • slug (string, required).
  • title (string, required).
  • body (string, required).
  • kind ("skill" | "scenario", default "skill").
  • meta (object, nullable) — for a scenario: the checklist keys to collect

and an estimated step count.

{
  "name": "create_skill",
  "arguments": { "slug": "claim-not-topic", "title": "Name nodes as claims", "body": "Every node title is a claim, never a bare topic word." }
}

update_skill

Edit one of your own skills or scenarios by its id. Only the fields you send change. kind cannot be changed after creation.

  • id (uuid, required).
  • patch (object) — any of slug, title, body, meta.
{ "name": "update_skill", "arguments": { "id": "5a1c...", "patch": { "body": "Updated wording." } } }

delete_skill

Delete one of your own skills or scenarios by its id. Maps already built with it are untouched; they simply stop being told about it.

  • id (uuid, required).
{ "name": "delete_skill", "arguments": { "id": "5a1c..." } }

start_scenario

Begin a scenario run on a map you own. Starting again on a map with an active run resumes it rather than failing. Returns a kickoff instruction — follow it.

  • boardId (uuid, required).
  • slug (string, required) — from list_scenarios.
{ "name": "start_scenario", "arguments": { "boardId": "b1a2c3d4-...", "slug": "project-kickoff" } }

advance_scenario

Record what the latest answer collected and move the interview forward by one step. Call this once per answer, before asking the next question.

  • boardId (uuid, required).
  • collected (object of string → string, required) — keys must match the

scenario's own checklist.

  • complete (boolean) — set once every key is in and the map has been

built from them; this is what closes the run.

{
  "name": "advance_scenario",
  "arguments": { "boardId": "b1a2c3d4-...", "collected": { "goal": "Ship v2 by October" } }
}

mark_notifications_read

Mark notifications read — the write half of list_notifications. Call it after showing the person what you read, never before.

  • ids (array of uuid, 1–100) — exactly the rows you showed.
  • all (true) — clear everything unread. Use ids or all, never both.
{ "name": "mark_notifications_read", "arguments": { "ids": ["a1b2...", "c3d4..."] } }

ask_user

Compose several questions at once and get them back as a numbered list to put to the person in your own reply. There is no form here — an MCP host has no panel to draw one — so use this instead of a question-per-message back-and-forth.

  • questions (array, required) — each `{ key, question, hint?, kind,

options? }, wherekindis"text"or"choice"`.

{
  "name": "ask_user",
  "arguments": {
    "questions": [
      { "key": "scope", "question": "Rebuild the whole map or just this branch?", "kind": "choice", "options": ["Whole map", "This branch"] }
    ]
  }
}

Delegation

delegate

Hand a task to an assistant and get its answer back in the same call — synchronous, with no memory between calls. Name where the work goes in exactly one of three ways: an already-seated agent, a block id to use as a branch root, or a whole map you own. A branch worker can rewrite, link and delete blocks in its branch but cannot add a block — send your own apply_map call for that. A board worker can do the same anywhere on its map and also add blocks with apply_map, so it can build: put a document on the map as one block, then ask it to turn that into a map. Naming a place never reaches further than you already can. This is the one tool on this surface that spends the account's own AI allowance, because the work runs on this server rather than in your own client.

A turn that builds can take minutes, and the call streams while it runs. Send a progressToken in the request's _meta and you get a notifications/progress about every 20 seconds, which a client with its own request timeout can reset that timeout on; without one, the stream carries keep-alive comments instead. One turn runs for at most 5 minutes, plus the step it is on when the limit arrives; a turn stopped there says so in its answer, and every change it already made stays on the map. If your client gives up anyway, the work goes on — read the map with get_board rather than calling delegate again, which would start a second turn.

  • agent (string) — the name or id of a seated agent from list_agents.
  • branch (uuid) — a block id to use as the branch root directly, naming no

agent.

  • board (uuid) — the id of a map you own, from list_boards, to work on

the whole of it. A map you were only invited to is refused; name a branch on it instead. Exactly one of agent, branch or board, never more than one, never none.

  • task (string, ≤4000 characters, required) — everything the assistant

needs; it has no memory of anything said before this call.

{ "name": "delegate", "arguments": { "branch": "9f0e...", "task": "Tidy this branch: merge duplicate blocks, fix stray links." } }

What it deliberately does not do

  • No system prompt of ours. Your client already holds a conversation and

already plans. It gets the data, not our assistant.

  • No pictures. No tool uploads an image, so an assistant cannot invent a

reference to one.

  • No settings. How a map is arranged and coloured is yours to choose in the

app; a connection reads those settings and applies them, and can never change them.

Limits

Every ordinary tool above runs no model of ours — the thinking happens in your own client, on your own key — so it does not spend a Corlecti Map AI allowance. delegate is the one exception, and its own entry says so.

Calling this endpoint is separately metered by volume: your plan gives you a number of MCP calls per day and per week, unrelated to the AI allowance. A refusal names the count and when it resets — never a percentage, since nothing was charged. There is also a short-window pause well above ordinary use, on every account together, so a burst of calls waits a few seconds rather than being refused outright.

The full figures for both, and every refusal code this endpoint can answer with, are on /docs/limits.

If something is wrong

A refused connection is almost always the key: mint a new one in Settings → MCP Server and replace it. If a tool answers that a map was not found, the connection is looking at a different account than you are.

Anything else: [email protected].