{"id":"f2ca30e5-065d-4661-99e4-526cbf6619d2","slug":"mindnode-mcp","kind":"mcp","name":"mindnode-mcp","summary":"MCP server to read/write MindNode mind maps directly via their .mindnode plist format (no AppleScript/Shortcuts)","owner":"masamitsu-konya","repoUrl":"https://github.com/masamitsu-konya/mindnode-mcp","license":"MIT","licenseTier":"permissive","trust":"community","stars":2,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-06-22T04:20:11.000Z","addedAt":"2026-10-21T17:37:11.149Z","logoUrl":"https://avatars.githubusercontent.com/u/1867291?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra","utilities"],"tags":[],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T21:53:38.367Z","sample":false,"url":"https://conduits.dev/tools/mindnode-mcp","language":"Python","transports":["stdio"],"install":{"pypi":"mindnode-mcp"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# mindnode-mcp\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/)\n\n**English** | [日本語](README.ja.md)\n\nAn [MCP](https://modelcontextprotocol.io) server that lets an AI assistant\n(Claude, etc.) **read and write your [MindNode](https://mindnode.com) mind maps\ndirectly** — by parsing the `.mindnode` file format itself. No AppleScript, no\nShortcuts, no export/import dance.\n\nAsk your assistant to *\"read my project map\"*, *\"add these three ideas under\nMarketing\"*, *\"connect the API node to the Auth node\"*, or *\"turn this outline\ninto a new mind map\"* — and it operates on the real files MindNode syncs.\n\n> **Why this exists:** MindNode dropped its AppleScript dictionary, and its\n> Shortcuts/URL-scheme automation is thin. But a `.mindnode` document is just a\n> package whose `contents.xml` is an Apple **binary plist** — a clean recursive\n> node tree. Read/write that, and you get full programmatic control.\n\n## Requirements\n\n- **macOS** with [MindNode](https://mindnode.com) installed (format version 9 —\n  current MindNode releases)\n- **Python 3.11+** and [uv](https://github.com/astral-sh/uv)\n- An MCP client (e.g. [Claude Code](https://claude.com/claude-code))\n\n## Install\n\n```bash\ngit clone https://github.com/masamitsu-konya/mindnode-mcp.git\ncd mindnode-mcp\nuv sync\n```\n\nRegister it with Claude Code (user scope = available everywhere):\n\n```bash\nclaude mcp add --scope user mindnode -- uv --directory \"$PWD\" run mindnode-mcp\n```\n\nThen start a new session and run `/mcp` (or `claude mcp list`) to confirm it\nshows **mindnode ✔ Connected**.\n\nBy default it finds your MindNode documents in the iCloud container\nautomatically. Point it elsewhere with the `MINDNODE_DOCS_DIR` environment\nvariable (a local library, or a fixture folder for testing).\n\n## Usage\n\nTalk to your assistant in plain language — it picks the right tool. Examples:\n\n| You say | What happens |\n|---------|--------------|\n| \"List my mind maps\" | `list_documents` → names + dates, newest first |\n| \"Read my *Project Plan* map\" | `read_document` → the full node tree as JSON |\n| \"Search all my maps for 'pricing'\" | `search_nodes` → every matching node + its document |\n| \"Add 'Hire a designer' under the Team node in *Roadmap*\" | `add_node` |\n| \"Connect 'Frontend' to 'API' with the label 'calls'\" | `add_connection` |\n| \"Tag the 'Launch' node as #urgent\" | `add_tag` (creates the tag if new) |\n| \"Mark 'Ship v1' as done\" | `set_task` |\n| \"Attach ~/Desktop/wireframe.png to the Design node\" | `attach_image` |\n| \"Make a new map 'Q3 Goals' with branches Sales, Product, Hiring\" | `create_map` |\n\nNodes can be referenced by their text (a case-insensitive substring is enough)\nor by their exact id (ids come back from `read_document`).\n\n## Tools\n\n| Tool | Kind | What it does |\n|------|------|--------------|\n| `list_documents` | read | All `.mindnode` files, newest first |\n| `read_document` | read | Mind maps as `{id, text, note?, task?, tags?, attachment?, children?}` trees, plus `connections` and the document's tag list |\n| `search_nodes` | read | Substring search over node text + notes, in one document or all |\n| `add_node` | write | Add a node under a parent (by id or text), with optional note |\n| `add_connection` | write | Cross-link two existing nodes with an optional label and arrow direction |\n| `add_tag` / `remove_tag` | write | Tag / untag a node (tags are document-wide and auto-created) |\n| `set_task` | write | Turn a node into a checkbox task and set done / todo |\n| `attach_image` | write | Attach a local image to a node (copied into the package's `resources/`) |\n| `create_map` | write | Create a new `.mindnode` from a title + (optionally nested) outline |\n\n### Connections (cross-links)\n\nFree links between any two nodes, independent of the parent/child tree (stored\nat `canvas.crossConnections[]`). `add_connection(document, start, end, label?,\ndirection?)` — `direction` ∈ `forward` (default) / `backward` / `both` / `none`.\n`read_document` returns each as `{id, start_id, end_id, start_text, end_text,\ndirection, label?}`.\n\n### Tags, tasks, attachments\n\n`read_document` surfaces these per node (and lists all tag names at the top):\n\n- **Tags** — normalized: `canvas.tags[]` defines `{tagID, name, color}`,\n  `node.tags[]` references tagIDs. `add_tag` auto-defines a tag of that name if\n  it doesn't exist, then attaches it (idempotent).\n- **Tasks** — `node.task = {state, uuids}`, where `state` 1 = todo, 2 = done.\n  `set_task(document, node, done)` toggles it.\n- **Attachments (images)** — `node.attachment = {fileName, size, tintKind,\n  type}`; image bytes live in `resources/<fileName>`. `attach_image` copies the\n  file in and links it, clamping display width to 300px (matching MindNode).\n\n## How it works\n\nA `.mindnode` document is a **package directory**. Its `contents.xml` — despite\nthe extension — is an Apple **binary plist** holding the mind map (format\n**version 9**):\n\n```\ncanvas.mindMaps[].mainNode        # root node of each map\n  ├─ nodeID                       # UUID\n  ├─ title.text                   # the node's text, stored as small HTML\n  ├─ note / task / tags / attachment\n  └─ subnodes[]                   # children (same shape, recursive)\ncanvas.crossConnections[]         # free links between nodes\ncanvas.tags[]                     # tag definitions\n```\n\nThe server reads and writes this with Python's standard `plistlib`, so it needs\n**no third-party plist libraries**. Node text round-trips through a minimal\nHTML encode/decode (with proper escaping).\n\n## Write safety\n\nThese tools mutate your real files, so every write:\n\n- **backs up** `contents.xml` to a timestamped `.bak-*` first,\n- writes to a temp file then **atomically replaces** it (no partial writes),\n- **preserves keys it didn't author** (styling, layout, print info), and\n- drops the stale QuickLook preview so it regenerates.\n\n`create_map` clones an existing document as a structural skeleton (keeping all\nopaque auxiliary files valid) and overwrites only the node tree.\n\n> **Caveat — document open in MindNode.** Writes go straight to disk. If MindNode\n> (or another device via iCloud) has the same document open, its next autosave\n> can clobber the change, or you may get an iCloud conflict copy. Close the\n> document in MindNode before writing, or reopen it afterwards to pick up the\n> edit. Backups make this recoverable, but it's cleaner to avoid.\n\n## Development\n\n```bash\nuv run python tests/smoke.py\n```\n\nThe smoke test exercises reads against your real documents (read-only) and all\nwrites against throwaway temp copies — it never modifies your actual maps. It\nalso asserts that generated structures (nodes, connections, tags, tasks, image\nattachments) match the schema of real MindNode files key-for-key.\n\n## Status & roadmap\n\n- [x] Read — list / read / search\n- [x] Write — add_node / create_map\n- [x] Connections / cross-links — read + add_connection\n- [x] Tags, tasks, image attachments — read + write\n- [ ] Non-image attachments (links, stickers)\n- [ ] Tag color palette / rename, task removal\n- [ ] Connection waypoint editing\n\n## License\n\n[MIT](LICENSE) © 2026 Masamitsu Konya\n"}