# PowerScroll (npm · powerscroll-mcp)

Read and edit a live local-first PowerScroll visual notebook.

- Trust score: 82/100 (high trust)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-21

## Components

- npm · `powerscroll-mcp`: 82/100 (this document), [markdown](https://verifymcp.io/servers/cynacons-powerscroll/powerscroll-mcp.md), [page](https://verifymcp.io/servers/cynacons-powerscroll/powerscroll-mcp)

## Channel facts

- Registry: `npm`
- Package: `powerscroll-mcp`
- Version: `0.67.1`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-21.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 31 of 96 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 48/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 29 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 70/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 7972 tokens (~215/item across 37 items; 37 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 100/100
  - No destabilizing schema changes in the last 30 days.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 5 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "delete_page" implies "delete" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 37 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the PowerScroll MCP server?

PowerScroll runs locally as an npm package, launched with npx -y powerscroll-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add cynacons-powerscroll -- npx -y powerscroll-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "cynacons-powerscroll": {
      "command": "npx",
      "args": [
        "-y",
        "powerscroll-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "cynacons-powerscroll": {
      "command": "npx",
      "args": [
        "-y",
        "powerscroll-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add cynacons-powerscroll -- npx -y powerscroll-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "cynacons-powerscroll": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "powerscroll-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add cynacons-powerscroll --command npx --arg -y --arg powerscroll-mcp
```

### Hermes

```yaml
mcp_servers:
  cynacons-powerscroll:
    command: "npx"
    args: ["-y", "powerscroll-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "cynacons-powerscroll": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "powerscroll-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add cynacons-powerscroll -t stdio -c npx -a -y powerscroll-mcp
```

### Other

```json
{
  "mcpServers": {
    "cynacons-powerscroll": {
      "command": "npx",
      "args": [
        "-y",
        "powerscroll-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-21 (score 82, 0)

- [security] Stability: 0.97 → pass

### 2026-09-20 (score 82, +1)

No change was recorded against any check on this day. Stability & Change Management went from 93 to 97. That category is still filling its 30-day observation window: 28 days of observed history at the previous scan, 29 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-18 (score 81, +1)

No change was recorded against any check on this day. Stability & Change Management went from 87 to 90. That category is still filling its 30-day observation window: 26 days of observed history at the previous scan, 27 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-16 (score 80, +1)

No change was recorded against any check on this day. Stability & Change Management went from 80 to 83. That category is still filling its 30-day observation window: 24 days of observed history at the previous scan, 25 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-14 (score 79, +1)

No change was recorded against any check on this day. Stability & Change Management went from 73 to 77. That category is still filling its 30-day observation window: 22 days of observed history at the previous scan, 23 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-12 (score 78, +1)

No change was recorded against any check on this day. Stability & Change Management went from 67 to 70. That category is still filling its 30-day observation window: 20 days of observed history at the previous scan, 21 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-09 (score 77, +1)

No change was recorded against any check on this day. Stability & Change Management went from 57 to 60. That category is still filling its 30-day observation window: 17 days of observed history at the previous scan, 18 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-07 (score 76, +1)

No change was recorded against any check on this day. Stability & Change Management went from 50 to 53. That category is still filling its 30-day observation window: 15 days of observed history at the previous scan, 16 at this one. The score rises as the window fills, whether or not the server changes.

## MCP tools (37)

### `list_pages` (~41 tokens)

List every section and page in the connected notebook, with the number of text blocks on each and which page is currently open. Call this first to discover page ids.

### `read_page` (~491 tokens)

Read a page. DEFAULT CHANGED in v0.54: blocks[] is free-standing markdown only — diagram labels (text nodes with a groupId) no longer leak in as fake content, and diagrams are collapsed to diagrams[] {id, title, format, memberCount, bounds} with NO members and NO source. Images are a compact images[] index {id, alt, x, y, w, h, naturalWidth, naturalHeight, bytes, mini, scrollId} — never the base64 payload. Discovery flow: read_page → diagrams[].id → read_diagram / delete_diagram / fit_diagram; read_page → images[].id → read_image (writes a local file you open with vision). include defaults to ["blocks","diagrams","images"]; pass ["images"] for an images-only fetch or ["diagrams"] for diagrams-only. Optional scrollId filters all three lists. limit + cursor page blocks in column-major reading order (cursor = last block id from the previous page; stable across appends). Serialized reads are hard-capped at 20000 characters and never fail for size: blocks drop first (truncated {at, notice}), then per-diagram source is replaced with sourceOmitted {length, notice: "use read_diagram"}, then diagrams[] trims at an entry boundary, then images[] trims (imagesTruncated {at, notice}). A single oversized block has its markdown cut (markdownTruncated {fullLength, notice}). include_diagram_source:true adds source on each diagrams[] entry. Use get_block(blockId) to re-fetch one block after a cap.

Input parameters:

- `cursor` (string): Block id to start AFTER. Pass the last blockId (or nextCursor) from the previous read_page. Stable across appends.
- `include` (array): Which collections to return. Default ["blocks","diagrams","images"]. "images" alone is the images-only fetch; "diagrams" alone is diagrams-only.
- `include_diagram_source` (boolean): If true, each diagrams[] entry includes its source text. Default false.
- `limit` (integer): Max blocks to return (after filters). Combine with cursor to page.
- `pageId` (string): Page to read. Defaults to the page currently open in the app.
- `scrollId` (string): Restrict blocks, diagrams and images to this scroll band.

### `read_diagram` (~302 tokens)

Read one diagram: id, title, format, source, bounds, memberCount, and a page of members [{id, type, x, y, w, h, label?}]. member_limit + member_cursor page members (cursor = last member id; same style as read_page). Default member_limit is derived from the 20000-character budget so a typical page sits well under it. If the serialized response still exceeds the budget, members trim at an entry boundary (truncated {at, notice}). Source counts toward the budget; if source alone blows it, source is cut (sourceTruncated {fullLength, notice}) and the notice names the full length and suggests exporting as .drawio. Discover ids from read_page diagrams[]. An unknown id is NOT_FOUND. A non-diagram id is UNSUPPORTED naming the type. A draw.io diagram rendered as an exact snapshot (renderMode 'snapshot', v0.64+) returns members: [] and memberCount 0 BY DESIGN — its structure lives entirely in the source XML, which is what you read and edit.

Input parameters:

- `diagramId` (string, required): Diagram frame id from read_page diagrams[].id or create_diagram.
- `member_cursor` (string): Member id to start AFTER. Pass the last member id (or nextCursor) from the previous read_diagram.
- `member_limit` (integer): Max members to return. Default is derived from the 20000-character budget.

### `read_image` (~198 tokens)

Export one image by node id so you can look at it. Discovers ids from read_page images[].id. The notebook returns the embedded bytes over the bridge; this server decodes them and writes a local file. Optional out_path (absolute) names the destination — parent dirs are created; an existing file is overwritten. When omitted, the file lands in os.tmpdir()/powerscroll-images/<id>.<ext>. The response is {path, format, bytes, naturalWidth, naturalHeight, alt} — NEVER the base64 payload. Unknown id is NOT_FOUND. A non-image id is UNSUPPORTED naming the type. Then open the file with your vision tools.

Input parameters:

- `id` (string, required): Image node id from read_page images[].id.
- `out_path` (string): Absolute file path to write. Parent directory is created if missing. Omit to write os.tmpdir()/powerscroll-images/<id>.<ext>.

### `get_block` (~137 tokens)

Re-fetch a single markdown block by id (BlockSummary plus page location). Use this after read_page hits the size cap. A block larger than the 20000-character budget is returned in slices: the response carries markdownTruncated {fullLength, notice} and nextOffset — pass nextOffset back as offset to continue. Any block, any size, is fully readable in bounded calls. Unknown or non-block ids are NOT_FOUND.

Input parameters:

- `blockId` (string, required): Block id from read_page.
- `offset` (number): Character offset into the block markdown (from a previous response's nextOffset). Omit to start at 0.

### `fit_diagram` (~135 tokens)

Refit an existing diagram to the scroll band its frame sits in. Unlike placement-time fit (shrink-only), this scales BOTH directions: up to fill band-width minus padding when the diagram is under-width, down with the same 0.45 floor when over. Works on member diagrams (members rescale) and on renderMode 'snapshot' diagrams (the frame rescales; the image follows). Out-of-band frames are refused (PRECONDITION, naming the diagram). Discover ids from read_page diagrams[]. One undo in the app.

Input parameters:

- `diagramId` (string, required): Diagram frame id from read_page diagrams[].id.

### `create_section` (~43 tokens)

Create a new section (a tab in the notebook sidebar). Sections contain pages. A first empty page is created along with it.

Input parameters:

- `title` (string, required): Section title.

### `create_page` (~114 tokens)

Create a new page and open it. By default also writes the title onto the canvas as an H1 block so the page reads as a titled note.

Input parameters:

- `column` (integer): A4 page guide to place the heading in: 0 is the leftmost (default).
- `sectionId` (string): Section to create the page in. Defaults to the active section.
- `title` (string, required): Page title.
- `withHeading` (boolean): Write an "# Title" block onto the canvas too. Default true.

### `append_block` (~265 tokens)

Append a markdown block to the bottom of a page. This is the main way to write content. The markdown is rendered live in the app: use "- " for bullets, "1. " for numbered lists, "- [ ] " for checkboxes (which the user can then click to tick), "#"/"##" for headings, **bold**, `code`, tables, and $math$. Prefer one block per logical chunk (one list, one paragraph) rather than one block per line.

Input parameters:

- `column` (integer): Raw A4 page-guide index: 0 is the leftmost, 1 the guide to its right. Legacy fallback — prefer scrollId, which keeps pointing at the same scroll after scrolls are reordered. Ignored when scrollId is…
- `markdown` (string, required): Markdown content for the block.
- `pageId` (string): Page to append to. Defaults to the page currently open.
- `scrollId` (string): Scroll (named column) to write into. THIS IS THE PREFERRED WAY to choose where a block lands — call list_scrolls first and pass an id from it. Each scroll stacks independently, so two workstreams can…

### `insert_block` (~249 tokens)

Insert a markdown block into a scroll at a chosen position, shifting every occupant below it (text, diagram frames, images, shapes, ungrouped ink) down by the new block's height + 12px. Frame members ride the frame. Prefer after (a block or diagram-frame id): ids survive reordering, indices do not. Exactly one of after or index. index 0 is the top of the column (ceiling-clamped when a titled scroll arms the page ceiling). index counts packed occupants of the scroll, not read_page blocks[] — prefer after. Returns the new block plus displacedCount.

Input parameters:

- `after` (string): Insert after this occupant id (a markdown block or diagram frame in the same scroll). Preferred. Do not pass index at the same time.
- `index` (integer): 0-based index in the scroll's packed-occupant reading order (text and diagrams). 0 = column top. Do not pass after at the same time. Prefer after: index is not read_page blocks[] order.
- `markdown` (string, required): Markdown content for the block.
- `scrollId` (string, required): Scroll to insert into (from list_scrolls). Required.

### `insert_image` (~409 tokens)

Insert an image into a scroll at a chosen position, shifting every occupant below it (text, diagram frames, images, shapes, ungrouped ink) down by the image's display height + 12px. Frame members ride the frame. Source is exactly one of data (a data:image/...;base64, URI) or path (a local png/jpg/jpeg/gif/webp file this server reads and encodes — the app never sees paths). Both or neither is an error. Placement matches insert_block: prefer after (an occupant id); exactly one of after or index. index 0 is the column top (ceiling-clamped when a titled scroll arms the page ceiling). Optional alt (defaults to "image") and mini (land as a 160px-wide thumbnail). Oversized images are downscaled to a 2048px long edge like UI imports. Returns id + display and natural dims + displacedCount — never the base64 payload. One undo.

Input parameters:

- `after` (string): Insert after this occupant id (a markdown block or diagram frame in the same scroll). Preferred. Do not pass index at the same time.
- `alt` (string): Alt text. Defaults to "image".
- `data` (string): Base64 data URI (data:image/...;base64,...). Exactly one of data or path.
- `index` (integer): 0-based index in the scroll's packed-occupant reading order. 0 = column top. Do not pass after at the same time. Prefer after.
- `mini` (boolean): If true, land as a Mini thumbnail (default width 160). Displacement uses the mini height.
- `path` (string): Local file path (png, jpg, jpeg, gif, or webp). This server reads and encodes it; the app never sees the path. Exactly one of data or path.
- `scrollId` (string, required): Scroll to insert into (from list_scrolls). Required.

### `move_block` (~226 tokens)

Move a markdown block or diagram frame inside a scroll or across scrolls on the same page. Occupants below the source close up; occupants at the target open a gap. Diagram members ride the frame. ONE undo. Prefer after (a block or diagram-frame id) over index: ids survive reordering. scrollId is optional and defaults to the block's current scroll; when given it must be on the same page. Exactly one of after or index. Diagram members, shapes and images as the move target are refused by name. Returns the block plus displacedCount.

Input parameters:

- `after` (string): Place after this block id in the destination scroll. Preferred. Do not pass index at the same time.
- `blockId` (string, required): Markdown block or diagram frame to move (from read_page / diagrams[].id).
- `index` (integer): 0-based index in the destination scroll's remaining content blocks. Do not pass after at the same time.
- `scrollId` (string): Destination scroll. Omit to stay in the current scroll. Must be on the same page.

### `create_diagram_plantuml` (~535 tokens)

Draw a UML diagram on the page from PLANTUML source. (For Mermaid source, use create_diagram_mermaid instead - this tool refuses it rather than half-drawing it.) Two PlantUML dialects are supported, and the right one is detected automatically. (1) COMPONENT / COMPOSITE STRUCTURE: components, nested components, ports (port / portin / portout), provided and required interfaces, and assembly and delegation connectors. A composite-structure part puts its role in the label, as component "role : Type [multiplicity]" as alias. Nesting uses braces. (2) ACTIVITY / SWIMLANE FLOWCHARTS: |Lane| switches the swimlane, start and stop are the pseudostates, :action; is a step, and if (cond) then (label) / else (label) / endif adds a decision with guards on the arrows. Steps run top to bottom in source order and the lane fixes the column. What lands on the canvas is ordinary PowerScroll shapes and text inside a diagram frame, so the user can drag any part of it afterwards - this is NOT an image. Supply semantics only: name the steps or entities and what connects to what. Every coordinate is computed here from real text metrics, and there is no way to position anything yourself. Do NOT include skinparam, !include, !theme or any styling - PowerScroll supplies the style and those lines come back as skipped diagnostics. fork, split, while and repeat are refused rather than drawn wrong, and activity branches currently render in source order rather than as parallel paths that rejoin. The response carries the diagnostics, so one call is enough to know whether it came out right.

Input parameters:

- `column` (integer): Raw A4 page-guide index. Legacy fallback - prefer scrollId. Ignored when scrollId is given.
- `pageId` (string): Page to draw on. Defaults to the page currently open.
- `scrollId` (string): Scroll (named column) to draw into - call list_scrolls first. The diagram lands below whatever is already in that column, like append_block. Defaults to the leftmost.
- `source` (string, required): PlantUML text; @startuml/@enduml optional, one statement per line. Component example: component "gateway" as gw { portin telemetry ... }. Swimlane example: |Sensor| then start then :sample burst; the…
- `title` (string): Title shown on the diagram frame. Defaults to "Diagram".

### `create_diagram_mermaid` (~562 tokens)

Draw a diagram on the page from MERMAID source. (For PlantUML source, use create_diagram_plantuml instead.) A DOCUMENTED SUBSET is read, and anything outside it comes back as a diagnostic instead of being drawn wrong. (1) FLOWCHART: "flowchart TD" / "flowchart LR" / "graph LR" as the first line, then nodes A[Label], A(Label) and A{Label}, and edges A --> B, A -->|guard| B and A --- B. Chains such as A --> B --> C work. (2) SEQUENCE: "sequenceDiagram" as the first line, then participant X (optionally "participant X as Label") and messages A->>B: text and A-->>B: reply. TWO THINGS TO EXPECT. Node SHAPE is carried as a stereotype above the name, not as geometry: a {decision} renders as a box labelled "decision" rather than as a diamond, because the shared layout has one box shape. LAYOUT is left to right whatever direction the header names, and a sequence renders as participants side by side with numbered messages between them, not as lifelines running down the page. NOT SUPPORTED, and refused with a diagnostic: subgraphs, dotted (-.->), thick (==>) and circle/cross (--o, --x) links, compound node shapes such as A[[Sub]] or A((Circle)), loop/alt/opt/par/note blocks, and the class, state, ER and Gantt families. What lands on the canvas is ordinary PowerScroll shapes and text inside a diagram frame, so the user can drag any part of it afterwards - this is NOT an image. Supply semantics only; every coordinate is computed here from real text metrics. The response carries the diagnostics, so one call is enough to know whether it came out right.

Input parameters:

- `column` (integer): Raw A4 page-guide index. Legacy fallback - prefer scrollId. Ignored when scrollId is given.
- `pageId` (string): Page to draw on. Defaults to the page currently open.
- `scrollId` (string): Scroll (named column) to draw into - call list_scrolls first. The diagram lands below whatever is already in that column, like append_block. Defaults to the leftmost.
- `source` (string, required): Mermaid text. The FIRST line must be flowchart/graph with a direction, or sequenceDiagram. Example: "flowchart LR" then A[Read sensor] --> B{Uplink up?} then B -->|yes| C[Send batch].
- `title` (string): Title shown on the diagram frame. Defaults to "Diagram".

### `bridge_status` (~126 tokens)

Who else is working in this notebook. Several agents may be connected at once but only one may operate at a time, so call this when a tool comes back LOCKED, or before starting a long piece of work. Reports the connected notebook, every connected agent, which one holds the notebook, how long until it frees up, and whether that is you. This tool is never blocked. Note that a free result can go stale immediately -- another agent may take the lock before your next call -- so treat the LOCKED error as the real signal and this as a way to understand it.

### `create_diagram_svg` (~466 tokens)

Draw an SVG onto the page as native PowerScroll shapes. Every element becomes an ordinary rectangle, circle, line or text run that the user can select and drag afterwards -- it is transpiled, NOT embedded as an image. THIS TOOL IS THE OPPOSITE OF THE OTHER TWO: create_diagram_plantuml and create_diagram_mermaid want semantics and compute every coordinate for you, whereas here YOU have already done the layout and the coordinates are the payload. Use this when you want exact placement, and one of the others when you want a diagram laid out well without doing the geometry yourself. Supported: svg (viewBox and width/height set a uniform scale), g with translate and uniform scale, rect (incl. rx), circle, ellipse, line, polyline, polygon, and text with tspan and text-anchor. Paint via fill, stroke, stroke-width, stroke-dasharray and font-*, read from attributes or a style attribute and inherited down the tree. REFUSED, each with a diagnostic naming it: path (a bezier has no native node, and flattening it to line segments is a lie that survives every later edit), gradients, patterns, filters, masks, clipPath, use, symbol, image, foreignObject, textPath, style, script, animation and nested svg. rotate() transforms are refused too, because the native rotation turns about an element's own corner rather than the user-space origin, so honouring it would move your drawing. Keep to the supported subset and nothing is silently lost. The response carries the diagnostics, so one call tells you what was dropped.

Input parameters:

- `column` (integer): Raw A4 page-guide index. Legacy fallback - prefer scrollId.
- `pageId` (string): Page to draw on. Defaults to the page currently open.
- `scrollId` (string): Scroll (named column) to draw into - call list_scrolls first. Lands below whatever is already in that column. Defaults to the leftmost.
- `source` (string, required): SVG markup. A root <svg> with a viewBox is best -- it fixes the scale and the corner lands where the diagram is placed.
- `title` (string): Title shown on the diagram frame. Defaults to "Diagram".

### `create_diagram_drawio` (~1125 tokens)

Draw a draw.io / mxGraph diagram onto the page. BY DEFAULT (v0.64+) the source is rendered by the embedded draw.io viewer into an exact vector snapshot — colours, gradients, curved edges, double-headed arrows, HTML labels, shadows and every built-in shape render exactly as diagrams.net shows them. The response then carries renderMode 'snapshot' and elementCount 0: a snapshot diagram has NO member nodes BY DESIGN — the stored XML remains the editable source, read_diagram returns it, and "Export as .drawio" emits it verbatim. Library stencil ICONS (mxgraph.aws4/mscae/cisco) are the one gap: they render as their styled box + label, since stencil packs are not bundled. If the viewer extension is unavailable (e.g. offline before it was installed), the tool FALLS BACK to transpiling the source into native PowerScroll shapes and says so in warnings. Pass render:'nodes' to force that transpile path deliberately, when you WANT individually-selectable member nodes instead of exact paint. Everything below documents the transpiled subset — it applies to render:'nodes' and to the fallback, NOT to snapshots. THIS TOOL IS A SIBLING OF create_diagram_svg: YOU have already done the layout and the coordinates are the payload. Use create_diagram_plantuml or create_diagram_mermaid when you want a diagram laid out well without doing the geometry yourself. Supported vertices: default and rounded rectangles, ellipses, rhombi (as a 45° rect fitted to the cell), triangles, labels, standalone text cells, and flattened groups / containers / swimlanes. Paint via fillColor, strokeColor, strokeWidth, dashed=1 and dashPattern. rotation on a rectangle is honoured. Supported vertices also include UML module/component (rectangle with two left tabs), ports (small rectangles, including relative geometry + mxPoint offset so they sit on the parent edge), and box-like mxgraph.uml / mxgraph.basic library shapes (drawn as rectangles; an ignored diagnostic names the original stencil). Supported edges: a st…

Input parameters:

- `column` (integer): Raw A4 page-guide index. Legacy fallback - prefer scrollId.
- `pageId` (string): Page to draw on. Defaults to the page currently open.
- `render` (string): How to draw it. 'snapshot' (default): exact image via the draw.io viewer. 'nodes': transpile into native, individually-editable shapes (lossy — see the subset above).
- `scrollId` (string): Scroll (named column) to draw into - call list_scrolls first. Lands below whatever is already in that column. Defaults to the leftmost.
- `source` (string, required): draw.io / mxGraph XML. An <mxfile> or a bare <mxGraphModel>. Compressed (base64+deflate) pages are accepted — they are inflated before use.
- `title` (string): Title shown on the diagram frame. Defaults to "Diagram".

### `create_diagram` (~183 tokens)

DEPRECATED - use create_diagram_plantuml, create_diagram_mermaid, create_diagram_svg or create_diagram_drawio instead, which name the language they take and each document their own subset. This name still works and sniffs the format from the source, but it will be removed. Behaviour is otherwise identical.

Input parameters:

- `column` (integer): Legacy fallback - prefer scrollId.
- `pageId` (string): Page to draw on. Defaults to the open page.
- `render` (string): draw.io sources only: 'snapshot' (default) renders an exact image; 'nodes' transpiles to native shapes.
- `scrollId` (string): Scroll to draw into. Defaults to the leftmost.
- `source` (string, required): Diagram source. The format is sniffed.
- `title` (string): Title shown on the diagram frame.

### `list_scrolls` (~111 tokens)

List the scrolls (named vertical columns) on a page, with how many blocks each holds. Call this before writing when a page may have more than one workstream on it: passing the returned scrollId to append_block keeps your blocks in their own column instead of stacking under someone else's. A block belongs to whichever scroll it physically sits in, so a block the user drags to another scroll moves with it.

Input parameters:

- `pageId` (string): Page to inspect. Defaults to the page currently open.

### `create_scroll` (~115 tokens)

Create a new named scroll on a page — a fresh vertical column to the right of the existing ones, with its title shown at the top on the canvas. Use this to keep a separate workstream (say "Open questions" beside "Research log") from interleaving with what is already there. Returns a scrollId to pass to append_block.

Input parameters:

- `pageId` (string): Page to add the scroll to. Defaults to the page currently open.
- `title` (string, required): Title shown at the top of the scroll.

### `rename_scroll` (~112 tokens)

Rename a scroll. The new title appears immediately at the top of that column on the canvas. Naming a previously untitled scroll is how its header first appears. Pass an empty title to untitle it: the header disappears and the page ceiling disarms — a plain page is one untitled scroll, not zero scrolls.

Input parameters:

- `scrollId` (string, required): Scroll to rename (from list_scrolls).
- `title` (string, required): New title. Empty string untitleds the scroll (header gone, ceiling off).

### `move_scroll` (~159 tokens)

Move a scroll (named column band) left or right, or to an absolute column. Members, grouped ink and the band's own width travel with it; neighbouring scrolls shift to make room and cumulative offsets are recomputed. A diagram belongs to the band of its frame origin — the whole group moves, it is never split. Refuses with NOT_FOUND on an unknown id and PRECONDITION when the scroll is already at the named edge.

Input parameters:

- `direction` (string): Step one band left or right. Refused at the corresponding edge.
- `scrollId` (string, required): Scroll to move (from list_scrolls).
- `toColumn` (integer): Absolute 0-based column to move to. Refused when already there or past an edge.

### `resize_scroll` (~107 tokens)

Resize a scroll band by stable id. The width is clamped to PowerScroll's shared limits, persists on that page, and every node or stroke in bands to the right moves by the same delta. The resized band's own content stays fixed. One undo restores width and positions. list_scrolls reports the effective width.

Input parameters:

- `scrollId` (string, required): Scroll to resize (from list_scrolls).
- `width` (number, required): Requested band width in canvas pixels; clamped by the app.

### `update_block` (~141 tokens)

Replace the markdown of an existing block, found via read_page. Use this to revise content or tick a checkbox ("- [ ]" to "- [x]") rather than appending a duplicate block. If the block grows or shrinks, every occupant below it in the scroll (text, diagram frames, images, shapes, ungrouped ink) shifts by the height delta; frame members ride the frame. One undo. Returns displacedCount. A diagram-member id is refused (UNSUPPORTED) naming the owning diagram — redraw the source instead.

Input parameters:

- `blockId` (string, required): Block id from read_page.
- `markdown` (string, required): New markdown content.

### `rename_page` (~103 tokens)

Retitle a page in the sidebar. If the page still opens with the "# Old title" heading block that create_page wrote, that block is retitled too so the two do not drift apart; a hand-edited heading is left alone.

Input parameters:

- `pageId` (string): Page to rename. Defaults to the page currently open.
- `title` (string, required): New page title.
- `updateHeading` (boolean): Also rewrite the matching canvas H1. Default true.

### `move_page` (~97 tokens)

Move a page into a different section. Section ids come from list_pages. A section must keep at least one page, so moving the only page out of a section is refused — create another page there first.

Input parameters:

- `pageId` (string): Page to move. Defaults to the page currently open.
- `toIndex` (integer): Position within the destination section. Defaults to last.
- `toSectionId` (string, required): Destination section id.

### `rename_notebook` (~76 tokens)

Rename the notebook itself (the title shown in the app and used for future Save As filenames). This does NOT rename the .html file already on disk — that keeps its current name until the user saves to a new location. The result reports the bound filename so you can tell them.

Input parameters:

- `filename` (string, required): New notebook name.

### `delete_page` (~92 tokens)

Delete a page and everything on it. Cannot be undone from here — the app has no agent-facing undo — so ask the user before calling, then pass confirm:true. Refuses when the page is the last one in its section.

Input parameters:

- `confirm` (boolean, required): Must be true. Confirms the user agreed to the deletion.
- `pageId` (string): Page to delete. Defaults to the page currently open.

### `delete_section` (~82 tokens)

Delete a section AND every page inside it. This is the widest-reaching delete available — confirm the page list with the user first (list_pages shows what would go). Refuses when it is the notebook's only section.

Input parameters:

- `confirm` (boolean, required): Must be true. Confirms the user agreed to the deletion.
- `sectionId` (string, required): Section to delete.

### `delete_scroll` (~300 tokens)

Delete a named scroll (a column band). When the band holds ANY content — nodes, group members, or ink — content is REQUIRED: "delete" removes them with the band, "keep" closes the gap and leaves them where they are. There is deliberately no default. The old default was keep (confirm:true used to be a safe trim); a default flip would silently destroy callers that never passed the flag. An empty band needs no content param. Membership is group-aware: a diagram belongs to the band of its frame origin, and its members and grouped ink follow that verdict. A page must keep at least one scroll: a plain page is one untitled scroll — untitle it instead of deleting the last one.

Input parameters:

- `confirm` (boolean, required): Must be true. Confirms the user agreed to the deletion.
- `content` (string): Required when the band is not empty. "delete" removes the band's nodes, group members and ink with the scroll. "keep" closes the gap and leaves them. There is no default — the old default was keep, a…
- `scrollId` (string, required): Scroll to delete (from list_scrolls).
- `withBlocks` (boolean): Deprecated alias for content. true = delete, false = keep. Ignored when content is set. The response includes a deprecation notice.

### `delete_block` (~142 tokens)

Delete a single markdown block by id. Use read_page first to get block ids and check you are removing the right one. Passed a diagram FRAME id, this cascades to the frame's members and grouped strokes — same primitive as delete_diagram, which names the act and reports the counts. Passed a diagram MEMBER id (a node whose groupId is owned by a diagram frame), the call is refused (UNSUPPORTED) naming the owning diagram and pointing at delete_diagram or the redraw path.

Input parameters:

- `blockId` (string, required): Block to delete (from read_page).
- `confirm` (boolean, required): Must be true. Confirms the user agreed to the deletion.

### `delete_diagram` (~183 tokens)

Delete a diagram frame and everything that belongs to it: every node whose groupId is the frame id, and every stroke grouped with it. Use this when you placed a diagram you no longer want — delete_block on the frame id does the same cascade, but this tool names the act and reports how many members and strokes went. Occupants below the frame in the same scroll close the gap. An unknown id is NOT_FOUND. Passing a non-diagram node id is refused (the message names the type and points at delete_block). The bridge has no undo, so pass confirm:true once the user has agreed. Returns deletedMembers and deletedStrokes.

Input parameters:

- `confirm` (boolean, required): Must be true. Confirms the user agreed to the deletion.
- `diagramId` (string, required): Id of the diagram frame (from read_page diagrams[].id or create_diagram).

### `get_background` (~94 tokens)

Read the canvas look a page is actually drawn with: which guide style is active and which background colour. A page may override the notebook default, so the reply also carries "source" (whether each value came from the page or the notebook) and "notebookDefault". Call before set_background when you intend to restore the previous look afterwards.

Input parameters:

- `pageId` (string): Page to read. Defaults to the active page.

### `set_background` (~234 tokens)

Change the notebook's canvas look. "scroll" renders each column as one continuous vertical sheet with light page separators — best for long reading and for notes an agent is filling in. "pages" shows detached A4 cards, "grid" a dot grid, "none" a blank canvas. The setting is stored in the notebook itself, so it survives closing and reopening. Pass at least one of guideStyle or color.

Scope defaults to "notebook" (every page that has not overridden it). Pass scope:"page" to change one page only — useful when a notebook wants a scrolling page of notes next to a grid page of diagrams.

Input parameters:

- `color` (string): Canvas background colour. "paper" is a warm off-white.
- `guideStyle` (string): Guide overlay to use.
- `pageId` (string): Page to change when scope is "page". Defaults to the active page.
- `scope` (string): Which layer to write. "notebook" (default) sets the default every page follows unless it overrides it, and leaves existing overrides alone. "page" overrides one page only.

### `save_notebook` (~64 tokens)

Write the notebook back to the file it was opened from. Only works when the notebook is already bound to a file: the Save As picker needs a click from the user, which an agent cannot supply. Call this after a batch of edits so the work is on disk.

### `check_update` (~58 tokens)

Check whether a newer PowerScroll release exists on GitHub. Reports the running version and the latest one. Note "checked": when false the API was unreachable or rate limited and the status is unknown — that is not the same as up to date.

### `run_update` (~95 tokens)

Install the latest PowerScroll release into the current notebook file. This rewrites the .html on disk and reloads the app, which drops this bridge connection until the notebook reconnects. Ask the user before calling it, then pass confirm:true. A safety backup is downloaded first where the browser allows it. Run check_update first.

Input parameters:

- `confirm` (boolean, required): Must be true. Confirms the user agreed to the overwrite + reload.

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/cynacons-powerscroll/powerscroll-mcp#diagnostics

## Score history

- 2026-09-21: 82
- 2026-09-20: 82
- 2026-09-19: 81
- 2026-09-18: 81
- 2026-09-17: 80
- 2026-09-16: 80
- 2026-09-15: 79
- 2026-09-14: 79
- 2026-09-13: 78
- 2026-09-12: 78
- 2026-09-11: 77
- 2026-09-10: 77
- 2026-09-09: 77
- 2026-09-08: 76
- 2026-09-07: 76
- 2026-09-06: 75
- 2026-09-05: 75
- 2026-09-04: 74
- 2026-09-03: 74
- 2026-09-02: 73
- 2026-09-01: 73
- 2026-08-31: 72
- 2026-08-30: 72
- 2026-08-29: 68
- 2026-08-28: 68
- 2026-08-27: 68
- 2026-08-26: 68
- 2026-08-25: 68
- 2026-08-24: 68
- 2026-08-23: 68

## Common questions

### What is the PowerScroll MCP server?

PowerScroll is an MCP server listed in the public MCP registry as io.github.CynaCons/powerscroll. Read and edit a live local-first PowerScroll visual notebook. This page covers its npm package (powerscroll-mcp).

### Is the PowerScroll MCP server safe to use?

PowerScroll scores 82 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 21 September 2026. It declares no install or post-install scripts. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the PowerScroll MCP server expose?

PowerScroll exposes 37 tools: list_pages, read_page, read_diagram, read_image, get_block, and 32 more. Their descriptions and schemas cost roughly 7,972 tokens of context every time the server is loaded.

### Is the PowerScroll MCP server still maintained?

PowerScroll is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the PowerScroll MCP server under?

PowerScroll declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/powerscroll-mcp
- Socket report: https://socket.dev/npm/package/powerscroll-mcp
- Repository: https://github.com/CynaCons/PowerScroll
- Changelog RSS feed: https://verifymcp.io/servers/cynacons-powerscroll/powerscroll-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/cynacons-powerscroll/powerscroll-mcp.json
- HTML version of this page: https://verifymcp.io/servers/cynacons-powerscroll/powerscroll-mcp
