affine-mcp-server 1.12.0 → 2.0.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 +155 -502
- package/dist/edgeless/layout.js +222 -0
- package/dist/index.js +34 -62
- package/dist/markdown/parse.js +51 -10
- package/dist/toolSurface.js +322 -0
- package/dist/tools/comments.js +25 -5
- package/dist/tools/docs.js +3220 -583
- package/dist/tools/organize.js +419 -42
- package/dist/tools/workspaces.js +25 -6
- package/dist/util/mcp.js +26 -2
- package/docs/assets/edgeless-canvas-demo-advanced-dark.png +0 -0
- package/docs/assets/edgeless-canvas-demo-advanced-light.png +0 -0
- package/docs/client-setup.md +174 -0
- package/docs/configuration-and-deployment.md +265 -0
- package/docs/edgeless-canvas-cookbook.md +226 -0
- package/docs/getting-started.md +229 -0
- package/docs/tool-reference.md +186 -0
- package/docs/workflow-recipes.md +147 -0
- package/package.json +11 -2
- package/tool-manifest.json +89 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
This guide is the fastest way to get AFFiNE MCP Server working and confirm that your MCP client can reach AFFiNE successfully.
|
|
4
|
+
|
|
5
|
+
## Choose a setup path
|
|
6
|
+
|
|
7
|
+
| Scenario | Recommended path |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| Local MCP client on your machine | Use the saved-config flow with `affine-mcp login` |
|
|
10
|
+
| AFFiNE Cloud | Use an API token |
|
|
11
|
+
| Self-hosted AFFiNE | Use an API token, or email/password if your instance allows it |
|
|
12
|
+
| Temporary usage without a global install | Use `npx` |
|
|
13
|
+
| Run the server in Docker | Use the GHCR image and HTTP transport |
|
|
14
|
+
| Remote MCP deployment | Skip to [configuration and deployment](configuration-and-deployment.md) |
|
|
15
|
+
|
|
16
|
+
## Path A: Saved config with interactive login
|
|
17
|
+
|
|
18
|
+
This is the recommended local setup because it keeps client config minimal and avoids repeated environment-variable configuration.
|
|
19
|
+
|
|
20
|
+
### 1. Install the CLI
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm i -g affine-mcp-server
|
|
24
|
+
affine-mcp --version
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Save credentials
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
affine-mcp login
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
What happens:
|
|
34
|
+
|
|
35
|
+
- The CLI asks for your AFFiNE base URL
|
|
36
|
+
- For AFFiNE Cloud, it prompts for an API token
|
|
37
|
+
- For self-hosted AFFiNE, it can sign in with email/password and generate a token automatically
|
|
38
|
+
- The effective config is stored at `~/.config/affine-mcp/config`
|
|
39
|
+
|
|
40
|
+
### 3. Verify the saved config
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
affine-mcp status
|
|
44
|
+
affine-mcp doctor
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 4. Register the server with a client
|
|
48
|
+
|
|
49
|
+
Minimal stdio config:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"affine": {
|
|
55
|
+
"command": "affine-mcp"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
See [client setup](client-setup.md) for full client-specific snippets.
|
|
62
|
+
|
|
63
|
+
## Path B: Explicit environment variables
|
|
64
|
+
|
|
65
|
+
Use this path when you prefer stateless or container-friendly setup instead of a saved config file.
|
|
66
|
+
|
|
67
|
+
### Required variables
|
|
68
|
+
|
|
69
|
+
- `AFFINE_BASE_URL`
|
|
70
|
+
- One auth strategy:
|
|
71
|
+
- `AFFINE_API_TOKEN`
|
|
72
|
+
- `AFFINE_COOKIE`
|
|
73
|
+
- `AFFINE_EMAIL` and `AFFINE_PASSWORD`
|
|
74
|
+
|
|
75
|
+
### Example: AFFiNE Cloud
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
export AFFINE_BASE_URL="https://app.affine.pro"
|
|
79
|
+
export AFFINE_API_TOKEN="ut_xxx"
|
|
80
|
+
affine-mcp status
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Example: self-hosted AFFiNE with email/password
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
export AFFINE_BASE_URL="https://your-affine.example.com"
|
|
87
|
+
export AFFINE_EMAIL="you@example.com"
|
|
88
|
+
export AFFINE_PASSWORD="secret"
|
|
89
|
+
affine-mcp status
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
For the full environment-variable reference, see [configuration and deployment](configuration-and-deployment.md#environment-variables).
|
|
93
|
+
|
|
94
|
+
## Path C: Run from the Docker image
|
|
95
|
+
|
|
96
|
+
Use this when:
|
|
97
|
+
|
|
98
|
+
- you want a containerized local or remote deployment
|
|
99
|
+
- you prefer an HTTP MCP endpoint over stdio
|
|
100
|
+
- you do not want to install Node.js on the target host
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
docker run -d \
|
|
104
|
+
-p 3000:3000 \
|
|
105
|
+
-e MCP_TRANSPORT=http \
|
|
106
|
+
-e AFFINE_BASE_URL=https://your-affine-instance.com \
|
|
107
|
+
-e AFFINE_API_TOKEN=ut_your_token \
|
|
108
|
+
-e AFFINE_MCP_AUTH_MODE=bearer \
|
|
109
|
+
-e AFFINE_MCP_HTTP_TOKEN=your-strong-secret \
|
|
110
|
+
ghcr.io/dawncr0w/affine-mcp-server:latest
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Quick verification:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
curl http://localhost:3000/healthz
|
|
117
|
+
curl http://localhost:3000/readyz
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Client-side MCP config:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"mcpServers": {
|
|
125
|
+
"affine": {
|
|
126
|
+
"type": "http",
|
|
127
|
+
"url": "http://localhost:3000/mcp",
|
|
128
|
+
"headers": {
|
|
129
|
+
"Authorization": "Bearer your-strong-secret"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
For OAuth mode, origin controls, and deployment hardening, continue with [configuration and deployment](configuration-and-deployment.md#docker).
|
|
137
|
+
|
|
138
|
+
## Path D: Run from a local clone
|
|
139
|
+
|
|
140
|
+
Use this when you want to inspect or modify the server locally.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
git clone https://github.com/dawncr0w/affine-mcp-server.git
|
|
144
|
+
cd affine-mcp-server
|
|
145
|
+
npm install
|
|
146
|
+
npm run build
|
|
147
|
+
node dist/index.js
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
You can also expose a linked CLI locally:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm link
|
|
154
|
+
affine-mcp --version
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Verify your setup
|
|
158
|
+
|
|
159
|
+
Use this sequence after any first-run setup:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
affine-mcp status
|
|
163
|
+
affine-mcp show-config
|
|
164
|
+
affine-mcp doctor
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If you are running the Docker image, also verify:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
curl http://localhost:3000/healthz
|
|
171
|
+
curl http://localhost:3000/readyz
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Expected results:
|
|
175
|
+
|
|
176
|
+
- `status` confirms the active base URL, auth source, and connection result
|
|
177
|
+
- `show-config` prints the effective configuration with secrets redacted
|
|
178
|
+
- `doctor` checks config shape and connectivity and points to the failing layer
|
|
179
|
+
- `healthz` and `readyz` return successful probe responses when the HTTP server is healthy
|
|
180
|
+
|
|
181
|
+
If you are onboarding another client, these helpers can generate snippets from the current config:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
affine-mcp snippet claude --env
|
|
185
|
+
affine-mcp snippet codex --env
|
|
186
|
+
affine-mcp snippet all --env
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Common first-run failures
|
|
190
|
+
|
|
191
|
+
### Cloudflare blocks email/password sign-in
|
|
192
|
+
|
|
193
|
+
AFFiNE Cloud (`app.affine.pro`) is behind Cloudflare. Programmatic requests to `/api/auth/sign-in` are blocked.
|
|
194
|
+
|
|
195
|
+
Use:
|
|
196
|
+
|
|
197
|
+
- `AFFINE_API_TOKEN`
|
|
198
|
+
- or `affine-mcp login`, which guides you toward the supported path
|
|
199
|
+
|
|
200
|
+
### Saved config exists, but the client cannot connect
|
|
201
|
+
|
|
202
|
+
Run:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
affine-mcp status
|
|
206
|
+
affine-mcp doctor
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Then verify that the client is invoking `affine-mcp` from the same environment where the config file exists.
|
|
210
|
+
|
|
211
|
+
### Workspace is missing
|
|
212
|
+
|
|
213
|
+
This server can access only server-backed AFFiNE workspaces.
|
|
214
|
+
|
|
215
|
+
It cannot access workspaces that exist only in browser local storage.
|
|
216
|
+
|
|
217
|
+
### "Method not found" when calling a tool
|
|
218
|
+
|
|
219
|
+
MCP tools are not JSON-RPC top-level method names. Use an MCP client that calls `tools/list` and `tools/call` instead of sending direct JSON-RPC methods such as `{"method":"list_workspaces"}`.
|
|
220
|
+
|
|
221
|
+
### Self-hosted email/password does not work
|
|
222
|
+
|
|
223
|
+
Confirm:
|
|
224
|
+
|
|
225
|
+
- your instance exposes the standard auth endpoints
|
|
226
|
+
- Cloudflare or another bot-protection layer is not blocking sign-in
|
|
227
|
+
- the credentials are valid
|
|
228
|
+
|
|
229
|
+
If in doubt, switch to an API token.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Tool Reference
|
|
2
|
+
|
|
3
|
+
`tool-manifest.json` is the source of truth for the canonical tool names exposed by this server.
|
|
4
|
+
|
|
5
|
+
Use this document as a grouped catalog. For exact schemas, your MCP client should inspect `tools/list`.
|
|
6
|
+
|
|
7
|
+
## Conventions
|
|
8
|
+
|
|
9
|
+
- Canonical names only: legacy alias names are not part of the public tool surface
|
|
10
|
+
- Document editing relies on AFFiNE WebSocket-backed operations where noted
|
|
11
|
+
- Experimental organize tools are marked explicitly
|
|
12
|
+
- Use `AFFINE_TOOL_PROFILE=read_only`, `core`, or `authoring` in production if you want a reduced surface
|
|
13
|
+
|
|
14
|
+
## Workspace
|
|
15
|
+
|
|
16
|
+
| Tool | Purpose | Notes |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `list_workspaces` | List all available workspaces | Good first discovery step |
|
|
19
|
+
| `get_workspace` | Read workspace details | Includes settings and metadata |
|
|
20
|
+
| `create_workspace` | Create a workspace with an initial document | Destructive in the sense that it creates new server state |
|
|
21
|
+
| `update_workspace` | Update workspace settings | Use carefully in shared workspaces |
|
|
22
|
+
| `delete_workspace` | Permanently delete a workspace | Destructive |
|
|
23
|
+
| `list_workspace_tree` | Return the workspace document hierarchy as a tree | Useful before moving docs |
|
|
24
|
+
| `get_orphan_docs` | Find documents that are not linked from a parent doc | Useful for cleanup and audits |
|
|
25
|
+
|
|
26
|
+
## Organization
|
|
27
|
+
|
|
28
|
+
| Tool | Purpose | Notes |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `list_collections` | List workspace collections | |
|
|
31
|
+
| `get_collection` | Read a collection by id | |
|
|
32
|
+
| `create_collection` | Create a collection | |
|
|
33
|
+
| `update_collection` | Rename a collection | |
|
|
34
|
+
| `update_collection_rules` | Replace a collection's rules and rebuild its allow-list from workspace docs | Useful for rule-backed collections |
|
|
35
|
+
| `delete_collection` | Delete a collection | Destructive |
|
|
36
|
+
| `add_doc_to_collection` | Add a document to a collection allow-list | |
|
|
37
|
+
| `remove_doc_from_collection` | Remove a document from a collection allow-list | |
|
|
38
|
+
| `list_organize_nodes` | Dump the organize or folder tree | Experimental |
|
|
39
|
+
| `create_folder` | Create a root or nested folder | Experimental |
|
|
40
|
+
| `create_workspace_blueprint` | Create a simple workspace folder blueprint | Good for structured onboarding setups |
|
|
41
|
+
| `rename_folder` | Rename a folder | Experimental |
|
|
42
|
+
| `delete_folder` | Delete a folder recursively | Experimental and destructive |
|
|
43
|
+
| `move_organize_node` | Move a folder or link node | Experimental |
|
|
44
|
+
| `add_organize_link` | Add a doc, tag, or collection link under a folder | Experimental |
|
|
45
|
+
| `delete_organize_link` | Delete a doc, tag, or collection link | Experimental and destructive |
|
|
46
|
+
|
|
47
|
+
## Documents
|
|
48
|
+
|
|
49
|
+
### Discovery and metadata
|
|
50
|
+
|
|
51
|
+
| Tool | Purpose | Notes |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `list_docs` | List documents with pagination | Includes `node.tags` |
|
|
54
|
+
| `list_tags` | List all tags in a workspace | |
|
|
55
|
+
| `search_docs` | Search titles with substring, prefix, or exact matching | Supports tag filter and updatedAt sorting |
|
|
56
|
+
| `list_docs_by_tag` | List documents with a specific tag | |
|
|
57
|
+
| `get_doc` | Read document metadata | |
|
|
58
|
+
| `read_doc` | Read block content and plain text snapshot | WebSocket-backed |
|
|
59
|
+
| `get_capabilities` | Inspect the server's high-level authoring and fidelity capabilities | Useful for adaptive clients |
|
|
60
|
+
| `analyze_doc_fidelity` | Analyze how a document maps to Markdown and which native AFFiNE structures are lossy | Good before export or migration |
|
|
61
|
+
| `list_children` | List direct child docs linked from a document | |
|
|
62
|
+
|
|
63
|
+
### Publish and visibility
|
|
64
|
+
|
|
65
|
+
| Tool | Purpose | Notes |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `publish_doc` | Make a document public | |
|
|
68
|
+
| `revoke_doc` | Revoke public access | |
|
|
69
|
+
|
|
70
|
+
### Create, duplicate, and move
|
|
71
|
+
|
|
72
|
+
| Tool | Purpose | Notes |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `create_doc` | Create a new document | WebSocket-backed |
|
|
75
|
+
| `create_doc_from_markdown` | Create a document from Markdown content | |
|
|
76
|
+
| `inspect_template_structure` | Inspect a template's native AFFiNE structure and native-clone support | Helps choose a clone strategy |
|
|
77
|
+
| `instantiate_template_native` | Instantiate a template via native AFFiNE block cloning, with optional Markdown fallback | Higher-fidelity than Markdown-only cloning |
|
|
78
|
+
| `move_doc` | Move a document in the sidebar by relinking it under another parent | |
|
|
79
|
+
| `delete_doc` | Delete a document | WebSocket-backed and destructive |
|
|
80
|
+
|
|
81
|
+
### Content editing
|
|
82
|
+
|
|
83
|
+
| Tool | Purpose | Notes |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| `update_doc_title` | Rename a document in workspace metadata and in the page block | |
|
|
86
|
+
| `append_block` | Append canonical block types with validation and placement control | Supports text, media, embeds, database, and edgeless blocks. `frame`/`edgeless_text`/`note` accept `x`/`y`/`width`/`height`. `note` with `text` auto-creates a child paragraph so it renders on the edgeless canvas. |
|
|
87
|
+
| `create_semantic_page` | Create an AFFiNE-native page with an intentional section skeleton and native block composition | High-level authoring helper |
|
|
88
|
+
| `append_semantic_section` | Append a semantic section to an existing page by heading title | High-level authoring helper |
|
|
89
|
+
| `append_markdown` | Append Markdown content to an existing document | |
|
|
90
|
+
| `replace_doc_with_markdown` | Replace the main note content with Markdown | Overwrites main note content |
|
|
91
|
+
|
|
92
|
+
### Tags
|
|
93
|
+
|
|
94
|
+
| Tool | Purpose | Notes |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `create_tag` | Create a reusable workspace-level tag | |
|
|
97
|
+
| `add_tag_to_doc` | Attach a tag to a document | |
|
|
98
|
+
| `remove_tag_from_doc` | Detach a tag from a document | |
|
|
99
|
+
|
|
100
|
+
### Markdown export
|
|
101
|
+
|
|
102
|
+
| Tool | Purpose | Notes |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `export_doc_markdown` | Export document content as Markdown | Useful for backup and automation |
|
|
105
|
+
| `export_with_fidelity_report` | Export a document with a machine-readable fidelity report | Useful when native AFFiNE structures matter |
|
|
106
|
+
|
|
107
|
+
## Database blocks
|
|
108
|
+
|
|
109
|
+
| Tool | Purpose | Notes |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| `compose_database_from_intent` | Create or enrich a database block from a high-level schema intent | Useful for project boards and structured tables |
|
|
112
|
+
| `add_database_column` | Add a column to a database block | Supports `rich-text`, `select`, `multi-select`, `number`, `checkbox`, `link`, and `date` |
|
|
113
|
+
| `add_database_row` | Add a row to a database block | Can set the built-in title field |
|
|
114
|
+
| `delete_database_row` | Delete a row by row block id | Destructive |
|
|
115
|
+
| `read_database_columns` | Read schema metadata, types, options, and view mappings | Useful before edits |
|
|
116
|
+
| `read_database_cells` | Read row titles and decoded cell values | Supports row and column filters |
|
|
117
|
+
| `update_database_row` | Update multiple cells on a row at once | `createOption` defaults to `true` |
|
|
118
|
+
|
|
119
|
+
## Edgeless canvas and surface elements
|
|
120
|
+
|
|
121
|
+
AFFiNE's edgeless doc has two layers: top-level edgeless blocks (`note`, `frame`, `edgeless-text`) with `prop:xywh`, and the surface layer (`affine:surface`) which stores free-floating shapes, connectors, canvas text, and groups in `prop:elements.value` — the native BlockSuite representation.
|
|
122
|
+
|
|
123
|
+
| Tool | Purpose | Notes |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| `get_edgeless_canvas` | Read the full canvas: edgeless blocks + surface elements with parsed `{x,y,width,height}`, aggregate `bounds`, per-type `elementCounts` | Deterministic z-order (fractional-index sorted). Note entries carry a structured `children` array of their block descendants (`flavour`, `type`, `text`, `language`, `checked`) so markdown-seeded content round-trips faithfully. |
|
|
126
|
+
| `add_surface_element` | Add a `shape`, `connector`, `text`, or `group` to the surface | Shapes: rect/ellipse/diamond/triangle with fill, stroke, and text. Connectors accept `sourceId`/`targetId` and optional `sourcePosition`/`targetPosition` relative `[x,y]` in `[0,1]`. When both endpoints are bound by id and neither position is supplied, they auto-snap to BlockSuite's four tangent-carrying side-midpoints based on relative bounds. Creates the surface block if the doc doesn't have one. |
|
|
127
|
+
| `list_surface_elements` | List all surface elements (optionally filter by `type` or `elementId`) | Returns raw `xywh` plus parsed `bounds` sorted by fractional `index` ascending; serializes `Y.Text` fields to plain strings. |
|
|
128
|
+
| `update_surface_element` | Partially update an element by id | `x`/`y`/`width`/`height` merge with current `xywh` (move without resizing, or vice versa). `text`/`label`/`title` replace their `Y.Text` wholesale. Fields not applicable to the element's type come back in the response `ignored` list. |
|
|
129
|
+
| `delete_surface_element` | Delete an element by id | `pruneConnectors: true` additionally removes any connectors referencing the deleted element. |
|
|
130
|
+
| `update_frame_children` | Replace a frame block's contents wholesale | Every resolved id (surface element or edgeless block) goes into `prop:childElementIds` and comes back in `ownedIds`; unknown ids in `missing`. Default `resizeToFit: true` recomputes xywh to match new contents + `padding` + title band; pass `resizeToFit: false` to preserve the current box. Pass `[]` to clear ownership (resize skipped). |
|
|
131
|
+
| `update_edgeless_block` | Partially update a note/frame/edgeless-text block | `x`/`y`/`width`/`height` merge with current `prop:xywh`; `background` replaces `prop:background`. Fields not applicable to the flavour come back under `ignored`. Use for repositioning / resizing / recoloring without re-creating the block. |
|
|
132
|
+
| `delete_block` | Delete a block by id | Removes descendants and unlinks from the parent's `sys:children` by default. `deleteChildren: false` keeps descendants orphaned; `pruneConnectors: true` also drops surface connectors referencing any deleted id. Refuses `affine:page`. |
|
|
133
|
+
|
|
134
|
+
### Layout helpers on `append_block`
|
|
135
|
+
|
|
136
|
+
When the new block is a frame/note/edgeless_text on the canvas, `append_block` accepts three optional fields that compute coordinates from the current doc state instead of the caller doing arithmetic:
|
|
137
|
+
|
|
138
|
+
| Field | Applies to | Purpose |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| `markdown` | `type="note"` | Parse markdown into heading/paragraph/list/code child blocks inside the note. Height auto-estimated from the content when `height` is omitted. |
|
|
141
|
+
| `childElementIds: [id, ...]` | `type="frame"` | The frame's contents. Accepts ids of surface elements (shapes/connectors/groups) AND edgeless blocks (notes/frames/edgeless-text) — every resolved id goes into `prop:childElementIds`, matching what BlockSuite's editor writes when you drag members into a frame. Dragging the frame drags every owned member. Unresolved ids come back under `missing`. If `width`/`height` are omitted, the frame is sized to the union of resolvable bounds + `padding` + a 30px title band. |
|
|
142
|
+
| `stackAfter: { blockId, direction?, gap? }` | any canvas block | Position relative to one or more existing siblings. `blockId` may be an array — picks whichever ref is furthest in the stack direction (useful when stacking below a row of columns) and centers the new block on the union bounds' orthogonal axis (when widths match, same as inheriting the anchor's x). Caller-provided `x` / `y` on the orthogonal axis still wins. Default `gap` is direction-aware: **80px horizontal** (left/right), **40px vertical** (up/down) — mirrors native-flowchart spacing where the flow axis gets more breathing room. |
|
|
143
|
+
| `padding` | used by `childElementIds` auto-sizing and as fallback `gap` for `stackAfter` | Default 40. Explicit `padding` on the block overrides the direction-aware default; explicit `stackAfter.gap` wins over both. |
|
|
144
|
+
|
|
145
|
+
## Comments
|
|
146
|
+
|
|
147
|
+
| Tool | Purpose | Notes |
|
|
148
|
+
| --- | --- | --- |
|
|
149
|
+
| `list_comments` | List comments on a document | |
|
|
150
|
+
| `create_comment` | Create a comment on a document | |
|
|
151
|
+
| `update_comment` | Update comment content | |
|
|
152
|
+
| `delete_comment` | Delete a comment | Destructive |
|
|
153
|
+
| `resolve_comment` | Resolve or unresolve a comment | |
|
|
154
|
+
|
|
155
|
+
## Version History
|
|
156
|
+
|
|
157
|
+
| Tool | Purpose | Notes |
|
|
158
|
+
| --- | --- | --- |
|
|
159
|
+
| `list_histories` | List document history timestamps | |
|
|
160
|
+
|
|
161
|
+
## Users and tokens
|
|
162
|
+
|
|
163
|
+
| Tool | Purpose | Notes |
|
|
164
|
+
| --- | --- | --- |
|
|
165
|
+
| `current_user` | Return the current signed-in user | |
|
|
166
|
+
| `sign_in` | Sign in with email and password | Self-hosted flows only for direct programmatic sign-in |
|
|
167
|
+
| `update_profile` | Update current user profile data | |
|
|
168
|
+
| `update_settings` | Update user notification preferences | |
|
|
169
|
+
| `list_access_tokens` | List personal access tokens | |
|
|
170
|
+
| `generate_access_token` | Create a personal access token | Sensitive operation |
|
|
171
|
+
| `revoke_access_token` | Revoke a personal access token | Destructive |
|
|
172
|
+
|
|
173
|
+
## Notifications
|
|
174
|
+
|
|
175
|
+
| Tool | Purpose | Notes |
|
|
176
|
+
| --- | --- | --- |
|
|
177
|
+
| `list_notifications` | List notifications for the current user | |
|
|
178
|
+
| `read_all_notifications` | Mark notifications as read | |
|
|
179
|
+
|
|
180
|
+
## Blob storage
|
|
181
|
+
|
|
182
|
+
| Tool | Purpose | Notes |
|
|
183
|
+
| --- | --- | --- |
|
|
184
|
+
| `upload_blob` | Upload a file or blob to workspace storage | |
|
|
185
|
+
| `delete_blob` | Delete a blob from workspace storage | Destructive |
|
|
186
|
+
| `cleanup_blobs` | Permanently remove deleted blobs | Cleanup-oriented |
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Workflow Recipes
|
|
2
|
+
|
|
3
|
+
This guide shows practical tool sequences for common AFFiNE workflows.
|
|
4
|
+
|
|
5
|
+
The exact JSON schema for each tool is discoverable from MCP `tools/list`. The recipes below focus on tool selection and ordering.
|
|
6
|
+
|
|
7
|
+
## 1. Discover the right workspace and document
|
|
8
|
+
|
|
9
|
+
Use when:
|
|
10
|
+
|
|
11
|
+
- you are connecting to an AFFiNE instance for the first time
|
|
12
|
+
- you need to locate a document before editing it
|
|
13
|
+
|
|
14
|
+
Typical tool sequence:
|
|
15
|
+
|
|
16
|
+
1. `list_workspaces`
|
|
17
|
+
2. `list_docs` or `search_docs`
|
|
18
|
+
3. `get_doc` or `read_doc`
|
|
19
|
+
|
|
20
|
+
Prompt example:
|
|
21
|
+
|
|
22
|
+
> List my workspaces, find the document titled "Launch Plan", and show me its current structure before editing anything.
|
|
23
|
+
|
|
24
|
+
## 2. Create a document and place it in the sidebar tree
|
|
25
|
+
|
|
26
|
+
Use when:
|
|
27
|
+
|
|
28
|
+
- you need a new document under an existing parent doc
|
|
29
|
+
- you want the new page to be visible in the workspace tree immediately
|
|
30
|
+
|
|
31
|
+
Typical tool sequence:
|
|
32
|
+
|
|
33
|
+
1. `search_docs` to find the parent
|
|
34
|
+
2. `create_doc` or `create_doc_from_markdown`
|
|
35
|
+
3. `move_doc` if you created the doc before deciding its final parent
|
|
36
|
+
4. `list_children` to verify placement
|
|
37
|
+
|
|
38
|
+
Prompt example:
|
|
39
|
+
|
|
40
|
+
> Create a document called "Q2 Notes" under "Team Wiki", then verify that it appears as a child page.
|
|
41
|
+
|
|
42
|
+
## 3. Find documents by tag or title and clean up metadata
|
|
43
|
+
|
|
44
|
+
Use when:
|
|
45
|
+
|
|
46
|
+
- titles are inconsistent
|
|
47
|
+
- tags exist but you are not sure which document to open
|
|
48
|
+
|
|
49
|
+
Typical tool sequence:
|
|
50
|
+
|
|
51
|
+
1. `list_tags`
|
|
52
|
+
2. `list_docs_by_tag` or `search_docs` with `tag`
|
|
53
|
+
3. `update_doc_title`
|
|
54
|
+
4. `add_tag_to_doc` or `remove_tag_from_doc`
|
|
55
|
+
|
|
56
|
+
Prompt example:
|
|
57
|
+
|
|
58
|
+
> Show me documents related to onboarding, rename the outdated title, and make sure the page has the `Onboarding` tag.
|
|
59
|
+
|
|
60
|
+
## 4. Append content or replace the main note
|
|
61
|
+
|
|
62
|
+
Use when:
|
|
63
|
+
|
|
64
|
+
- you need to add content incrementally
|
|
65
|
+
- you already have Markdown content to import
|
|
66
|
+
|
|
67
|
+
Typical tool sequence:
|
|
68
|
+
|
|
69
|
+
1. `read_doc`
|
|
70
|
+
2. `append_block` or `append_markdown`
|
|
71
|
+
3. `replace_doc_with_markdown` only when you intend to overwrite the main note
|
|
72
|
+
|
|
73
|
+
Prompt example:
|
|
74
|
+
|
|
75
|
+
> Read the current document, append a short release checklist section, and leave the existing content intact.
|
|
76
|
+
|
|
77
|
+
## 5. Work with database blocks
|
|
78
|
+
|
|
79
|
+
Use when:
|
|
80
|
+
|
|
81
|
+
- you want to inspect or update an AFFiNE database
|
|
82
|
+
- you need to add rows or change schema
|
|
83
|
+
|
|
84
|
+
Typical tool sequence:
|
|
85
|
+
|
|
86
|
+
1. `read_doc` to inspect the page structure
|
|
87
|
+
2. `read_database_columns` to inspect schema
|
|
88
|
+
3. `add_database_column` if needed
|
|
89
|
+
4. `add_database_row`
|
|
90
|
+
5. `update_database_row`
|
|
91
|
+
6. `read_database_cells` to verify
|
|
92
|
+
|
|
93
|
+
Prompt example:
|
|
94
|
+
|
|
95
|
+
> Inspect the task database on this page, add a `Due Date` column if it is missing, then add a new task row and verify the final values.
|
|
96
|
+
|
|
97
|
+
## 6. Review comments and resolve them
|
|
98
|
+
|
|
99
|
+
Use when:
|
|
100
|
+
|
|
101
|
+
- you need to triage feedback on a document
|
|
102
|
+
- you want to update or resolve comments after an edit
|
|
103
|
+
|
|
104
|
+
Typical tool sequence:
|
|
105
|
+
|
|
106
|
+
1. `list_comments`
|
|
107
|
+
2. `create_comment` or `update_comment`
|
|
108
|
+
3. `resolve_comment`
|
|
109
|
+
|
|
110
|
+
Prompt example:
|
|
111
|
+
|
|
112
|
+
> List unresolved comments on the document, summarize them, and resolve the ones that are already addressed in the current draft.
|
|
113
|
+
|
|
114
|
+
## 7. Publish a document or revoke public access
|
|
115
|
+
|
|
116
|
+
Use when:
|
|
117
|
+
|
|
118
|
+
- you are moving a document from draft to public view
|
|
119
|
+
- you need to remove public access after review
|
|
120
|
+
|
|
121
|
+
Typical tool sequence:
|
|
122
|
+
|
|
123
|
+
1. `get_doc`
|
|
124
|
+
2. `publish_doc` or `revoke_doc`
|
|
125
|
+
3. `get_doc` again to confirm visibility state
|
|
126
|
+
|
|
127
|
+
Prompt example:
|
|
128
|
+
|
|
129
|
+
> Publish the final release notes page and confirm that public access is enabled.
|
|
130
|
+
|
|
131
|
+
## 8. Export, duplicate, or clean up linked structure
|
|
132
|
+
|
|
133
|
+
Use when:
|
|
134
|
+
|
|
135
|
+
- you need a markdown backup
|
|
136
|
+
- you want to recreate a page from Markdown
|
|
137
|
+
- you need to inspect linked child pages
|
|
138
|
+
|
|
139
|
+
Typical tool sequence:
|
|
140
|
+
|
|
141
|
+
1. `export_doc_markdown`
|
|
142
|
+
2. `create_doc_from_markdown` if you need a Markdown-based copy
|
|
143
|
+
3. `list_children` if you need structural context
|
|
144
|
+
|
|
145
|
+
Prompt example:
|
|
146
|
+
|
|
147
|
+
> Export the template page as Markdown, create a copy under the current parent, and verify the copied page's child links.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "affine-mcp-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Model Context Protocol server for AFFiNE - enables AI assistants to interact with AFFiNE workspaces, documents, and collaboration features.",
|
|
@@ -47,6 +47,13 @@
|
|
|
47
47
|
"test:db-schema": "node tests/test-database-schema.mjs",
|
|
48
48
|
"test:data-view": "node tests/test-data-view.mjs",
|
|
49
49
|
"test:doc-discovery": "node tests/test-doc-discovery.mjs",
|
|
50
|
+
"test:create-placement": "node tests/test-create-placement.mjs",
|
|
51
|
+
"test:surface-elements": "node tests/test-surface-elements.mjs",
|
|
52
|
+
"test:surface-element-gating": "node scripts/verify-surface-element-gating.mjs",
|
|
53
|
+
"test:edgeless-seed": "node tests/test-edgeless-canvas-setup.mjs",
|
|
54
|
+
"test:edgeless-ui": "npx playwright test tests/playwright/verify-edgeless-canvas.pw.ts --config tests/playwright/playwright.config.ts",
|
|
55
|
+
"test:capabilities-fidelity": "node tests/test-capabilities-fidelity.mjs",
|
|
56
|
+
"test:native-template": "node tests/test-native-template-instantiation.mjs",
|
|
50
57
|
"test:data-view-ui": "npx playwright test tests/playwright/verify-data-view.pw.ts --config tests/playwright/playwright.config.ts",
|
|
51
58
|
"test:bearer": "node tests/test-bearer-auth.mjs",
|
|
52
59
|
"test:http-email-password": "node tests/test-http-email-password.mjs",
|
|
@@ -65,7 +72,9 @@
|
|
|
65
72
|
"files": [
|
|
66
73
|
"bin",
|
|
67
74
|
"dist",
|
|
75
|
+
"docs",
|
|
68
76
|
"README.md",
|
|
77
|
+
"tool-manifest.json",
|
|
69
78
|
"LICENSE"
|
|
70
79
|
],
|
|
71
80
|
"engines": {
|
|
@@ -84,7 +93,7 @@
|
|
|
84
93
|
"markdown-it": "^14.1.0",
|
|
85
94
|
"node-fetch": "^3.3.2",
|
|
86
95
|
"socket.io-client": "^4.8.1",
|
|
87
|
-
"undici": "^
|
|
96
|
+
"undici": "^8.0.2",
|
|
88
97
|
"yjs": "^13.6.27",
|
|
89
98
|
"zod": "^3.23.8"
|
|
90
99
|
},
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.0",
|
|
3
|
+
"tools": [
|
|
4
|
+
"add_database_column",
|
|
5
|
+
"add_database_row",
|
|
6
|
+
"add_doc_to_collection",
|
|
7
|
+
"add_organize_link",
|
|
8
|
+
"add_surface_element",
|
|
9
|
+
"add_tag_to_doc",
|
|
10
|
+
"analyze_doc_fidelity",
|
|
11
|
+
"append_block",
|
|
12
|
+
"append_markdown",
|
|
13
|
+
"append_semantic_section",
|
|
14
|
+
"cleanup_blobs",
|
|
15
|
+
"compose_database_from_intent",
|
|
16
|
+
"create_collection",
|
|
17
|
+
"create_comment",
|
|
18
|
+
"create_doc",
|
|
19
|
+
"create_doc_from_markdown",
|
|
20
|
+
"create_folder",
|
|
21
|
+
"create_semantic_page",
|
|
22
|
+
"create_tag",
|
|
23
|
+
"create_workspace",
|
|
24
|
+
"create_workspace_blueprint",
|
|
25
|
+
"current_user",
|
|
26
|
+
"delete_blob",
|
|
27
|
+
"delete_block",
|
|
28
|
+
"delete_collection",
|
|
29
|
+
"delete_comment",
|
|
30
|
+
"delete_database_row",
|
|
31
|
+
"delete_doc",
|
|
32
|
+
"delete_folder",
|
|
33
|
+
"delete_organize_link",
|
|
34
|
+
"delete_surface_element",
|
|
35
|
+
"delete_workspace",
|
|
36
|
+
"export_doc_markdown",
|
|
37
|
+
"export_with_fidelity_report",
|
|
38
|
+
"generate_access_token",
|
|
39
|
+
"get_capabilities",
|
|
40
|
+
"get_collection",
|
|
41
|
+
"get_doc",
|
|
42
|
+
"get_edgeless_canvas",
|
|
43
|
+
"get_orphan_docs",
|
|
44
|
+
"get_workspace",
|
|
45
|
+
"inspect_template_structure",
|
|
46
|
+
"instantiate_template_native",
|
|
47
|
+
"list_access_tokens",
|
|
48
|
+
"list_children",
|
|
49
|
+
"list_collections",
|
|
50
|
+
"list_comments",
|
|
51
|
+
"list_docs",
|
|
52
|
+
"list_docs_by_tag",
|
|
53
|
+
"list_histories",
|
|
54
|
+
"list_notifications",
|
|
55
|
+
"list_organize_nodes",
|
|
56
|
+
"list_surface_elements",
|
|
57
|
+
"list_tags",
|
|
58
|
+
"list_workspace_tree",
|
|
59
|
+
"list_workspaces",
|
|
60
|
+
"move_doc",
|
|
61
|
+
"move_organize_node",
|
|
62
|
+
"publish_doc",
|
|
63
|
+
"read_all_notifications",
|
|
64
|
+
"read_database_cells",
|
|
65
|
+
"read_database_columns",
|
|
66
|
+
"read_doc",
|
|
67
|
+
"remove_doc_from_collection",
|
|
68
|
+
"remove_tag_from_doc",
|
|
69
|
+
"rename_folder",
|
|
70
|
+
"replace_doc_with_markdown",
|
|
71
|
+
"resolve_comment",
|
|
72
|
+
"revoke_access_token",
|
|
73
|
+
"revoke_doc",
|
|
74
|
+
"search_docs",
|
|
75
|
+
"sign_in",
|
|
76
|
+
"update_collection",
|
|
77
|
+
"update_collection_rules",
|
|
78
|
+
"update_comment",
|
|
79
|
+
"update_database_row",
|
|
80
|
+
"update_doc_title",
|
|
81
|
+
"update_edgeless_block",
|
|
82
|
+
"update_frame_children",
|
|
83
|
+
"update_profile",
|
|
84
|
+
"update_settings",
|
|
85
|
+
"update_surface_element",
|
|
86
|
+
"update_workspace",
|
|
87
|
+
"upload_blob"
|
|
88
|
+
]
|
|
89
|
+
}
|