lakeboard-converter

Read XMind (.xmind) and Yuque Lakeboard (.lakeboard) mind maps from local files as Markdown, a JSON topic tree or an outline. All sheets, notes, labels and links. Local, read-only, stdio via npx.

Documentation

lakeboard-converter — Mind maps to Markdown your agents can read.

Convert Yuque Lakeboard (.lakeboard) and XMind (.xmind) mind maps into Markdown or a fresh .xmind. Comes as a CLI, a guided TUI, an MCP server and a drag-and-drop web UI (convert.ledring.dev). English | 简体中文

Quickstart

Open convert.ledring.dev to convert a file in your browser.

Or use the CLI without installing it globally (requires Node 20.12+):

npx -y lakeboard-converter convert ./map.xmind --format md --out-dir ./out

Read Markdown to stdout without writing files:

npx -y lakeboard-converter read ./map.xmind --format md

Start the read-only MCP server:

npx -y lakeboard-converter mcp

Development

git clone https://github.com/FelixFangX/lakeboard-converter.git
cd lakeboard-converter && npm ci
npm run convert -- ./test-fixtures/test.lakeboard --format md --out-dir ./out

Usage

ModeCommand
Guided TUInpm run dev
CLInpm run convert -- <file> --format md|xmind|both [--markdown-style generic|xmind] [--out-dir DIR] [--output FILE] [--overwrite]
Web UInpm run dev:web (production build: npm run build:web)
Library (from source)import { convertBuffer, convertFile } from './src/index.js'

Existing output files are never overwritten unless you pass --overwrite.

Web UI

Live demo: https://convert.ledring.dev

The web UI runs entirely in your browser: files never leave your machine. It is self-hosted with no third-party requests, including fonts. Run npm run dev:web locally, or try the bundled Lakeboard and XMind samples.

Deploy (Cloudflare Workers)

With CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID set in your environment:

npm ci && npm run deploy:web

wrangler.jsonc serves dist-web/ as static assets with single-page application fallback.

To deploy a preview build to lakeboard-convert-preview.<subdomain>.workers.dev instead (it has no custom domain), run npm run build:web && npx wrangler@3 deploy --env preview.

For agents (MCP and --json)

Agents get one read-only tool, convert(path, format, sheet?, maxNodes?, maxDepth?), where format is md, json (topic tree) or outline (compact indented text). Requires Node 20.12+.

Start the MCP server:

npx -y lakeboard-converter mcp

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "lakeboard-converter": {
      "command": "npx",
      "args": ["-y", "lakeboard-converter", "mcp"]
    }
  }
}

Cursor (.cursor/mcp.json or ~/.cursor/mcp.json):

{
  "mcpServers": {
    "lakeboard-converter": {
      "command": "npx",
      "args": ["-y", "lakeboard-converter", "mcp"]
    }
  }
}

Codex CLI (~/.codex/config.toml):

[mcp_servers.lakeboard-converter]
command = "npx"
args = ["-y", "lakeboard-converter", "mcp"]

Read a file from the CLI, or install the CLI globally:

npx -y lakeboard-converter read ./map.xmind --format md
npm i -g lakeboard-converter

From source

npm run build
node /abs/path/lakeboard-converter/dist/cli.js mcp
node dist/cli.js read ./test-fixtures/test.lakeboard --format json --json
node dist/cli.js convert ./test-fixtures/test.xmind -f md --out-dir out --json

Every CLI read JSON result has schemaVersion: 1, source, format, nodeCount, maxDepth and content. Errors with --json print {"error": "..."} and exit 1. See AGENTS.md for contributors.

Output and limits in 0.4.0

All XMind sheets are included. A Lakeboard document has one sheet named after its root. Untitled XMind sheets use Sheet N. With multiple sheets, generic Markdown uses # <sheet title> followed by ## <root title> and the topic list. A single sheet keeps # <root title>. First-level list items start at column 0; deeper items use two more spaces. Notes align with the item's content column. Linked topics use [Title](url) directly, including headings, with brackets in titles escaped. Generic Markdown labels follow the title as code spans, such as - Split vendor chunk `P1` `tech-debt` . Outlines use - Title [P1, tech-debt] and a sheet heading before each outline when there are several sheets.

XMind-import Markdown uses one root list per sheet separated by a blank line, without headings. It omits labels so they do not become part of imported topic titles.

Both CLI commands accept --sheet <index|name> (1-based index or exact case-insensitive title). MCP accepts sheet as a string or integer. Selection is recorded in selectedSheet; unknown selections list available sheets.

lakeboard-converter read /absolute/path/map.xmind --sheet Roadmap --max-nodes 5000 --max-depth 3 --json
lakeboard-converter convert ./map.xmind --sheet 2 --format md

readMindMap(file, format, options) and CLI read / MCP default to maxNodes: 2000; valid values are integers from 1 to 50000. Optional maxDepth is a nonnegative integer. The root is depth 0, so a root with children only has maxDepth: 1. Limits prune topics in pre-order across sheets. truncated and warnings report omitted topics. Increase the limits or select a sheet to read more. File conversion has no size limit. CLI read without --json and MCP text append warnings as > Note: ....

Use absolute paths for MCP: relative paths resolve against the server's working directory, which may differ from your terminal's. CLI read and MCP expand leading ~ or ~/ using the home directory. Missing relative paths report the resolved path and server working directory.

JSON format and MCP results

CLI read --json keeps schemaVersion: 1 and the existing fields, adding sheets: [{ index, title, nodeCount, maxDepth }], optional selectedSheet: { index, title }, truncated, and warnings. Counts describe the selected input before truncation; maxDepth always counts the root as depth 0.

For format: "json", content is a topic tree for one sheet. Starting in 0.4.0, multiple sheets produce { sheets: [{ index, title, root: AgentTopic }] }. Limits apply to these trees as well as Markdown and outlines.

MCP returns one plain text block containing Markdown, outline text, or pretty-printed JSON trees. It also returns structuredContent with { schemaVersion, file, source, format, nodeCount, maxDepth, sheets, selectedSheet?, truncated, warnings }, validated against the tool's registered outputSchema. The text is no longer a JSON-stringified result envelope. Warnings are appended as a final > Note: ... line. Errors use isError: true with a clear message; the tool remains read-only.

Cursor CLI in headless mode (cursor-agent -p) needs --force (plus --approve-mcps) or the tool call is rejected.

How it works

.lakeboard ─┐                          ┌─ renderGenericMarkdown → # root + nested list
            ├─ parse → MindMapDocument ─┼─ renderXmindMarkdown  → root list per sheet for XMind import
.xmind ─────┘     (TopicNode tree)      └─ renderXmind          → minimal .xmind package

The seam is convertBuffer / convertFile in src/index.ts. Every input format parses into one MindMapDocument, so a new format needs one parser and no renderer changes. The CLI and TUI use that seam. The web UI uses its browser converter with shared fallback ID allocation; agents use readMindMap in src/agent.ts, which never writes files.

Keeps text, hierarchy, order, notes and links. All XMind sheets are read. Labels appear inline in generic Markdown and outlines. Drops styling, colour and layout. XMind 8 (legacy XML) isn't supported.

Testing

npm test            # vitest specs at the convert, readMindMap and MCP seams, using test-fixtures/
npm run typecheck

License

MIT