@mseep/affine-mcp-server 2.3.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.
Files changed (43) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +270 -0
  3. package/bin/affine-mcp +5 -0
  4. package/dist/auth.js +61 -0
  5. package/dist/cli.js +726 -0
  6. package/dist/config.js +178 -0
  7. package/dist/edgeless/layout.js +222 -0
  8. package/dist/graphqlClient.js +116 -0
  9. package/dist/httpAuth.js +147 -0
  10. package/dist/httpDiagnostics.js +38 -0
  11. package/dist/index.js +209 -0
  12. package/dist/markdown/parse.js +559 -0
  13. package/dist/markdown/render.js +227 -0
  14. package/dist/markdown/types.js +1 -0
  15. package/dist/oauth.js +154 -0
  16. package/dist/sse.js +261 -0
  17. package/dist/toolSurface.js +349 -0
  18. package/dist/tools/accessTokens.js +45 -0
  19. package/dist/tools/auth.js +18 -0
  20. package/dist/tools/blobStorage.js +136 -0
  21. package/dist/tools/comments.js +104 -0
  22. package/dist/tools/docs.js +7478 -0
  23. package/dist/tools/history.js +22 -0
  24. package/dist/tools/icons.js +125 -0
  25. package/dist/tools/notifications.js +79 -0
  26. package/dist/tools/organize.js +1145 -0
  27. package/dist/tools/properties.js +426 -0
  28. package/dist/tools/user.js +13 -0
  29. package/dist/tools/userCRUD.js +77 -0
  30. package/dist/tools/workspaces.js +322 -0
  31. package/dist/util/explorerIcon.js +95 -0
  32. package/dist/util/mcp.js +28 -0
  33. package/dist/ws.js +113 -0
  34. package/docs/assets/edgeless-canvas-demo-advanced-dark.png +0 -0
  35. package/docs/assets/edgeless-canvas-demo-advanced-light.png +0 -0
  36. package/docs/client-setup.md +174 -0
  37. package/docs/configuration-and-deployment.md +265 -0
  38. package/docs/edgeless-canvas-cookbook.md +226 -0
  39. package/docs/getting-started.md +229 -0
  40. package/docs/tool-reference.md +200 -0
  41. package/docs/workflow-recipes.md +147 -0
  42. package/package.json +118 -0
  43. package/tool-manifest.json +99 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 The AFFiNE MCP Server Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,270 @@
