Skip to main content
Glama
README.md
# 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

A4.2/5.0

Scored across 5 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues