affine-mcp-server 1.11.2 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # AFFiNE MCP Server
2
2
 
3
- A Model Context Protocol (MCP) server that integrates with AFFiNE (self‑hosted or cloud). It exposes AFFiNE workspaces and documents to AI assistants over stdio (default) or HTTP (`/mcp`).
3
+ A Model Context Protocol (MCP) server for AFFiNE. It exposes AFFiNE workspaces and documents to AI assistants over stdio (default) or HTTP (`/mcp`) and supports both AFFiNE Cloud and self-hosted deployments.
4
4
 
5
- [![Version](https://img.shields.io/badge/version-1.11.2-blue)](https://github.com/dawncr0w/affine-mcp-server/releases)
5
+ [![Version](https://img.shields.io/badge/version-1.13.0-blue)](https://github.com/dawncr0w/affine-mcp-server/releases)
6
6
  [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.17.2-green)](https://github.com/modelcontextprotocol/typescript-sdk)
7
7
  [![CI](https://github.com/dawncr0w/affine-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/dawncr0w/affine-mcp-server/actions/workflows/ci.yml)
8
8
  [![License](https://img.shields.io/badge/license-MIT-yellow)](LICENSE)
@@ -11,568 +11,256 @@ A Model Context Protocol (MCP) server that integrates with AFFiNE (self‑hosted
11
11
  <img width="380" height="200" src="https://glama.ai/mcp/servers/@DAWNCR0W/affine-mcp-server/badge" alt="AFFiNE Server MCP server" />
12
12
  </a>
13
13
 
14
- ## Overview
15
-
16
- - Purpose: Manage AFFiNE workspaces and documents through MCP
17
- - Transport: stdio (default) and optional HTTP (`/mcp`) for remote MCP deployments
18
- - Auth: Token, Cookie, or Email/Password (priority order)
19
- - Tools: 76 focused tools with WebSocket-based document editing
20
- - Status: Active
21
-
22
- > New in v1.11.2: Corrected stale deleted-document visibility in `list_docs` after `delete_doc`, completing the `v1.11.1` delete-metadata fix.
14
+ ## Table of Contents
15
+
16
+ - [Overview](#overview)
17
+ - [Choose Your Path](#choose-your-path)
18
+ - [Quick Start](#quick-start)
19
+ - [Compatibility Matrix](#compatibility-matrix)
20
+ - [Tool Surface](#tool-surface)
21
+ - [Documentation Map](#documentation-map)
22
+ - [Verify Your Setup](#verify-your-setup)
23
+ - [Security and Scope](#security-and-scope)
24
+ - [Development](#development)
25
+ - [Release Notes](#release-notes)
26
+ - [License](#license)
27
+ - [Support](#support)
23
28
 
24
- ## Features
29
+ ## Overview
25
30
 
26
- - Workspace: create (with initial doc), read, update, delete
27
- - Documents: list/get/read/publish/revoke + create/append/replace/delete + markdown import/export + tags (WebSocket‑based)
28
- - Sidebar data: collections, folders, and organize links for AFFiNE workspace trees
29
- - Database workflows: create database blocks, inspect schema, add/update/delete rows, and read or update cell values via MCP tools
30
- - Comments: full CRUD and resolve
31
- - Version History: list
32
- - Users & Tokens: current user, sign in, profile/settings, and personal access tokens
33
- - Notifications: list and mark as read
34
- - Blob storage: upload/delete/cleanup
31
+ AFFiNE MCP Server is designed for three common scenarios:
35
32
 
36
- ## Requirements
33
+ - Run a local stdio MCP server for Claude Code, Codex CLI, Cursor, or Claude Desktop
34
+ - Expose a remote HTTP MCP endpoint for hosted or browser-connected clients
35
+ - Automate AFFiNE workspace, document, database, organization, and comment workflows through a stable MCP tool surface
37
36
 
38
- - Node.js 18+
39
- - An AFFiNE instance (self‑hosted or cloud)
40
- - Valid AFFiNE credentials or access token
37
+ Highlights:
41
38
 
42
- ## Installation
39
+ - Supports AFFiNE Cloud and self-hosted AFFiNE instances
40
+ - Supports stdio and HTTP transports
41
+ - Supports token, cookie, and email/password authentication
42
+ - Exposes 87 canonical MCP tools backed by AFFiNE GraphQL and WebSocket APIs
43
+ - Includes semantic page composition, native template instantiation, database intent composition, capability and fidelity reporting, and workspace blueprint helpers
44
+ - Includes Docker images, health probes, and end-to-end test coverage
43
45
 
44
- ```bash
45
- # Global install (recommended)
46
- npm i -g affine-mcp-server
46
+ Scope boundaries:
47
47
 
48
- # Or run ad‑hoc via npx (no install)
49
- npx -y -p affine-mcp-server affine-mcp -- --version
50
- ```
48
+ - This server can access only server-backed AFFiNE workspaces
49
+ - Browser-local workspaces stored only in local storage are not available through AFFiNE server APIs
50
+ - AFFiNE Cloud requires API-token-based access for MCP usage; programmatic email/password sign-in is blocked by Cloudflare
51
51
 
52
- The package installs a CLI named `affine-mcp` that runs the MCP server over stdio.
52
+ > New in v1.13.0: Added high-level semantic page, native template, fidelity, and workspace blueprint workflows, plus structured receipts and productized setup docs.
53
53
 
54
- Note: From v1.2.2+ the CLI wrapper (`bin/affine-mcp`) ensures Node runs the ESM entrypoint, preventing shell from misinterpreting JS.
54
+ ## Choose Your Path
55
55
 
56
- ## Configuration
56
+ | Goal | Start here |
57
+ | --- | --- |
58
+ | Set up a local stdio server with the least friction | [docs/getting-started.md](docs/getting-started.md) |
59
+ | Run the server in Docker or another OCI runtime | [docs/getting-started.md#path-c-run-from-the-docker-image](docs/getting-started.md#path-c-run-from-the-docker-image) |
60
+ | Configure Claude Code, Claude Desktop, Codex CLI, or Cursor | [docs/client-setup.md](docs/client-setup.md) |
61
+ | Run the server remotely over HTTP or behind OAuth | [docs/configuration-and-deployment.md](docs/configuration-and-deployment.md) |
62
+ | Lock down tool exposure for least-privilege deployments | [docs/configuration-and-deployment.md#least-privilege-tool-exposure](docs/configuration-and-deployment.md#least-privilege-tool-exposure) |
63
+ | Learn common AFFiNE workflows and tool sequences | [docs/workflow-recipes.md](docs/workflow-recipes.md) |
64
+ | Browse the tool catalog by domain | [docs/tool-reference.md](docs/tool-reference.md) |
57
65
 
58
- ### Interactive login (recommended)
66
+ ## Quick Start
59
67
 
60
- The easiest way to configure credentials:
68
+ ### 1. Install the CLI
61
69
 
62
70
  ```bash
63
71
  npm i -g affine-mcp-server
64
- affine-mcp login
72
+ affine-mcp --version
65
73
  ```
66
74
 
67
- This stores credentials in `~/.config/affine-mcp/config` (mode 600). The MCP server reads them automatically — no environment variables needed.
68
-
69
- **AFFiNE Cloud** (`app.affine.pro`): you'll be prompted to paste an API token from Settings → Integrations → MCP Server.
70
-
71
- **Self-hosted instances**: you can choose between email/password (recommended — auto-generates an API token) or pasting a token manually.
75
+ You can also run the package ad hoc:
72
76
 
77
+ ```bash
78
+ npx -y -p affine-mcp-server affine-mcp -- --version
73
79
  ```
74
- $ affine-mcp login
75
- Affine MCP Server — Login
76
-
77
- Affine URL [https://app.affine.pro]: https://my-affine.example.com
78
-
79
- Auth method — [1] Email/password (recommended) [2] Paste API token: 1
80
- Email: user@example.com
81
- Password: ****
82
- Signing in...
83
- ✓ Signed in as: User Name <user@example.com>
84
80
 
85
- Generating API token...
86
- ✓ Created token: ut_abc123... (name: affine-mcp-2026-02-18)
81
+ ### 2. Or run the server in Docker
87
82
 
88
- Detecting workspaces...
89
- Found 1 workspace: abc-def-123 (by User Name, 1 member, 2/10/2026)
90
- Auto-selected.
91
-
92
- Saved to /home/user/.config/affine-mcp/config (mode 600)
93
- The MCP server will use these credentials automatically.
83
+ ```bash
84
+ docker run -d \
85
+ -p 3000:3000 \
86
+ -e MCP_TRANSPORT=http \
87
+ -e AFFINE_BASE_URL=https://your-affine-instance.com \
88
+ -e AFFINE_API_TOKEN=ut_your_token \
89
+ -e AFFINE_MCP_AUTH_MODE=bearer \
90
+ -e AFFINE_MCP_HTTP_TOKEN=your-strong-secret \
91
+ ghcr.io/dawncr0w/affine-mcp-server:latest
94
92
  ```
95
93
 
96
- Other CLI commands:
97
- - `affine-mcp --help` / `-h` / `help` — show command help
98
- - `affine-mcp status` — show current config and test connection
99
- - `affine-mcp status --json` — machine-readable status output
100
- - `affine-mcp doctor` — run config and connectivity diagnostics
101
- - `affine-mcp show-config` — print the effective config with secrets redacted
102
- - `affine-mcp config-path` — print the config file path
103
- - `affine-mcp snippet <claude|cursor|codex|all> [--env]` — print ready-to-paste client configuration snippets
104
- - `affine-mcp logout` — remove stored credentials
105
- - `affine-mcp --version` / `-v` / `version` — print the installed CLI version and exit
106
-
107
- Non-interactive login helpers:
108
- - `affine-mcp login --url <url> --token <token> --workspace-id <id> --force`
109
-
110
- ### Environment variables
111
-
112
- You can also configure via environment variables (they override the config file):
113
-
114
- - Required: `AFFINE_BASE_URL`
115
- - Auth (choose one): `AFFINE_API_TOKEN` | `AFFINE_COOKIE` | `AFFINE_EMAIL` + `AFFINE_PASSWORD`
116
- - Optional: `AFFINE_GRAPHQL_PATH` (default `/graphql`), `AFFINE_WORKSPACE_ID`, `AFFINE_LOGIN_AT_START` (set `sync` only when you must block startup)
117
- - Tool filtering: `AFFINE_DISABLED_GROUPS`, `AFFINE_DISABLED_TOOLS` (see [Filtering Exposed Tools](#filtering-exposed-tools))
118
-
119
- Authentication priority:
120
- 1) `AFFINE_API_TOKEN` → 2) `AFFINE_COOKIE` → 3) `AFFINE_EMAIL` + `AFFINE_PASSWORD`
121
-
122
- > **Cloudflare note**: `AFFINE_EMAIL`/`AFFINE_PASSWORD` auth requires programmatic access to `/api/auth/sign-in`. AFFiNE Cloud (`app.affine.pro`) is behind Cloudflare, which blocks these requests. Use `AFFINE_API_TOKEN` for cloud, or use `affine-mcp login` which handles this automatically. Email/password works for self-hosted instances without Cloudflare.
123
-
124
- ## Quick Start
125
-
126
- ### Claude Code
127
-
128
- After running `affine-mcp login`, add to your project's `.mcp.json`:
94
+ Then point your client at:
129
95
 
130
96
  ```json
131
97
  {
132
98
  "mcpServers": {
133
99
  "affine": {
134
- "command": "affine-mcp"
100
+ "type": "http",
101
+ "url": "http://localhost:3000/mcp",
102
+ "headers": {
103
+ "Authorization": "Bearer your-strong-secret"
104
+ }
135
105
  }
136
106
  }
137
107
  }
138
108
  ```
139
109
 
140
- No `env` block needed the server reads `~/.config/affine-mcp/config` automatically.
110
+ For Docker, health checks, and remote deployment details, see [docs/configuration-and-deployment.md#docker](docs/configuration-and-deployment.md#docker).
141
111
 
142
- If you prefer explicit env vars instead of the config file:
112
+ ### 3. Save credentials with interactive login
143
113
 
144
- ```json
145
- {
146
- "mcpServers": {
147
- "affine": {
148
- "command": "affine-mcp",
149
- "env": {
150
- "AFFINE_BASE_URL": "https://app.affine.pro",
151
- "AFFINE_API_TOKEN": "ut_xxx"
152
- }
153
- }
154
- }
155
- }
114
+ ```bash
115
+ affine-mcp login
156
116
  ```
157
117
 
158
- ### Claude Desktop
118
+ This stores credentials in `~/.config/affine-mcp/config` with mode `600`.
119
+
120
+ - For AFFiNE Cloud, use an API token from `Settings -> Integrations -> MCP Server`
121
+ - For self-hosted AFFiNE, you can use either an API token or email/password
159
122
 
160
- Add to your Claude Desktop configuration:
123
+ ### 4. Register the server with your client
161
124
 
162
- - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
163
- - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
164
- - Linux: `~/.config/Claude/claude_desktop_config.json`
125
+ Claude Code project config:
165
126
 
166
127
  ```json
167
128
  {
168
129
  "mcpServers": {
169
130
  "affine": {
170
- "command": "affine-mcp",
171
- "env": {
172
- "AFFINE_BASE_URL": "https://app.affine.pro",
173
- "AFFINE_API_TOKEN": "ut_xxx"
174
- }
131
+ "command": "affine-mcp"
175
132
  }
176
133
  }
177
134
  }
178
135
  ```
179
136
 
180
- Or with email/password for self-hosted instances (not supported on AFFiNE Cloud — see Cloudflare note above):
137
+ Codex CLI:
181
138
 
182
- ```json
183
- {
184
- "mcpServers": {
185
- "affine": {
186
- "command": "affine-mcp",
187
- "env": {
188
- "AFFINE_BASE_URL": "https://your-self-hosted-affine.com",
189
- "AFFINE_EMAIL": "you@example.com",
190
- "AFFINE_PASSWORD": "secret!"
191
- }
192
- }
193
- }
194
- }
139
+ ```bash
140
+ codex mcp add affine -- affine-mcp
195
141
  ```
196
142
 
197
- Tips
198
- - Prefer `affine-mcp login` or `AFFINE_API_TOKEN` for zero‑latency startup.
199
- - If your password contains `!` (zsh history expansion), wrap it in single quotes in shells or use the JSON config above.
200
- - `affine-mcp doctor` is the fastest way to confirm that your saved config still works.
201
- - `affine-mcp snippet claude --env` and `affine-mcp snippet codex --env` can generate ready-to-paste client setup from your current config.
202
- - `affine-mcp snippet all --env` prints Claude, Cursor, and Codex setup in one shot.
143
+ More client-specific setup is in [docs/client-setup.md](docs/client-setup.md).
203
144
 
204
- ### Codex CLI
145
+ ### 5. Verify the connection
205
146
 
206
- Register the MCP server with Codex:
147
+ ```bash
148
+ affine-mcp status
149
+ affine-mcp doctor
150
+ ```
207
151
 
208
- - With config file (after `affine-mcp login`):
209
- - `codex mcp add affine -- affine-mcp`
152
+ If you want to expose the server remotely over HTTP instead of stdio, start with [docs/configuration-and-deployment.md](docs/configuration-and-deployment.md).
210
153
 
211
- - With API token:
212
- - `codex mcp add affine --env AFFINE_BASE_URL=https://app.affine.pro --env AFFINE_API_TOKEN=ut_xxx -- affine-mcp`
154
+ ## Compatibility Matrix
213
155
 
214
- - With email/password (self-hosted only):
215
- - `codex mcp add affine --env AFFINE_BASE_URL=https://your-self-hosted-affine.com --env 'AFFINE_EMAIL=you@example.com' --env 'AFFINE_PASSWORD=secret!' -- affine-mcp`
156
+ | Target | Transport | Recommended auth | Recommended path |
157
+ | --- | --- | --- | --- |
158
+ | Claude Code | stdio | Saved config or API token | [docs/client-setup.md#claude-code](docs/client-setup.md#claude-code) |
159
+ | Claude Desktop | stdio | Saved config or API token | [docs/client-setup.md#claude-desktop](docs/client-setup.md#claude-desktop) |
160
+ | Codex CLI | stdio | Saved config or API token | [docs/client-setup.md#codex-cli](docs/client-setup.md#codex-cli) |
161
+ | Cursor | stdio | Saved config or API token | [docs/client-setup.md#cursor](docs/client-setup.md#cursor) |
162
+ | Containerized remote deployment | HTTP | Bearer token or OAuth | [docs/getting-started.md#path-c-run-from-the-docker-image](docs/getting-started.md#path-c-run-from-the-docker-image) |
163
+ | Remote MCP clients | HTTP | Bearer token or OAuth | [docs/configuration-and-deployment.md#http-mode](docs/configuration-and-deployment.md#http-mode) |
164
+ | AFFiNE Cloud | stdio or HTTP | API token | [docs/configuration-and-deployment.md#auth-strategy-matrix](docs/configuration-and-deployment.md#auth-strategy-matrix) |
165
+ | Self-hosted AFFiNE | stdio or HTTP | API token, cookie, or email/password | [docs/configuration-and-deployment.md#auth-strategy-matrix](docs/configuration-and-deployment.md#auth-strategy-matrix) |
216
166
 
217
- ### Cursor
167
+ ## Tool Surface
218
168
 
219
- Cursor also supports MCP over stdio with `mcp.json`.
169
+ `tool-manifest.json` is the source of truth for canonical tool names. The MCP server exposes those tools through `tools/list` and `tools/call`.
220
170
 
221
- Project-local (`.cursor/mcp.json`) example:
171
+ Domains:
222
172
 
223
- ```json
224
- {
225
- "mcpServers": {
226
- "affine": {
227
- "command": "affine-mcp",
228
- "env": {
229
- "AFFINE_BASE_URL": "https://app.affine.pro",
230
- "AFFINE_API_TOKEN": "ut_xxx"
231
- }
232
- }
233
- }
234
- }
235
- ```
236
-
237
- If you prefer `npx`:
238
-
239
- ```json
240
- {
241
- "mcpServers": {
242
- "affine": {
243
- "command": "npx",
244
- "args": ["-y", "-p", "affine-mcp-server", "affine-mcp"],
245
- "env": {
246
- "AFFINE_BASE_URL": "https://app.affine.pro",
247
- "AFFINE_API_TOKEN": "ut_xxx"
248
- }
249
- }
250
- }
251
- }
252
- ```
253
-
254
- ### Remote Server
255
-
256
- If you want to host the server remotely (e.g., using Render, Railway, Docker, or a VPS) and connect via HTTP MCP (Streamable HTTP on `/mcp`) instead of local `stdio`, run the server in HTTP mode.
173
+ - Workspace: create, inspect, update, delete, and traverse workspaces
174
+ - Organization: collections, collection-rule sync, workspace blueprints, and experimental organize or folder helpers
175
+ - Documents: search, read, create, publish, move, tag, import/export, semantic composition, template inspection and native instantiation, capability and fidelity reporting, and text mutation
176
+ - Databases: create columns, add rows, update cells, inspect schema, and compose database structures from intent
177
+ - Comments: list, create, update, delete, resolve, and list unresolved threads
178
+ - History: version history listing
179
+ - Users and tokens: current user, sign-in, profile/settings, personal access tokens
180
+ - Notifications: list and mark notifications as read
181
+ - Blob storage: upload, delete, and cleanup blobs
257
182
 
258
- #### Environment variables (HTTP mode)
183
+ For the grouped catalog, notes, and operational caveats, see [docs/tool-reference.md](docs/tool-reference.md).
259
184
 
260
- Required:
261
- - `MCP_TRANSPORT=http`
262
- - `AFFINE_BASE_URL` (example: `https://app.affine.pro`)
263
- - `AFFINE_MCP_AUTH_MODE=bearer` (default) or `AFFINE_MCP_AUTH_MODE=oauth`
185
+ ## Documentation Map
264
186
 
265
- Bearer mode backend auth:
266
- - `AFFINE_API_TOKEN` (recommended), or `AFFINE_COOKIE`, or `AFFINE_EMAIL` + `AFFINE_PASSWORD`
187
+ | Document | Purpose |
188
+ | --- | --- |
189
+ | [docs/getting-started.md](docs/getting-started.md) | First-run setup paths and verification |
190
+ | [docs/client-setup.md](docs/client-setup.md) | Client-specific configuration snippets and tips |
191
+ | [docs/configuration-and-deployment.md](docs/configuration-and-deployment.md) | Environment variables, auth modes, Docker, HTTP mode, and deployment guidance |
192
+ | [docs/workflow-recipes.md](docs/workflow-recipes.md) | End-to-end workflows and example tool sequences |
193
+ | [docs/tool-reference.md](docs/tool-reference.md) | Tool catalog grouped by domain |
194
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Contributor workflow |
195
+ | [SECURITY.md](SECURITY.md) | Security reporting |
267
196
 
268
- OAuth mode backend auth:
269
- - `AFFINE_API_TOKEN` (required service credential for AFFiNE backend access)
197
+ ## Verify Your Setup
270
198
 
271
- Recommended for remote/public deployments:
272
- - `AFFINE_MCP_HTTP_HOST=0.0.0.0`
273
- - `AFFINE_MCP_HTTP_ALLOWED_ORIGINS=<comma-separated-origins>` (for browser clients)
199
+ Useful CLI commands:
274
200
 
275
- Optional:
276
- - `PORT` (defaults to `3000`; many platforms like Render inject this automatically)
277
- - `AFFINE_WORKSPACE_ID`
278
- - `AFFINE_GRAPHQL_PATH` (defaults to `/graphql`)
279
- - `AFFINE_MCP_HTTP_ALLOW_ALL_ORIGINS=true` (testing only)
201
+ - `affine-mcp status` - test the effective configuration
202
+ - `affine-mcp status --json` - machine-readable status output
203
+ - `affine-mcp doctor` - diagnose config and connectivity issues
204
+ - `affine-mcp show-config` - print the effective config with secrets redacted
205
+ - `affine-mcp config-path` - print the config file path
206
+ - `affine-mcp snippet <claude|cursor|codex|all> [--env]` - generate ready-to-paste client config
207
+ - `affine-mcp logout` - remove stored credentials
280
208
 
281
- Bearer-mode only:
282
- - `AFFINE_MCP_HTTP_TOKEN=<strong-random-token>` (protects `/mcp`, `/sse`, `/messages`)
209
+ For common failures, see:
283
210
 
284
- OAuth-mode only:
285
- - `AFFINE_MCP_PUBLIC_BASE_URL=https://mcp.yourdomain.com`
286
- - `AFFINE_OAUTH_ISSUER_URL=https://auth.yourdomain.com`
287
- - `AFFINE_OAUTH_SCOPES=mcp` (defaults to `mcp`)
211
+ - [docs/getting-started.md#common-first-run-failures](docs/getting-started.md#common-first-run-failures)
212
+ - [docs/configuration-and-deployment.md#deployment-checklist](docs/configuration-and-deployment.md#deployment-checklist)
288
213
 
289
- #### HTTP auth modes
214
+ ## Security and Scope
290
215
 
291
- `AFFINE_MCP_AUTH_MODE=bearer` keeps the current static bearer-token behavior.
216
+ - Never commit secrets or long-lived tokens
217
+ - Prefer API tokens over cookies or passwords in production
218
+ - Use HTTPS for non-local deployments
219
+ - Rotate access tokens regularly
220
+ - Restrict exposed tools with `AFFINE_DISABLED_GROUPS` and `AFFINE_DISABLED_TOOLS` for least-privilege setups
221
+ - Use `/healthz` and `/readyz` when running the HTTP server behind a container platform or load balancer
292
222
 
293
- ```bash
294
- export MCP_TRANSPORT=http
295
- export AFFINE_MCP_AUTH_MODE=bearer
296
- export AFFINE_API_TOKEN="your_token..."
297
- export AFFINE_MCP_HTTP_HOST="0.0.0.0"
298
- export AFFINE_MCP_HTTP_TOKEN="your-super-secret-token"
299
- export PORT=3000
300
-
301
- npm run start:http
302
- ```
223
+ ## Development
303
224
 
304
- `AFFINE_MCP_AUTH_MODE=oauth` turns the MCP endpoint into an OAuth-protected resource for web MCP clients. In this mode:
305
- - the server exposes `/.well-known/oauth-protected-resource`
306
- - unauthenticated `/mcp` requests return `401` with a `WWW-Authenticate` challenge
307
- - `AFFINE_MCP_HTTP_TOKEN` and `?token=` are disabled
308
- - `sign_in` is not registered
309
- - `AFFINE_API_TOKEN` is still required so the server can call AFFiNE as a service credential
225
+ Run the main quality gates before opening a PR:
310
226
 
311
227
  ```bash
312
- export MCP_TRANSPORT=http
313
- export AFFINE_MCP_AUTH_MODE=oauth
314
- export AFFINE_API_TOKEN="your-affine-service-token"
315
- export AFFINE_MCP_HTTP_HOST="0.0.0.0"
316
- export AFFINE_MCP_PUBLIC_BASE_URL="https://mcp.yourdomain.com"
317
- export AFFINE_OAUTH_ISSUER_URL="https://auth.yourdomain.com"
318
- export AFFINE_OAUTH_SCOPES="mcp"
319
- export PORT=3000
320
-
321
- npm run start:http
322
- ```
323
-
324
- Notes for OAuth mode:
325
- - use HTTPS for non-local deployments
326
- - `AFFINE_MCP_HTTP_ALLOW_ALL_ORIGINS=true` is rejected in OAuth mode
327
- - tokens are validated against the issuer discovery metadata and JWKS
328
- - the protected resource metadata is also served at `/.well-known/oauth-protected-resource/mcp` for path-specific discovery
329
- - `GET /healthz` and `GET /readyz` are available for deployment diagnostics
330
-
331
- #### Recommended presets
332
-
333
- Local testing (HTTP mode):
334
- - `MCP_TRANSPORT=http`
335
- - `AFFINE_MCP_AUTH_MODE=bearer`
336
- - `AFFINE_MCP_HTTP_HOST=127.0.0.1`
337
- - `AFFINE_MCP_HTTP_TOKEN=<token>` (recommended even locally)
338
- - `AFFINE_MCP_HTTP_ALLOWED_ORIGINS=http://localhost:3000` (if testing from a browser app)
339
-
340
- Docker / container runtime:
341
- - `MCP_TRANSPORT=http`
342
- - `AFFINE_MCP_AUTH_MODE=bearer`
343
- - `AFFINE_MCP_HTTP_HOST=0.0.0.0`
344
- - `PORT=3000` (or container/platform port)
345
- - `AFFINE_MCP_HTTP_TOKEN=<strong-token>`
346
- - `AFFINE_MCP_HTTP_ALLOWED_ORIGINS=<your app origin(s)>`
347
-
348
- Render / Railway / VPS (public endpoint):
349
- - `MCP_TRANSPORT=http`
350
- - `AFFINE_MCP_AUTH_MODE=bearer` or `oauth`
351
- - `AFFINE_MCP_HTTP_HOST=0.0.0.0`
352
- - `AFFINE_MCP_HTTP_TOKEN=<strong-token>` (bearer mode)
353
- - `AFFINE_MCP_PUBLIC_BASE_URL=<public base URL>` (OAuth mode)
354
- - `AFFINE_OAUTH_ISSUER_URL=<issuer URL>` (OAuth mode)
355
- - `AFFINE_MCP_HTTP_ALLOWED_ORIGINS=<your client origin(s)>`
356
-
357
- Endpoints currently available:
358
- - `/mcp` - MCP server (Streamable HTTP)
359
- - `/sse` - SSE endpoint (old protocol compatible)
360
- - `/messages` - Messages endpoint (old protocol compatible)
361
- - `/healthz` - HTTP liveness probe
362
- - `/readyz` - HTTP readiness probe
363
-
364
-
365
- ## Available Tools
366
-
367
- ### Workspace
368
- - `list_workspaces` – list all workspaces
369
- - `get_workspace` – get workspace details
370
- - `create_workspace` – create workspace with initial document
371
- - `update_workspace` – update workspace settings
372
- - `delete_workspace` – delete workspace permanently
373
- - `list_workspace_tree` – return the workspace document hierarchy as a tree
374
- - `get_orphan_docs` – find documents that are not linked from any parent doc in the sidebar tree
375
-
376
- ### Organization
377
- - `list_collections` – list workspace collections
378
- - `get_collection` – get a collection by id
379
- - `create_collection` – create a collection
380
- - `update_collection` – rename a collection
381
- - `delete_collection` – delete a collection
382
- - `add_doc_to_collection` – add a document to a collection allow-list
383
- - `remove_doc_from_collection` – remove a document from a collection allow-list
384
- - `list_organize_nodes` – experimental organize/folder tree dump
385
- - `create_folder` – experimental root or nested folder creation
386
- - `rename_folder` – experimental folder rename
387
- - `delete_folder` – experimental recursive folder delete
388
- - `move_organize_node` – experimental folder/link move
389
- - `add_organize_link` – experimental doc/tag/collection link under a folder
390
- - `delete_organize_link` – experimental doc/tag/collection link delete
391
-
392
-
393
- ### Documents
394
- - `list_docs` – list documents with pagination (includes `node.tags`)
395
- - `list_tags` – list all tags in a workspace
396
- - `search_docs` – fast title search with substring/prefix/exact matching, optional tag filtering, and updatedAt sorting
397
- - `list_docs_by_tag` – list documents that contain the requested tag
398
- - `get_docs_by_tag` – discover documents by case-insensitive tag substring and return `availableTags` when nothing matches
399
- - `get_doc` – get document metadata
400
- - `get_doc_by_title` – find a document by title and return its Markdown content
401
- - `read_doc` – read document block content and plain text snapshot (WebSocket)
402
- - `export_doc_markdown` – export document content as markdown
403
- - `publish_doc` – make document public
404
- - `revoke_doc` – revoke public access
405
- - `create_doc` – create a new document (WebSocket)
406
- - `create_doc_from_markdown` – create a document from markdown content
407
- - `create_doc_from_template` – clone a template doc, substitute `{{variables}}`, and optionally link it under a parent doc
408
- - `duplicate_doc` – clone a document into a new doc, optionally under a parent doc
409
- - `create_tag` – create a reusable workspace-level tag
410
- - `add_tag_to_doc` – attach a tag to a document
411
- - `remove_tag_from_doc` – detach a tag from a document
412
- - `update_doc_title` – rename a document in both workspace metadata and the internal page block
413
- - `append_paragraph` – append a paragraph block (WebSocket)
414
- - `append_block` – append canonical block types (text/list/code/media/embed/database/edgeless) with strict validation and placement control (`viewMode=kanban` enables preset-backed data views; `data_view` defaults to kanban)
415
- - `move_doc` – move a document in the sidebar by relinking it under a different parent
416
- - `batch_create_docs` – create up to 20 documents in a single call
417
- - `add_database_column` – add a column to a database block (`rich-text`, `select`, `multi-select`, `number`, `checkbox`, `link`, `date`)
418
- - `add_database_row` – add a row to a database block with values mapped by column name/ID (`title` / `Title` updates the built-in row title)
419
- - `delete_database_row` – delete a row from a database block by row block id
420
- - `read_database_columns` – read database schema metadata including column IDs/types, select options, and table view column mappings
421
- - `read_database_cells` – read row titles plus decoded database cell values with optional row / column filters
422
- - `update_database_cell` – update a single database cell or the built-in row title (`createOption` defaults to `true` for select fields)
423
- - `update_database_row` – batch update multiple cells on a database row (`createOption` defaults to `true` for select fields)
424
- - `append_markdown` – append markdown content to an existing document
425
- - `replace_doc_with_markdown` – replace the main note content with markdown content
426
- - `list_children` – list the direct child docs linked from a document
427
- - `list_backlinks` – list the parent/reference docs that link to a document
428
- - `cleanup_orphan_embeds` – remove linked-doc embeds that point to missing docs
429
- - `find_and_replace` – preview or apply text replacement across a document
430
- - `delete_doc` – delete a document (WebSocket)
431
-
432
- ### Comments
433
- - `list_comments`, `create_comment`, `update_comment`, `delete_comment`, `resolve_comment`
434
-
435
- ### Version History
436
- - `list_histories`
437
-
438
- ### Users & Tokens
439
- - `current_user`, `sign_in`, `update_profile`, `update_settings`
440
- - `list_access_tokens`, `generate_access_token`, `revoke_access_token`
441
-
442
- ### Notifications
443
- - `list_notifications`, `read_all_notifications`
444
-
445
- ### Blob Storage
446
- - `upload_blob`, `delete_blob`, `cleanup_blobs`
447
-
448
- ## Filtering Exposed Tools
449
-
450
- Optional environment variables to narrow the exposed surface.
451
-
452
- ### Group-level — `AFFINE_DISABLED_GROUPS`
453
-
454
- | Group name | Tools included |
455
- |---|---|
456
- | `workspaces` | `list_workspaces`, `get_workspace`, `create_workspace`, `update_workspace`, `delete_workspace` |
457
- | `docs` | `list_docs`, `read_doc`, `search_docs`, `create_doc`, `create_doc_from_markdown`, `create_doc_from_template`, `duplicate_doc`, `append_paragraph`, `append_block`, `append_markdown`, `replace_doc_with_markdown`, `delete_doc`, `publish_doc`, `revoke_doc`, `list_tags`, `list_docs_by_tag`, `create_tag`, `add_tag_to_doc`, `remove_tag_from_doc`, `list_workspace_tree`, `get_orphan_docs`, `list_children`, `update_doc_title`, `get_doc_by_title`, `get_docs_by_tag`, `list_backlinks`, `move_doc`, `batch_create_docs`, `cleanup_orphan_embeds`, `find_and_replace`, `add_database_column`, `add_database_row`, `delete_database_row`, `read_database_columns`, `read_database_cells`, `update_database_cell`, `update_database_row` |
458
- | `comments` | `list_comments`, `create_comment`, `update_comment`, `delete_comment`, `resolve_comment` |
459
- | `history` | `list_histories` |
460
- | `organize` | `list_collections`, `get_collection`, `create_collection`, `update_collection`, `delete_collection`, `add_doc_to_collection`, `remove_doc_from_collection`, `list_organize_nodes`, `create_folder`, `rename_folder`, `delete_folder`, `move_organize_node`, `add_organize_link`, `delete_organize_link` |
461
- | `users` | `current_user`, `sign_in`, `update_profile`, `update_settings` |
462
- | `access_tokens` | `list_access_tokens`, `generate_access_token`, `revoke_access_token` |
463
- | `blobs` | `upload_blob`, `delete_blob`, `cleanup_blobs` |
464
- | `notifications` | `list_notifications`, `read_all_notifications` |
465
-
466
- ```json
467
- "env": {
468
- "AFFINE_DISABLED_GROUPS": "comments,history,blobs,users"
469
- }
228
+ npm run build
229
+ npm run test:tool-manifest
230
+ npm run pack:check
470
231
  ```
471
232
 
472
- ### Tool-level — `AFFINE_DISABLED_TOOLS`
233
+ Additional validation:
473
234
 
474
- Disables individual tools by exact name (comma-separated).
235
+ - `npm run test:comprehensive` boots a local Docker AFFiNE stack and validates the tool surface
236
+ - `npm run test:e2e` runs Docker, MCP, and Playwright together
237
+ - `npm run test:playwright` runs the Playwright suite only
238
+ - Focused runners for the new high-level tool surface include `npm run test:create-placement`, `npm run test:capabilities-fidelity`, `npm run test:native-template`, `node tests/test-database-intent.mjs`, `node tests/test-semantic-page-composer.mjs`, `node tests/test-structured-receipts.mjs`, `node tests/test-organize-tools.mjs`, and `node tests/test-supporting-tools.mjs`
475
239
 
476
- ```json
477
- "env": {
478
- "AFFINE_DISABLED_TOOLS": "delete_workspace,delete_doc"
479
- }
480
- ```
481
-
482
- ## Use Locally (clone)
240
+ Local clone flow:
483
241
 
484
242
  ```bash
485
243
  git clone https://github.com/dawncr0w/affine-mcp-server.git
486
244
  cd affine-mcp-server
487
245
  npm install
488
246
  npm run build
489
- # Run directly
490
247
  node dist/index.js
491
-
492
- # Or expose as a global CLI for Codex/Claude without publishing
493
- npm link
494
- # Now use `affine-mcp` like a global binary
495
- ```
496
-
497
- ## Quality Gates
498
-
499
- ```bash
500
- npm run build
501
- npm run test:tool-manifest
502
- npm run pack:check
503
248
  ```
504
249
 
505
- - `tool-manifest.json` is the source of truth for publicly exposed tool names.
506
- - CI validates that `registerTool(...)` declarations match the manifest exactly.
507
- - For full tool-surface verification, run `npm run test:comprehensive` (self-bootstraps a local Docker AFFiNE stack).
508
- - For pre-provisioned environments, use `npm run test:comprehensive:raw`.
509
- - For full environment verification, run `npm run test:e2e` (Docker + MCP + Playwright).
510
- - Additional focused runners: `npm run test:db-create`, `npm run test:db-cells`, `npm run test:db-schema`, `npm run test:supporting-tools`, `npm run test:organize`, `npm run test:bearer`, `npm run test:http-email-password`, `npm run test:http-bearer`, `npm run test:oauth-http`, `npm run test:doc-discovery`, `npm run test:cli-version`, `npm run test:cli-commands`, `npm run test:cli-live`, `npm run test:tool-filtering`, `npm run test:markdown-rich-text-import`, `npm run test:playwright`.
511
-
512
- ## Troubleshooting
513
-
514
- Authentication
515
- - **Cloudflare (403 "Just a moment...")**: AFFiNE Cloud (`app.affine.pro`) uses Cloudflare protection, which blocks programmatic sign-in via `/api/auth/sign-in`. Use `AFFINE_API_TOKEN` instead, or run `affine-mcp login` which guides you through the right method automatically. Email/password auth only works for self-hosted instances.
516
- - Email/Password: only works on self-hosted instances without Cloudflare. Ensure your instance allows password auth and credentials are valid.
517
- - Cookie: copy cookies (e.g., `affine_session`, `affine_csrf`) from the browser DevTools after login
518
- - Token: generate a personal access token; verify it hasn't expired. Run `affine-mcp status` to test.
519
- - Startup timeouts: v1.2.2+ includes a CLI wrapper fix and default async login to avoid blocking the MCP handshake. Set `AFFINE_LOGIN_AT_START=sync` only if needed.
520
-
521
- Connection
522
- - Confirm `AFFINE_BASE_URL` is reachable
523
- - GraphQL endpoint default is `/graphql`
524
- - Check firewall/proxy rules; verify CORS if self‑hosted
525
-
526
- Method not found
527
- - MCP tool names (for example `list_workspaces`) are not JSON-RPC top-level method names.
528
- - Use an MCP client (`tools/list`, `tools/call`) instead of sending direct JSON-RPC calls like `{\"method\":\"list_workspaces\"}`.
529
- - From v1.3.0, only canonical tool names are exposed (legacy `affine_*` aliases were removed).
530
-
531
- Workspace visibility
532
- - This MCP server can access server-backed workspaces only (AFFiNE cloud/self-hosted).
533
- - Browser local-storage workspaces are client-side data, so they are not visible via server GraphQL/WebSocket APIs.
534
-
535
- ## Security Considerations
536
-
537
- - Never commit `.env` with secrets
538
- - Prefer environment variables in production
539
- - Rotate access tokens regularly
540
- - Use HTTPS
541
- - Store credentials in a secrets manager
542
-
543
250
  ## Release Notes
544
251
 
545
- - Changelog: [CHANGELOG.md](CHANGELOG.md)
546
- - Release notes: [RELEASE_NOTES.md](RELEASE_NOTES.md)
547
- - GitHub Releases: [Releases](https://github.com/dawncr0w/affine-mcp-server/releases)
548
-
549
- ## Contributing
550
-
551
- Contributions are welcome!
552
- 1. Read `CONTRIBUTING.md`
553
- 2. Run `npm run ci` locally before opening PR
554
- 3. Keep tool changes synced with `tool-manifest.json`
555
- 4. Use issue/PR templates in `.github/`
556
-
557
- ## Community Health
558
-
559
- - Code of Conduct: `CODE_OF_CONDUCT.md`
560
- - Security policy: `SECURITY.md`
561
- - Contributing guide: `CONTRIBUTING.md`
252
+ - [CHANGELOG.md](CHANGELOG.md)
253
+ - [RELEASE_NOTES.md](RELEASE_NOTES.md)
254
+ - [GitHub Releases](https://github.com/dawncr0w/affine-mcp-server/releases)
562
255
 
563
256
  ## License
564
257
 
565
- MIT License - see LICENSE file for details
258
+ MIT License - see [LICENSE](LICENSE).
566
259
 
567
260
  ## Support
568
261
 
569
- For issues and questions:
570
262
  - Open an issue on [GitHub](https://github.com/dawncr0w/affine-mcp-server/issues)
571
- - Check AFFiNE documentation at https://docs.affine.pro
572
-
573
- ## Author
574
-
575
- **dawncr0w** - [GitHub](https://github.com/dawncr0w)
263
+ - Review AFFiNE product documentation at [docs.affine.pro](https://docs.affine.pro)
576
264
 
577
265
  ## Acknowledgments
578
266