1
+ # AFFiNE MCP Server
2
+
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
+
5
+ [![Version](https://img.shields.io/badge/version-2.3.0-blue)](https://github.com/dawncr0w/affine-mcp-server/releases)
6
+ [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.17.2-green)](https://github.com/modelcontextprotocol/typescript-sdk)
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
+ [![License](https://img.shields.io/badge/license-MIT-yellow)](LICENSE)
9
+
10
+ <a href="https://glama.ai/mcp/servers/@DAWNCR0W/affine-mcp-server">
11
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@DAWNCR0W/affine-mcp-server/badge" alt="AFFiNE Server MCP server" />
12
+ </a>
13
+
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)
28
+
29
+ ## Overview
30
+
31
+ AFFiNE MCP Server is designed for three common scenarios:
32
+ - Run a local stdio MCP server for Claude Code, Codex CLI, Cursor, or Claude Desktop
33
+ - Expose a remote HTTP MCP endpoint for hosted or browser-connected clients
34
+ - Automate AFFiNE workspace, document, database, organization, and comment workflows through a stable MCP tool surface
35
+
36
+ Highlights:
37
+
38
+ - Supports AFFiNE Cloud and self-hosted AFFiNE instances
39
+ - Supports stdio and HTTP transports
40
+ - Supports token, cookie, and email/password authentication
41
+ - Exposes 94 canonical MCP tools backed by AFFiNE GraphQL and WebSocket APIs
42
+ - Includes semantic page composition, native template instantiation, database intent composition, capability and fidelity reporting, and workspace blueprint helpers
43
+ - Includes Docker images, health probes, and end-to-end test coverage
44
+
45
+ Scope boundaries:
46
+
47
+ - This server can access only server-backed AFFiNE workspaces
48
+ - Browser-local workspaces stored only in local storage are not available through AFFiNE server APIs
49
+ - AFFiNE Cloud requires API-token-based access for MCP usage; programmatic email/password sign-in is blocked by Cloudflare
50
+
51
+ > New in v2.3.0: Added document and folder sidebar icon tools, plus richer hierarchy detection for inline LinkedPage references and synced-doc embeds.
52
+
53
+ ## Choose Your Path
54
+ | Goal | Start here |
55
+ | --- | --- |
56
+ | Set up a local stdio server with the least friction | [docs/getting-started.md](docs/getting-started.md) |
57
+ | 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) |
58
+ | Configure Claude Code, Claude Desktop, Codex CLI, or Cursor | [docs/client-setup.md](docs/client-setup.md) |
59
+ | Run the server remotely over HTTP or behind OAuth | [docs/configuration-and-deployment.md](docs/configuration-and-deployment.md) |
60
+ | 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) |
61
+ | Learn common AFFiNE workflows and tool sequences | [docs/workflow-recipes.md](docs/workflow-recipes.md) |
62
+ | Browse the tool catalog by domain | [docs/tool-reference.md](docs/tool-reference.md) |
63
+
64
+ ## Quick Start
65
+
66
+ ### 1. Install the CLI
67
+
68
+ ```bash
69
+ npm i -g affine-mcp-server
70
+ affine-mcp --version
71
+ ```
72
+
73
+ You can also run the package ad hoc:
74
+
75
+ ```bash
76
+ npx -y -p affine-mcp-server affine-mcp -- --version
77
+ ```
78
+
79
+ ### 2. Or run the server in Docker
80
+
81
+ ```bash
82
+ docker run -d \
83
+ -p 3000:3000 \
84
+ -e MCP_TRANSPORT=http \
85
+ -e AFFINE_BASE_URL=https://your-affine-instance.com \
86
+ -e AFFINE_API_TOKEN=ut_your_token \
87
+ -e AFFINE_MCP_AUTH_MODE=bearer \
88
+ -e AFFINE_MCP_HTTP_TOKEN=your-strong-secret \
89
+ ghcr.io/dawncr0w/affine-mcp-server:latest
90
+ ```
91
+
92
+ Then point your client at:
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "affine": {
98
+ "type": "http",
99
+ "url": "http://localhost:3000/mcp",
100
+ "headers": {
101
+ "Authorization": "Bearer your-strong-secret"
102
+ }
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ For Docker, health checks, and remote deployment details, see [docs/configuration-and-deployment.md#docker](docs/configuration-and-deployment.md#docker).
109
+
110
+ ### 3. Save credentials with interactive login
111
+
112
+ ```bash
113
+ affine-mcp login
114
+ ```
115
+
116
+ This stores credentials in `~/.config/affine-mcp/config` with mode `600`.
117
+
118
+ - For AFFiNE Cloud, use an API token from `Settings -> Integrations -> MCP Server`
119
+ - For self-hosted AFFiNE, you can use either an API token or email/password
120
+
121
+ ### 4. Register the server with your client
122
+
123
+ Claude Code project config:
124
+
125
+ ```json
126
+ {
127
+ "mcpServers": {
128
+ "affine": {
129
+ "command": "affine-mcp"
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ Codex CLI:
136
+
137
+ ```bash
138
+ codex mcp add affine -- affine-mcp
139
+ ```
140
+
141
+ More client-specific setup is in [docs/client-setup.md](docs/client-setup.md).
142
+
143
+ ### 5. Verify the connection
144
+
145
+ ```bash
146
+ affine-mcp status
147
+ affine-mcp doctor
148
+ ```
149
+
150
+ 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).
151
+
152
+ ## Compatibility Matrix
153
+
154
+ | Target | Transport | Recommended auth | Recommended path |
155
+ | --- | --- | --- | --- |
156
+ | Claude Code | stdio | Saved config or API token | [docs/client-setup.md#claude-code](docs/client-setup.md#claude-code) |
157
+ | Claude Desktop | stdio | Saved config or API token | [docs/client-setup.md#claude-desktop](docs/client-setup.md#claude-desktop) |
158
+ | Codex CLI | stdio | Saved config or API token | [docs/client-setup.md#codex-cli](docs/client-setup.md#codex-cli) |
159
+ | Cursor | stdio | Saved config or API token | [docs/client-setup.md#cursor](docs/client-setup.md#cursor) |
160
+ | 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) |
161
+ | Remote MCP clients | HTTP | Bearer token or OAuth | [docs/configuration-and-deployment.md#http-mode](docs/configuration-and-deployment.md#http-mode) |
162
+ | AFFiNE Cloud | stdio or HTTP | API token | [docs/configuration-and-deployment.md#auth-strategy-matrix](docs/configuration-and-deployment.md#auth-strategy-matrix) |
163
+ | 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) |
164
+
165
+ ## Tool Surface
166
+
167
+ `tool-manifest.json` is the source of truth for canonical tool names. The MCP server exposes those tools through `tools/list` and `tools/call`.
168
+
169
+ Domains:
170
+
171
+ - Workspace: create, inspect, update, delete, and traverse workspaces
172
+ - Organization: collections, collection-rule sync, workspace blueprints, and experimental organize or folder helpers
173
+ - Documents: search, read, create, publish, move, tag, custom properties, import/export, semantic composition, template inspection and native instantiation, capability and fidelity reporting, and block-level mutation
174
+ - Databases: create columns, add rows, update rows, inspect schema, and compose database structures from intent
175
+ - Comments: list, create, update, delete, and resolve
176
+ - History: version history listing
177
+ - Users and tokens: current user, sign-in, profile/settings, personal access tokens
178
+ - Notifications: list and mark notifications as read
179
+ - Blob storage: upload, delete, and cleanup blobs
180
+
181
+ Use `AFFINE_TOOL_PROFILE=read_only`, `core`, or `authoring` when a deployment should expose a smaller surface than the complete `full` default. You can also combine profiles with `AFFINE_DISABLED_GROUPS` such as `docs.database`, `destructive`, or `admin` for finer control.
182
+
183
+ For the grouped catalog, notes, and operational caveats, see [docs/tool-reference.md](docs/tool-reference.md).
184
+
185
+ ## Documentation Map
186
+
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
+ | [docs/edgeless-canvas-cookbook.md](docs/edgeless-canvas-cookbook.md) | Edgeless canvas layout helpers and surface elements, worked end-to-end |
195
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Contributor workflow |
196
+ | [SECURITY.md](SECURITY.md) | Security reporting |
197
+
198
+ ## Verify Your Setup
199
+
200
+ Useful CLI commands:
201
+
202
+ - `affine-mcp status` - test the effective configuration
203
+ - `affine-mcp status --json` - machine-readable status output
204
+ - `affine-mcp doctor` - diagnose config and connectivity issues
205
+ - `affine-mcp show-config` - print the effective config with secrets redacted
206
+ - `affine-mcp config-path` - print the config file path
207
+ - `affine-mcp snippet <claude|cursor|codex|all> [--env]` - generate ready-to-paste client config
208
+ - `affine-mcp logout` - remove stored credentials
209
+
210
+ For common failures, see:
211
+
212
+ - [docs/getting-started.md#common-first-run-failures](docs/getting-started.md#common-first-run-failures)
213
+ - [docs/configuration-and-deployment.md#deployment-checklist](docs/configuration-and-deployment.md#deployment-checklist)
214
+
215
+ ## Security and Scope
216
+
217
+ - Never commit secrets or long-lived tokens
218
+ - Prefer API tokens over cookies or passwords in production
219
+ - Use HTTPS for non-local deployments
220
+ - Rotate access tokens regularly
221
+ - Restrict exposed tools with `AFFINE_DISABLED_GROUPS` and `AFFINE_DISABLED_TOOLS` for least-privilege setups
222
+ - Use `/healthz` and `/readyz` when running the HTTP server behind a container platform or load balancer
223
+
224
+ ## Development
225
+
226
+ Run the main quality gates before opening a PR:
227
+
228
+ ```bash
229
+ npm run build
230
+ npm run test:tool-manifest
231
+ npm run pack:check
232
+ ```
233
+
234
+ Additional validation:
235
+
236
+ - `npm run test:comprehensive` boots a local Docker AFFiNE stack and validates the tool surface
237
+ - `npm run test:e2e` runs Docker, MCP, and Playwright together
238
+ - `npm run test:playwright` runs the Playwright suite only
239
+ - 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`
240
+
241
+ Local clone flow:
242
+
243
+ ```bash
244
+ git clone https://github.com/dawncr0w/affine-mcp-server.git
245
+ cd affine-mcp-server
246
+ npm install
247
+ npm run build
248
+ node dist/index.js
249
+ ```
250
+
251
+ ## Release Notes
252
+
253
+ - [CHANGELOG.md](CHANGELOG.md)
254
+ - [RELEASE_NOTES.md](RELEASE_NOTES.md)
255
+ - [GitHub Releases](https://github.com/dawncr0w/affine-mcp-server/releases)
256
+
257
+ ## License
258
+
259
+ MIT License - see [LICENSE](LICENSE).
260
+
261
+ ## Support
262
+
263
+ - Open an issue on [GitHub](https://github.com/dawncr0w/affine-mcp-server/issues)
264
+ - Review AFFiNE product documentation at [docs.affine.pro](https://docs.affine.pro)
265
+
266
+ ## Acknowledgments
267
+
268
+ - Built for the [AFFiNE](https://affine.pro) knowledge base platform
269
+ - Uses the [Model Context Protocol](https://modelcontextprotocol.io) specification
270
+ - Powered by [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)
package/bin/affine-mcp ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ // Lightweight CLI wrapper to ensure Node runs the ESM entry
3
+ // Resolves to the compiled ESM entrypoint in dist
4
+ import '../dist/index.js';
5
+
package/dist/auth.js ADDED
@@ -0,0 +1,61 @@
1
+ import { fetch } from "undici";
2
+ const AUTH_FETCH_TIMEOUT_MS = 30_000;
3
+ function extractCookiePairs(setCookies) {
4
+ const pairs = [];
5
+ for (const sc of setCookies) {
6
+ const first = sc.split(";")[0];
7
+ if (first)
8
+ pairs.push(first.trim());
9
+ }
10
+ return pairs.join("; ");
11
+ }
12
+ /** Reject cookie values containing CR/LF to prevent header injection. */
13
+ function assertNoCRLF(value, label) {
14
+ if (/[\r\n]/.test(value)) {
15
+ throw new Error(`${label} contains illegal CR/LF characters`);
16
+ }
17
+ }
18
+ export async function loginWithPassword(baseUrl, email, password) {
19
+ const url = `${baseUrl.replace(/\/$/, "")}/api/auth/sign-in`;
20
+ const controller = new AbortController();
21
+ const timer = setTimeout(() => controller.abort(), AUTH_FETCH_TIMEOUT_MS);
22
+ let res;
23
+ try {
24
+ res = await fetch(url, {
25
+ method: "POST",
26
+ headers: { "Content-Type": "application/json" },
27
+ body: JSON.stringify({ email, password }),
28
+ signal: controller.signal,
29
+ });
30
+ }
31
+ catch (err) {
32
+ if (err.name === "AbortError")
33
+ throw new Error(`Sign-in request timed out after ${AUTH_FETCH_TIMEOUT_MS / 1000}s`);
34
+ throw err;
35
+ }
36
+ finally {
37
+ clearTimeout(timer);
38
+ }
39
+ if (!res.ok) {
40
+ const raw = await res.text().catch(() => "");
41
+ const sanitized = raw.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim();
42
+ const truncated = sanitized.length > 200 ? sanitized.slice(0, 200) + "..." : sanitized;
43
+ throw new Error(`Sign-in failed: ${res.status} ${truncated}`);
44
+ }
45
+ const anyHeaders = res.headers;
46
+ let setCookies = [];
47
+ if (typeof anyHeaders.getSetCookie === "function") {
48
+ setCookies = anyHeaders.getSetCookie();
49
+ }
50
+ else {
51
+ const sc = res.headers.get("set-cookie");
52
+ if (sc)
53
+ setCookies = [sc];
54
+ }
55
+ if (!setCookies.length) {
56
+ throw new Error("Sign-in succeeded but no Set-Cookie received");
57
+ }
58
+ const cookieHeader = extractCookiePairs(setCookies);
59
+ assertNoCRLF(cookieHeader, "Cookie header from sign-in");
60
+ return { cookieHeader };
61
+ }