Skip to main content
Glama
README.md
# sketchbord-mcp

[Русская версия](README.ru.md)

An [MCP](https://modelcontextprotocol.io) server that lets an AI agent **visualize a task** on a [Sketchbord](https://sketchbord.ru) whiteboard — and read the board back, including whatever the human drew on it.

The point is *not* "draw a flowchart". The agent describes a task as a handful of **sections** — a system drawn as pillars with labelled arrows between them, a checklist, a mind map, a funnel, a fishbone, a sequence diagram, a roadmap, a wall of stickies, and sometimes a flow — and the server lays them out as a poster, so **one look explains the task**.

**One picture, not a tree of pages.** Boards used to grow drill-down levels: a block with children became a level of its own, the top level said almost nothing, and the part that explained anything sat two clicks down where nobody looked. A `lanes` section says it in one view — the parts side by side, what each does inside it, and what travels between them written on the arrows.

Four things make it more than a drawing API:

- **The content decides the structure, not the kind of task.** There is no board template: `pick_form` maps the *shape* of what you have — an order, a hierarchy, a comparison, a loop, a set of causes — onto the form that carries it, and says when each form is the wrong one. Two boards for two tasks should not look alike, and a section that repeats its neighbour is one the reader has to read twice for nothing.
- **It draws with the whole board.** Pictograms from the board's own catalogue (`list_icons` — drawn by the same hand, not pasted images), a colour per part, arrows that say what travels along them, and cards that sketch the screen they describe (`ui`: a search field, a button, three rows of a list). A picture where every element is a rectangle with words in it is one the reader has to *read*.
- **Edits are incremental.** `visualize_task` with `mode: merge` rewrites only the sections you name — everything else keeps its place, including the user's own stickies and arrows. `read_board` reports those user additions back (`userMarks`), so the agent can react to what a person sketched, e.g. turn it into code changes.
- **It reads the drawing back to you.** An agent draws blind: it never learns that its picture came out as four stacked bullet lists, that half the arrows say nothing, that a caption grew into a sentence. So every draw answers with what the drawing measurably *is* — and nothing else, because a rule ("boards should have five sections") is a shape repeated whatever the task, which is how every board ends up alike. Remarks are observations to accept or ignore per task, and a board with nothing worth remarking on gets no remarks at all.

## Tools

| Tool | What it does |
| --- | --- |
| `visualize_task` | **The tool that draws.** A task as sections — 27 forms: **lanes** (a system as pillars + labelled arrows) / process / glossary / note / list / steps / checklist / tree / mindmap / pyramid / venn / flow / states / cycle / sequence / funnel / timeline / roadmap / fishbone / table / quadrants / proscons / metrics / cards / stickies / kanban / legend. `mode: replace` draws the whole board; `mode: merge` rewrites only the sections you name and leaves the rest — and the user's own drawings — in place. A `checklist` draws **real, clickable checkboxes** whose ticked state lives in the document. `newBoard: "<task>"` draws onto a board created on the spot. Returns a URL. |
| `update_visual` | The edits that are *not* a section rewrite: `removeSections` by id, or element-level `ops` (`set_text`, `set_note`, `recolor`, `remove`, `add_sticky`). |
| `read_board` | Read a level back: sections (id, kind, title, texts) **+ `userMarks`** — what the user added, what a hand-drawn arrow connects, which section a note sits on. |
| `pick_form` | The map from the shape of your content to the form that carries it, **with the cases each form is wrong for**. No side effects. |
| `sketch_board` | **Several different** compositions for a request — starting points to cut down and mix, never templates to fill. No side effects. |
| `list_icons` | The pictogram ids you may put in `icon`. Fetched from the connected server, so it is never a stale copy. No side effects. |
| `create_board` | Create an empty board and return its id/URL (account keys). |
| `list_boards` | List reachable boards with ids and URLs, newest first. |
| `board_info` | Show what the key reaches: one bound board, or an account key that can create boards. |

## Setup

You need an API key (`bord_sk_…`) from [sketchbord.ru](https://sketchbord.ru):

- **Account key** (recommended) — **Мои доски** → **API-ключи** → create a key without choosing a board. It can create a board per task and draw on any board you can edit.
- **Board key** — the same page with a board selected, or inside a board: menu (☰) → **«Интеграции с ИИ»**. Bound to one board, so `boardId` is never needed.

### Configure your MCP client

The server speaks MCP over stdio. The key is the whole configuration — there is one hosted Sketchbord and the server knows where it is.

Claude Code / Claude Desktop (`.mcp.json` or `claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "sketchbord": {
      "command": "npx",
      "args": ["-y", "sketchbord-mcp"],
      "env": {
        "BORD_API_KEY": "bord_sk_xxx"
      }
    }
  }
}
```

From a clone instead:

```bash
npm install
npm run build
# then "command": "node", "args": ["/path/to/sketchbord-mcp/dist/index.js"]
```

### Environment

| Var | Required | Default | Notes |
| --- | --- | --- | --- |
| `BORD_API_KEY` | yes | — | `bord_sk_…` key; determines the reachable boards and permissions. **Normally the only one you set.** |
| `BORD_BOARD_ID` | no | — | Default board for an account key, so the agent needn't repeat it. |
| `BORD_API_URL` | no | `https://sketchbord.ru/api` | Development only. **Must be `https://` for any non-localhost host**: the server exits rather than put your key on the wire in clear text. |
| `BORD_TIMEOUT_MS` | no | `30000` | Request timeout, so a stalled API cannot hang the agent's turn. |
| `BORD_LANG` | no | `en` | Language the API answers errors in (`en` / `ru`). Set `ru` when your agent is talking to a Russian speaker. |

## Two kinds of key

- **Account key** (no board) — acts for your account, so the agent can **create a board per task** (`create_board`, or `visualize_task` with `newBoard`) and draw on any board you can edit. Set `BORD_BOARD_ID` if you'd rather it default to one project.
- **Board key** — bound to a single board, inferred from the key.

## Section anatomy

Every section has a stable `id` (that's what makes edits incremental), a `kind`, a `title`, and then whichever fields its kind uses:

- `items` (`note` / `tag` / `color` / `done` / `span` / `children`) — lists, steps, checklists, trees, mind maps, stickies, cards, timelines, metrics, the bands of a funnel (widest first) or a pyramid (apex first), the steps of a cycle, the sets of a venn, the cause categories of a fishbone;
- `groups` — the pillars of a `lanes` poster (`id` / `subtitle` / `icon` / `color`, and `bare` for the column of people down the left edge), kanban columns, the 4 quadrants, the two sides of `proscons` (first for, second against), the lanes of a `roadmap`;
- `links` — `lanes` only: the arrows between pillars (`from` / `to` by lane id, position or title, `label` for what travels along it, `both`, `dashed`);
- `nodes` + `edges` — flow / states, and `sequence`, where a node is a participant and an edge is a message;
- `columns` + `rows` — `table`; for a `roadmap`, `columns` are the periods and each item's `span` names the ones its bar covers;
- `text` — the paragraph of a `note`, the effect at the head of a `fishbone`, the caption for the overlap of a `venn`.

Any item, group or section may carry an `icon` (an id from `list_icons`; anything else is dropped rather than drawn as a placeholder). A card inside a `lanes` section may carry `ui` instead of `children` — `field` / `button` / `row` / `chip` — which sketches the interface it describes.

`width` is `narrow` / `normal` / `wide` — a hint about proportion, except on `lanes`, where it sets the width of one pillar. `accent` on the plan sets the board's colour, so two boards need not look alike.

**Flowchart node types:** `process` (step), `decision` (branch — use labelled edges), `error` (failure), `start`/`end` (terminals).

## Token cost

The tool definitions this server publishes are about **21 KB** (~6 000 tokens), and they sit in the agent's context on every request. Most of it is `visualize_task`'s schema, which is the price of a DSL an agent can fill in without a round trip. Everything else is deliberately thin: the long guidance on *which* form to pick lives in `pick_form` and `sketch_board`, which cost nothing until they are called, and `read_board` caps its answer at 60 000 characters so one board cannot fill a context window.

## Reading boards is a trust boundary

`read_board` returns text that other people wrote. The server wraps it in explicit
"untrusted data" delimiters and caps its size, because that content reaches the model
in the same context as your instructions — a sticky note saying "ignore previous
instructions" is a prompt-injection attempt, and it should be read as something a
user wrote on a whiteboard, not as a command.

## Example prompts

> Explain on a board how the service is put together: front end, API, workers, Postgres and S3.

A `lanes` section: a pillar per part, with a card for each thing it does, an icon on each card, and `links` between the pillars carrying "HTTP request", "SQL", "file URL". A `process` strip under it if the end-to-end path is worth spelling out, and a `glossary` strip if the reader is not on the team. `ui` on the front-end cards turns "there is a search screen" into a picture of one.

> Draw the task: importing a price list from CSV into the catalogue.

There is no fixed answer. The agent asks what the reader needs to know and picks a form per part: rows lost at each step of the import is a `funnel` with the numbers in `note`; several unrelated failure causes is a `fishbone`; what to touch in the code is a `tree`; what happens in which order between CSV, parser and catalogue is a `sequence`. Then a `checklist` for the work — real checkboxes, ticked by whoever does it.

> I drew an arrow and a sticky on the board — look and fix the code.

`read_board` returns `userMarks`, e.g. a sticky "needs a retry here!" sitting on the `flow` section and a hand-drawn arrow connecting two blocks. The agent acts on that, then reflects the decision back with `visualize_task` (`mode: merge`) or an `add_sticky` op — leaving the rest of the board untouched.

> Replace the plan and drop the legend.

`visualize_task` with `mode: merge` and one `plan` section, then `update_visual` with `removeSections: ["legend"]`. The `plan` card is redrawn **in place**; everything else keeps its position.

## Development

```bash
npm install
npm run build      # tsc → dist/
npm run typecheck
BORD_API_KEY=bord_sk_… BORD_API_URL=http://localhost:4000 npm start
```

## License

MIT — see [LICENSE](LICENSE).