@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
@@ -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,200 @@
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
+ | `update_folder_icon` | Set or clear a folder's sidebar icon (emoji or named icon) | Experimental |
43
+ | `get_folder_icon` | Read a folder's current sidebar icon | Experimental |
44
+ | `delete_folder` | Delete a folder recursively | Experimental and destructive |
45
+ | `move_organize_node` | Move a folder or link node | Experimental |
46
+ | `add_organize_link` | Add a doc, tag, or collection link under a folder | Experimental |
47
+ | `delete_organize_link` | Delete a doc, tag, or collection link | Experimental and destructive |
48
+
49
+ ## Documents
50
+
51
+ ### Discovery and metadata
52
+
53
+ | Tool | Purpose | Notes |
54
+ | --- | --- | --- |
55
+ | `list_docs` | List documents with pagination | Includes `node.tags` |
56
+ | `list_tags` | List all tags in a workspace | |
57
+ | `search_docs` | Search titles with substring, prefix, or exact matching | Supports tag filter and updatedAt sorting |
58
+ | `list_docs_by_tag` | List documents with a specific tag | |
59
+ | `get_doc` | Read document metadata | |
60
+ | `read_doc` | Read block content and plain text snapshot | WebSocket-backed; block rows include `linkedDocIds` for inline LinkedPage references |
61
+ | `get_capabilities` | Inspect the server's high-level authoring and fidelity capabilities | Useful for adaptive clients |
62
+ | `analyze_doc_fidelity` | Analyze how a document maps to Markdown and which native AFFiNE structures are lossy | Good before export or migration |
63
+ | `list_children` | List direct child docs linked from a document | |
64
+
65
+ ### Publish and visibility
66
+
67
+ | Tool | Purpose | Notes |
68
+ | --- | --- | --- |
69
+ | `publish_doc` | Make a document public | |
70
+ | `revoke_doc` | Revoke public access | |
71
+
72
+ ### Create, duplicate, and move
73
+
74
+ | Tool | Purpose | Notes |
75
+ | --- | --- | --- |
76
+ | `create_doc` | Create a new document | WebSocket-backed |
77
+ | `create_doc_from_markdown` | Create a document from Markdown content | |
78
+ | `inspect_template_structure` | Inspect a template's native AFFiNE structure and native-clone support | Helps choose a clone strategy |
79
+ | `instantiate_template_native` | Instantiate a template via native AFFiNE block cloning, with optional Markdown fallback | Higher-fidelity than Markdown-only cloning |
80
+ | `move_doc` | Move a document in the sidebar by relinking it under another parent | |
81
+ | `delete_doc` | Delete a document | WebSocket-backed and destructive |
82
+
83
+ ### Content editing
84
+
85
+ | Tool | Purpose | Notes |
86
+ | --- | --- | --- |
87
+ | `update_doc_title` | Rename a document in workspace metadata and in the page block | |
88
+ | `update_doc_icon` | Set or clear a document's sidebar icon (emoji or named icon) | |
89
+ | `get_doc_icon` | Read a document's current sidebar icon | |
90
+ | `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. |
91
+ | `create_semantic_page` | Create an AFFiNE-native page with an intentional section skeleton and native block composition | High-level authoring helper |
92
+ | `append_semantic_section` | Append a semantic section to an existing page by heading title | High-level authoring helper |
93
+ | `append_markdown` | Append Markdown content to an existing document | |
94
+ | `replace_doc_with_markdown` | Replace the main note content with Markdown | Overwrites main note content |
95
+
96
+ ### Tags
97
+
98
+ | Tool | Purpose | Notes |
99
+ | --- | --- | --- |
100
+ | `create_tag` | Create a reusable workspace-level tag | |
101
+ | `add_tag_to_doc` | Attach a tag to a document | |
102
+ | `remove_tag_from_doc` | Detach a tag from a document | |
103
+
104
+ ### Custom properties
105
+
106
+ | Tool | Purpose | Notes |
107
+ | --- | --- | --- |
108
+ | `list_doc_properties` | List workspace custom-property definitions and a document's current values | WebSocket-backed; reads the `db$docProperties` / `db$docCustomPropertyInfo` sub-docs |
109
+ | `create_custom_property` | Create a workspace-wide custom property definition | Types: `text`, `number`, `checkbox`, `date`. Returns the `propertyId` |
110
+ | `delete_custom_property` | Soft-delete a custom property definition by id or name | Destructive; existing values are hidden |
111
+ | `set_doc_property` | Set a document's custom property value by property id or name | Value validated per type (`checkbox` boolean, `number`, `date` `YYYY-MM-DD`, `text`) |
112
+ | `clear_doc_property` | Remove a custom property value from a document | |
113
+
114
+ ### Markdown export
115
+
116
+ | Tool | Purpose | Notes |
117
+ | --- | --- | --- |
118
+ | `export_doc_markdown` | Export document content as Markdown | Useful for backup and automation |
119
+ | `export_with_fidelity_report` | Export a document with a machine-readable fidelity report | Useful when native AFFiNE structures matter |
120
+
121
+ ## Database blocks
122
+
123
+ | Tool | Purpose | Notes |
124
+ | --- | --- | --- |
125
+ | `compose_database_from_intent` | Create or enrich a database block from a high-level schema intent | Useful for project boards and structured tables |
126
+ | `add_database_column` | Add a column to a database block | Supports `rich-text`, `select`, `multi-select`, `number`, `checkbox`, `link`, and `date` |
127
+ | `add_database_row` | Add a row to a database block | Can set the built-in title field |
128
+ | `delete_database_row` | Delete a row by row block id | Destructive |
129
+ | `read_database_columns` | Read schema metadata, types, options, and view mappings | Useful before edits |
130
+ | `read_database_cells` | Read row titles and decoded cell values | Supports row and column filters |
131
+ | `update_database_row` | Update multiple cells on a row at once | `createOption` defaults to `true` |
132
+
133
+ ## Edgeless canvas and surface elements
134
+
135
+ 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.
136
+
137
+ | Tool | Purpose | Notes |
138
+ | --- | --- | --- |
139
+ | `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. |
140
+ | `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. |
141
+ | `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. |
142
+ | `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. |
143
+ | `delete_surface_element` | Delete an element by id | `pruneConnectors: true` additionally removes any connectors referencing the deleted element. |
144
+ | `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). |
145
+ | `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. |
146
+ | `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`. |
147
+
148
+ ### Layout helpers on `append_block`
149
+
150
+ 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:
151
+
152
+ | Field | Applies to | Purpose |
153
+ | --- | --- | --- |
154
+ | `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. |
155
+ | `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. |
156
+ | `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. |
157
+ | `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. |
158
+
159
+ ## Comments
160
+
161
+ | Tool | Purpose | Notes |
162
+ | --- | --- | --- |
163
+ | `list_comments` | List comments on a document | |
164
+ | `create_comment` | Create a comment on a document | |
165
+ | `update_comment` | Update comment content | |
166
+ | `delete_comment` | Delete a comment | Destructive |
167
+ | `resolve_comment` | Resolve or unresolve a comment | |
168
+
169
+ ## Version History
170
+
171
+ | Tool | Purpose | Notes |
172
+ | --- | --- | --- |
173
+ | `list_histories` | List document history timestamps | |
174
+
175
+ ## Users and tokens
176
+
177
+ | Tool | Purpose | Notes |
178
+ | --- | --- | --- |
179
+ | `current_user` | Return the current signed-in user | |
180
+ | `sign_in` | Sign in with email and password | Self-hosted flows only for direct programmatic sign-in |
181
+ | `update_profile` | Update current user profile data | |
182
+ | `update_settings` | Update user notification preferences | |
183
+ | `list_access_tokens` | List personal access tokens | |
184
+ | `generate_access_token` | Create a personal access token | Sensitive operation |
185
+ | `revoke_access_token` | Revoke a personal access token | Destructive |
186
+
187
+ ## Notifications
188
+
189
+ | Tool | Purpose | Notes |
190
+ | --- | --- | --- |
191
+ | `list_notifications` | List notifications for the current user | |
192
+ | `read_all_notifications` | Mark notifications as read | |
193
+
194
+ ## Blob storage
195
+
196
+ | Tool | Purpose | Notes |
197
+ | --- | --- | --- |
198
+ | `upload_blob` | Upload a file or blob to workspace storage | |
199
+ | `delete_blob` | Delete a blob from workspace storage | Destructive |
200
+ | `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.