A method for building a note system out of claims rather than clippings, and the tooling that runs it on WorkFlowy.
Most note systems accumulate. This one is built to compound: you divide your thinking into a small set of durable regions, and you allow nothing into them that is not a claim you could argue with or an action someone will take. A claim is a sentence you could contradict. "Team structure constrains architecture more reliably than the reverse" is a claim; "Conway's Law and team topologies" is a container you have to open before it tells you anything. Everything else here exists to make that one distinction cheap enough to hold to.
Read the method first. It is the part worth your time, and it applies to any outliner, with or without this software. WorkFlowy now ships its own AI, and so does everyone else; asking your notes a question in plain language has become a commodity, and the plumbing in this repository matters less every month. What you wrote down, and where it lives, is the part no model can fix for you afterwards. That is the bet this repository is increasingly made of: the retrieval half is being won by the native tools, and should be, while the claim discipline is not a feature anyone will ship you.
If you want it running rather than only understood, the rest of this README is
the software: a Rust MCP server connecting Claude (Desktop, Code, or claude.ai)
to your workspace, a wflow-do CLI with the same surface for scripts and cron,
and a skill file that carries the method as instructions a model follows.
In practice that means you say "capture this as a task under Projects" mid-conversation and it lands in the right region, tagged and dated. You ask "what do I have on organisational design?" and get an answer assembled from your own claims. You paste a WorkFlowy link and Claude knows which node you mean. You run a morning review as a single question.
Two ways in:
- The method only. Read
docs/METHOD.mdandtemplates/skills/wflow/SKILL.md, write your regions down, and apply it by hand. Install nothing. - The method, automated. Hand
BOOTSTRAP.mdto Claude and let it run the install: build, wire the host, seed your private data directory, cache your structural node IDs, install the wflow skill that drives every later session. About ten minutes.
Before you install anything, it is worth reading
docs/SURFACES.md, which says candidly what WorkFlowy's own
tools now do better than this one and what is left that only this repository
provides. Several people should read it and then install the wf CLI instead.
Your data stays yours. The repo ships only generic templates; everything personal (your regions, node IDs, drafts, session logs, the search index) lives at a path you choose, outside the repo, on your machine.
Three commitments, described properly in docs/METHOD.md and
summarised here so you can judge whether it suits you.
Divide the space before you fill it. Regions come in three kinds: conceptual regions naming the activities of your practice ("how we build", "how we decide"), theme regions for what cuts across them, and life regions for everything that is not the practice at all. Name them as activities rather than subjects, keep them few enough to recite, and write the list down so routing is a decision made once rather than every time.
Admit only claims and actions. A claim states something you could disagree with. An action names an owner and a date. Everything else is raw material: it belongs in an inbox until someone turns it into one of the two, or drops it. If you cannot turn a source into a claim or an action, you have not finished reading it.
One claim, one home. A claim that bears on several regions lives canonically
in one and appears in the others as a mirror pointing back. Copies drift;
audit_mirrors exists to tell you when they have.
Most Workflowy integrations are demos. This one has been hardened in daily production use against a 250,000-node workspace, and every hard-won lesson is encoded in the code and pinned by a test: over 500 of them, including build-time invariant tests that make the design rules unbreakable by future contributors.
Concretely, the problems you would otherwise hit in week two have already been hit, diagnosed, and engineered away:
- Rate limits don't ruin your session. The client fails fast inside a 429 window instead of hanging for four minutes, adapts its request rate when the API pushes back (halve on 429, creep back on success), and never fires a burst into a freshly-reset quota. Bulk writes that stop early always tell you exactly what landed and how to resume.
- Big trees don't time out your questions. A persistent name index turns names, tags, backlinks, and Workflowy URLs into answers in O(1) from a local file, with no tree walk and no API calls. Searches fall back to live, scoped walks only when the index can't answer, and every truncated result says so honestly, with a recovery hint.
- Nothing fails silently. Every walk reports its coverage, every error
carries a typed cause (
rate_limited,timeout,auth, …) with a retry-ability flag, every write is auditable in an operation log, and deletes support a name-echo guard so a coerced ID can't take out the wrong node. - Repeat reads are nearly free. Complete children listings are cached with write-through invalidation, node payloads serialise sparse, and overlapping queries collapse to single API calls.
- The whole tree in one call. The search index rebuilds from Workflowy's
bulk
GET /nodes-exportendpoint: the entire workspace in a single request, seconds not minutes, with no level-by-level walk, no truncation, and no 429 storm.wflow-do reindex --full-exportis the nightly path; the coverage-complete--patientwalk remains for scoped rebuilds.
The same logic serves both surfaces, the MCP server for conversation and the
wflow-do CLI for scripts and cron, with parity enforced at build time so the
two can never drift apart.
WorkFlowy has been building fast, and several things this server was once the
only way to do are now native. As of August 2026 there are five ways to put a
model in front of a WorkFlowy tree: WorkFlowy Pro's own AI inside the client,
the wf CLI, the
MCP embedded in the desktop app,
this server, and a remote connector for anywhere the first four cannot be
installed.
docs/SURFACES.md is the full accounting, kept current
because it decides real routing. The short version:
Use the native tools for reading, searching, and ordinary writing. The wf
CLI answers from a local full-text cache in under 100 ms with each hit's
ancestor path attached, costs no API quota, and now also does nested writes,
todos, tags, change streams and webhooks. The desktop MCP traverses the
already-synced client with filters on regular expressions and date windows,
applies batched nested writes under advisory leases, and draws on no REST quota
at all. Both are better than this server at those jobs. If that is your whole
use case, install the CLI, wire in wf mcp, and stop there.
Use this server for the method, and for where the others cannot go. The
claim-based discipline and the skill that executes it, mirror creation and
drift auditing, the second-brain review, the $SECONDBRAIN_DIR layer, a
scheduled reindex, an index you can withhold private subtrees from, and typed
failures that make an unattended run diagnosable the next morning. None of that
is a tool WorkFlowy is likely to ship, because none of it is a feature.
And use it for anything that runs where WorkFlowy's tools cannot be
installed. A cloud sandbox and a phone have no desktop app and no shell. The
same binary behind an HTTP shim serves both as a custom connector, which is why
that surface has to keep a complete tool set even as the native tools absorb
more of the daily work. See
docs/REMOTE-CONNECTOR.md.
One thing has genuinely arrived and is worth flagging if you are weighing the
method. WorkFlowy's beta API now has real live mirrors, which do what this
repository's mirror_of: convention approximates. They are beta-only today,
and the drift audit answers wider questions than a live mirror makes redundant,
so the convention stays the production-safe path for now. The migration, when
it comes, is described in docs/SURFACES.md.
All of these share one WorkFlowy account and therefore one API rate limit
(the desktop MCP excepted, since it reads the synced client rather than the
API). Running several live clients divides a single budget, which is why this
repo ships an optional per-call usage log (WORKFLOWY_USAGE_LOG_DIR) so the
question of which surface actually carries your work is answered by counting
rather than by preference.
curl -fsSL https://github.com/rodolfo-terriquez/workflowy-cli/releases/latest/download/install.sh | bash
wf login # or: WORKFLOWY_API_KEY=... wf login
wf cache:sync # whole tree into ~/.workflowy/db
wf doctorThe install script verifies a SHA-256 checksum from the release before it moves
anything into place, which is the reason it is safe to pipe. On a
235,000-node workspace the first sync took 16.5 seconds and full-text search
answers in under 100 ms with the ancestor path attached, materially better
than this server's own name index, which stores names and descriptions but not
paths and has no ranking. Subsequent syncs re-export the whole tree, so they hit
WorkFlowy's ~65 s floor on /nodes-export; sync on a schedule, not in a loop.
Wire the same binary in as an MCP server:
claude mcp add workflowy-cli -s user -- "$HOME/.local/bin/wf" mcpor, for Claude Desktop, in claude_desktop_config.json:
"workflowy-cli": { "command": "/Users/you/.local/bin/wf", "args": ["mcp"] }wf mcp --tools read,search,add narrows the exposed set if a full 30-tool
surface is more than a given host needs.
On the Claude Desktop .mcpb extension: it packages
workflowy-local-mcp,
not the CLI, and at the time of writing it lags: v1.2.4 against the CLI's
v3.3.1, 8 tools against 30, a second SQLite cache under
com.workflowy.local-mcp rather than the CLI's ~/.workflowy, and a second
place your API key is stored. If you already have the CLI, wf mcp gives you a
strictly larger tool set off one cache and one credential. Install the
extension only if you want the double-click install and no terminal; do not run
both.
One privacy consequence is worth stating plainly. The CLI's cache and the
desktop MCP both hold your entire tree locally, with no way to withhold a
subtree. This server's on-disk index is the only one that can be told to
exclude subtrees (WORKFLOWY_INDEX_EXCLUDE_SUBTREES), which matters when that
index is replicated somewhere, to a hosted connector say. A local-only cache
and a replicated index deserve different postures; keep the exclusion on
whatever leaves the machine.
You need Rust 1.75+ (rustup install stable), a Workflowy API key
(Workflowy → Settings → API), and an MCP host (Claude Code or Claude
Desktop).
git clone https://github.com/dromologue/workflowyMCP.git ~/code/workflowy-mcp-server
cd ~/code/workflowy-mcp-server
cargo build --release
echo "WORKFLOWY_API_KEY=<your-token>" > .envWire target/release/workflowy-mcp-server into your host:
- Claude Code:
claude mcp add workflowy -- $(pwd)/target/release/workflowy-mcp-server - Claude Desktop: edit
~/Library/Application Support/Claude/claude_desktop_config.json(macOS) or%APPDATA%\Claude\claude_desktop_config.json(Windows). See BOOTSTRAP.md for the JSON shape.
Verify by asking Claude to call workflowy_status; you want
status: "ok", api_reachable: true, authenticated: true. Then try it:
"List the children of my workspace root." "Create a node called Read later under my Inbox." "What did I change in the last two days?"
That's the bare server working. The .env file covers a binary launched from
the repo directory; putting the same key in the host's env block (next
section) works from anywhere and is the recommended form.
Have your regions written down before you start; the install asks for them,
and docs/METHOD.md explains how to arrive at a set worth
keeping. Then hand BOOTSTRAP.md to Claude. It walks the
seven steps: build, wire the host, seed your private $SECONDBRAIN_DIR, cache
your structural node IDs (Inbox, Tasks, Journal…), install the wflow skill,
pre-warm the search index for large trees, and verify the whole chain
end-to-end. After that, every session opens with your workflows available
conversationally: daily and weekly reviews, task capture, inbox triage,
reading-list management, distillation of sources into atomic notes, mirror
discipline with drift auditing, and cross-note research.
The long-form walkthrough (multi-surface setups, large-tree convergence,
troubleshooting) is in docs/SETUP.md. Running behind a
remote connector for claude.ai web/mobile is covered in
docs/REMOTE-CONNECTOR.md.
The server reads five env vars at runtime. The repository ships no
machine-specific defaults: a path or node ID you don't set is a feature you
don't use. Set them in the env block of your MCP host config (Claude Code:
~/.claude.json; Claude Desktop: claude_desktop_config.json) and,
when you also use the wflow-do CLI from a shell, in your shell
profile (~/.zshrc or ~/.bashrc).
| Variable | Required? | What it controls |
|---|---|---|
WORKFLOWY_API_KEY |
Yes | Bearer token for the Workflowy API. |
SECONDBRAIN_DIR |
Optional | Absolute path to your operational secondBrain directory (drafts, session logs, briefs, memory). When set, the review tool's bucket-d session-log scan and the wflow-do index default output path read from $SECONDBRAIN_DIR/session-logs/. Unset or empty disables those features (graceful skip). |
WORKFLOWY_INDEX_PATH |
Optional | Absolute path to the persistent name-index JSON. Conventionally $SECONDBRAIN_DIR/memory/name_index.json. Unset or empty disables persistence, so the index lives only in memory for the lifetime of each process. |
WORKFLOWY_USAGE_LOG_DIR |
Optional | Directory for a durable per-call usage log ({ts, surface, tool, ok, ms, cause} JSONL, one file per day). Lets you measure this server's load, for instance against WorkFlowy's official desktop MCP. Unset disables it. |
WORKFLOWY_REVIEW_ROOT |
Optional | Default root node for the review and audit_mirrors tools when root_id is omitted (your review-anchor / "Distillations" node). No hardcoded fallback: if unset, those two tools require an explicit root_id. |
WORKFLOWY_INDEX_EXCLUDE_SUBTREES |
Optional | Comma-separated full UUIDs and/or 12-char short hashes whose subtrees must never be written to the persistent index file. Walks may still traverse them in memory (a live session still needs answers), but the on-disk index, a durable artefact other tools read, never carries them. Exclusion is transitive (root + all descendants); malformed tokens are dropped with a warning. Set this for any subtree holding material you don't want in a local file. |
Example MCP host env block (Claude Code or Desktop):
"env": {
"WORKFLOWY_API_KEY": "<your token>",
"SECONDBRAIN_DIR": "/absolute/path/to/secondBrain",
"WORKFLOWY_INDEX_PATH": "/absolute/path/to/secondBrain/memory/name_index.json",
"WORKFLOWY_REVIEW_ROOT": "<your review-anchor node id, optional>"
}Example shell profile (so the CLI agrees with the MCP server):
export SECONDBRAIN_DIR="/absolute/path/to/secondBrain"
export WORKFLOWY_INDEX_PATH="$SECONDBRAIN_DIR/memory/name_index.json"Neither path needs to be inside your home directory; a Dropbox / iCloud / Google Drive folder works as long as the host process can read and write it. Set the vars in the host config, not only your shell profile, because the server process inherits its environment from the host's launch, and a var visible only to your interactive shell silently disables the features it drives.
45 tools. node_id accepts a full UUID (with or without hyphens), the
12-char short hash from any Workflowy URL, or the 8-char doc prefix; paste
whatever you have.
Read this table alongside docs/SURFACES.md rather than as
a shopping list. On a machine that can reach WorkFlowy's own tools, most of the
search-and-navigate row and much of the create-and-edit row is better served
natively, at no API cost. The rows that have no native equivalent are mirror
discipline, review, and the diagnostics; those, plus headless and remote
operation, are the reason to run this at all.
| Category | Tools |
|---|---|
| Search & navigate | node_at_path, resolve_link, search_nodes, find_node, get_node, list_children, tag_search, get_subtree, find_backlinks, path_of, find_by_tag_and_path, read_batch |
| Create & edit | create_node, batch_create_nodes, insert_content, smart_insert, convert_markdown, edit_node, move_node, reorder_nodes, delete_node, complete_node, duplicate_node, create_from_template, bulk_update, bulk_tag, transaction, export_subtree |
| Mirror discipline | create_mirror (convention-based mirror_of: linking), audit_mirrors (finds broken and drifted mirrors) |
| Todos & scheduling | list_todos, list_upcoming, list_overdue, daily_review, since |
| Project management | get_project_summary, get_recent_changes |
| Diagnostics & ops | workflowy_status, health_check, cancel_all, build_name_index, review, get_recent_tool_calls |
Highlights worth knowing before you need them:
- Index-first retrieval.
search_nodesandfind_nodetakeprefer_index=true: answer from the local index when it can, fall back to a live scoped walk when it can't, one call either way.tag_searchandfind_backlinkstakeuse_index=truefor zero-API-call sweeps. The index matches names and descriptions, token-AND, any order. - Reads that survive awkward hosts.
read_batchruns many reads in one call with bounded concurrency and per-operation status, the reliable shape on hosts that mangle single-ID parameters. - Writes that can't land in the wrong place. The write tools require an
explicit
parent_id(empty string means workspace root), every scoped response echoesscope_resolvedso you can verify the target, anddelete_nodeaccepts anexpect_nameguard that refuses a delete when the resolved node's name doesn't match. - Batches that resume.
insert_contentreports a committed-count cursor on every failure, so a rate-limited batch resumes exactly where it stopped, with no double-writes.transactionrolls back on failure. - Ordering that matches the app. Listings sort into Workflowy display
order;
insert_contentwrites explicit ascending priorities;reorder_nodesis the deterministic reorder primitive. - Clean text in, rich nodes out. Since the 2026.01 API parses markdown
in a node's name on write (so stored names carry
<b>/<a>/<time>markup), reads render back to clean display text (links keep their URL, dates unwrap to their label) and search matches the visible text, not the tags.create_nodealso takes an explicitlayout(todo/h1/h2/h3/code-block/quote-block) so you can build headers and checklists directly.
Conventions parsed from node text: tags (#project), assignees (@alice),
due dates (due:2026-03-15, #due-2026-03-15, or a bare date).
Every API-touching handler runs inside a uniform wrapper with a kind-appropriate wall-clock budget, cancellation support, and an op-log entry: a call can time out, but it cannot vanish:
| Tool kind | Budget | Examples |
|---|---|---|
| Read | 30 s | get_node, list_children |
| Write | 15 s | create_node, delete_node, edit_node |
| Bulk | 180 s | insert_content, transaction, bulk_update |
| Walk | 20 s (internal) | search_nodes, get_subtree, find_node |
cancel_all interrupts anything in flight within ~50 ms. Every walk-shaped
response carries a four-field truncation envelope (truncated,
truncation_limit, truncation_reason, truncation_recovery_hint) so a
partial answer is never mistaken for a complete one. Every error carries a
typed envelope: proximate_cause, retryable, retry_after_secs, a
hint, so the right recovery is explicit rather than guessed. The full
behavioural contract, including 21 wiremock-driven failure-mode tests and
the build-time invariant suite, lives in
specs/specification.md with a machine-checked
traceability matrix mapping every contract to the
test that pins it.
Everything the MCP server does, as a shell command, with full surface parity enforced at build time. Use it for scheduled jobs, shell pipelines, and as a fallback when you'd rather not open a chat window.
wflow-do status # liveness
wflow-do search --query "concept maps" --use-index # zero-API-call search
wflow-do find "Tasks" --use-index # O(1) name lookup
wflow-do backlinks <uuid> --use-index # who links here?
wflow-do changed-since 2026-07-14 --root <uuid> # local incremental diff
wflow-do complete <uuid> # mark done
wflow-do bulk-update complete --tag urgent # bulk-toggle by filter
wflow-do --dry-run delete <uuid> # preview first
wflow-do reindex --full-export # whole tree in one bulk call
wflow-do reindex --timeout-secs 0 --patient --root <uuid> # coverage-complete scoped buildForty-two subcommands, --json for raw output, --dry-run on write verbs.
The nightly reindex rebuilds the whole index from one bulk /nodes-export
call (--full-export); the --patient walk is the convergence mechanism for
scoped rebuilds, waiting out rate-limit windows instead of dropping
branches. Either way the work is cumulative: every walk any tool performs
extends the same persistent file.
workflowyMCP/
├── docs/METHOD.md ← the method: regions, claims, actions
├── docs/SURFACES.md ← native vs this server: which surface, and why
├── BOOTSTRAP.md ← LLM-facing install script (hand to Claude)
├── README.md ← this file
├── docs/SETUP.md ← long-form setup walkthrough
├── docs/REMOTE-CONNECTOR.md ← claude.ai custom-connector notes
├── specs/ ← behavioural spec, principles, traceability
├── templates/
│ ├── secondbrain/ ← skeleton copied to $SECONDBRAIN_DIR
│ └── skills/wflow/SKILL.md ← the operating manual the assistant follows
├── dist/wflow.skill.zip ← ready-to-upload skill bundle for claude.ai
└── src/ ← Rust MCP server + wflow-do CLI
Everything specific to you (cached node IDs, your regions and routing
rules, drafts, session logs, the name index) lives at $SECONDBRAIN_DIR
and $WORKFLOWY_INDEX_PATH, never in the repo. Clone it and you get a clean
starting point; so does the next person.
| File you'll create | Lives at | What it holds |
|---|---|---|
workflowy_node_links.md |
$SECONDBRAIN_DIR/memory/ |
Cached UUIDs for your structural nodes (Inbox, Tasks, Reading List…) plus the triage-sources table. |
distillation_taxonomy.md |
$SECONDBRAIN_DIR/memory/ |
Your pillars, themes, and routing rules for the synthesis workflows. |
name_index.json |
$WORKFLOWY_INDEX_PATH |
Auto-managed persistent search index. Survives restarts; checkpoints every 30 s; grows with every walk and converges via the scheduled reindex --patient job. |
drafts/, session-logs/, briefs/ |
$SECONDBRAIN_DIR/ |
In-flight work, per-session audit trails, handoff documents. |
cargo build --release # optimised build (server + CLI)
cargo test --lib # 500+ unit tests, no live API calls
cargo test # full suite: lib + portability + traceability + eval coverageThe architecture guide is CLAUDE.md; the law of the project,
eight core principles, a definition of done, and a conflict-resolution
hierarchy, is specs/constitution.md. Every
consistency rule worth stating is pinned by a test that fails the build when
violated. Contributions are held to the same standard, which is precisely
why you can build on this without reading the whole source first.
MIT