quill-flowy
# quill-flowy
A lean, MIT-licensed **Model Context Protocol (MCP) server** that lets an AI agent
(Claude Code, Claude Desktop, …) **read** — and, in a separate module, **write** — a
**self-hosted [AppFlowy](https://appflowy.io) instance**.
AppFlowy stores a page's body as a **Yjs CRDT**, not plain text, so the pages are a silo
your agent can't see. quill-flowy authenticates to your instance, walks the workspace/page
tree, and **decodes the CRDT document into Markdown** — the same thing AppFlowy's own web
client does, using the same `yjs` library.
> Companion to [`anythingllm-mcp`](https://github.com/DezGDNY/anythingllm-mcp) — same
> minimal Node + `@modelcontextprotocol/sdk` stack, same safety posture.
## Tools
**Read (Phase 1 — always on, strictly read-only):**
| Tool | What it does |
|---|---|
| `check_auth` | Verify the account can log in and reach the instance |
| `list_workspaces` | List workspaces (name, id, role, members) |
| `list_pages` | The page/view tree of a workspace (titles, ids, document/grid/board type) |
| `get_page` | A document page's body **as Markdown** (the Yjs decoder — headings, lists, to-dos, code, links, page mentions) |
| `search_pages` | Find pages by title, or by body text (`body: true`) |
**Write (Phase 2 — mutating, opt-in):** `create_page`, `append_to_page`, `rename_page`,
`move_page`. These live in a separate module and are **registered only when
`QUILL_FLOWY_WRITE=1`**, so the default install exposes zero write surface to the agent.
All four are **additive or reversible** (none deletes content). `append_to_page` converts
Markdown to AppFlowy blocks (headings, lists, to-dos, code, quotes, formatting).
> `update_page` (replace an existing body in place) is **not supported**: AppFlowy exposes
> no high-level endpoint for editing/deleting arbitrary existing blocks — it would require
> raw Yjs collab over the sync protocol. Use `append_to_page` to add content, or edit in the
> AppFlowy app.
## Requirements
- Node 18+ (developed on Node 24).
- A self-hosted AppFlowy Cloud instance with a **password-based** account.
## Install
```bash
git clone https://github.com/DezGDNY/quill-flowy
cd quill-flowy
npm install
```
Register it with your MCP client, passing config via an `env` block, e.g. Claude Code:
```bash
claude mcp add quill-flowy --scope user -- node /absolute/path/to/quill-flowy/index.js
```
with `APPFLOWY_BASE_URL`, `APPFLOWY_EMAIL`, and `APPFLOWY_PASSWORD` set in the environment
(see `.env.example`).
## Configuration
| Env var | Meaning |
|---|---|
| `APPFLOWY_BASE_URL` | Your instance URL. **Use your self-hosted domain, not `appflowy.com`** (the vendor cloud is a different backend). Default `https://flow.example.com`. |
| `APPFLOWY_EMAIL` | The account to log in as. |
| `APPFLOWY_PASSWORD` | The account password (see secure handling below). |
| `QUILL_FLOWY_WRITE` | `1` to register the mutating write tools. Off by default. |
## Secure credential handling
Don't store a plaintext password in production. On **Windows**, keep it in a
**DPAPI-encrypted** file and use a small launcher that decrypts it into `APPFLOWY_PASSWORD`
**in-process** before exec'ing the server — the password never touches disk in plaintext,
your shell history, or your MCP config. (On macOS/Linux, use the OS keychain or a secret
manager the same way.) quill-flowy itself only ever reads `APPFLOWY_PASSWORD` from its
environment and uses it for a single login; it is never logged.
## How it works
1. **Auth:** `POST /gotrue/token?grant_type=password` → JWT bearer (cached; re-login on
expiry).
2. **Navigate:** `GET /api/workspace…` and `…/folder?depth=N` give the workspace and page
tree directly — no decoding needed.
3. **Read a body:** `GET /api/workspace/v1/{wid}/collab/{vid}?collab_type=0` returns the
Yjs `doc_state`; quill-flowy loads it with `yjs` and walks the AppFlowy document schema
(`data → document → blocks / meta.children_map / meta.text_map`) into Markdown, reading
each text run's delta for inline formatting.
## License
MIT © 2026 Dez
TDQS
Scored across 5 tools
Each tool has a clearly distinct purpose: auth verification, workspace listing, page tree listing, page content retrieval, and page search. The only minor overlap is get_page and search_pages by title, but their outputs differ enough to avoid real confusion.
All tools follow the same snake_case verb_noun pattern: check_auth, list_workspaces, list_pages, get_page, search_pages. The naming is predictable and makes the toolset easy to navigate.
Five tools is a well-scoped size for a read-only AppFlowy exploration server. Each tool covers a distinct need without redundancy or unnecessary bulk.
The read-only workflow is well covered: authenticate, list workspaces, browse the page tree, retrieve page bodies, and search content. The main gap is that list_pages mentions grid/board view types, but get_page only handles document page bodies, leaving non-document views without a retrieval path.