Skip to main content
Glama

AFFiNE MCP Server

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.

Version MCP SDK CI License

Table of Contents

Related MCP server: mcp-server-ispace-wiki

Overview

AFFiNE MCP Server is designed for three common scenarios:

  • Run a local stdio MCP server for Claude Code, Codex CLI, Cursor, or Claude Desktop

  • Expose a remote HTTP MCP endpoint for hosted or browser-connected clients

  • Automate AFFiNE workspace, document, database, organization, and comment workflows through a stable MCP tool surface

Highlights:

  • Supports AFFiNE Cloud and self-hosted AFFiNE instances

  • Supports stdio and HTTP transports

  • Coordinates concurrent writes per workspace through one shared MCP server; optional document revisions reject stale edits

  • Supports session-cookie and email/password authentication, plus compatible bearer tokens for older deployments

  • Exposes 112 canonical MCP tools backed by AFFiNE GraphQL, WebSocket, and REST APIs

  • Reads native database created-by (user ID) and created-time (timestamp) columns from row metadata; missing or invalid metadata returns null without backfilling it

  • Creates and updates code blocks with optional native preview state, with language and preview readback

  • Places, resizes, and connects images on edgeless canvases, with blob identifiers and geometry in readback

  • Appends canvas frames in creation-order presentation sequence while preserving their native background layering

  • Sets and clears native AFFiNE Journal dates with set_doc_journal

  • Includes semantic page composition, native template instantiation, database intent composition, capability and fidelity reporting, and workspace blueprint helpers

  • Includes Docker images, health probes, and end-to-end test coverage

Scope boundaries:

  • This server can access only server-backed AFFiNE workspaces

  • Browser-local workspaces stored only in local storage are not available through AFFiNE server APIs

  • After a content write, transient workspace timestamp failures are retried up to three times with backoff. If metadata still cannot be confirmed, inspect the saved document before any repair; do not repeat the content edit. See document mutation recovery.

  • AFFiNE 0.27+ removed the legacy personal-access-token GraphQL API; this server no longer exposes token-management tools

  • AFFiNE Cloud requires browser-session authentication for this external GraphQL integration; programmatic email/password sign-in is blocked by Cloudflare

  • delete_doc removes workspace page metadata and requests content deletion. Some AFFiNE servers retain content despite acknowledging the request; the tool returns a partial result with a warning unless content absence is verified. Use trash_doc for recoverable cleanup.

New in v3.2.1: Scripted cookie login now keeps session secrets out of process arguments, validates workspace access before saving credentials, and restores document pagination for ordinary workspace members.

Choose Your Path

Goal

Start here

Set up a local stdio server with the least friction

docs/getting-started.md

Run the server in Docker or another OCI runtime

docs/getting-started.md#path-c-run-from-the-docker-image

Configure Claude Code, Claude Desktop, Codex CLI, or Cursor

docs/client-setup.md

Let multiple agents write through one coordinated server

Concurrent writes

Run the server remotely over HTTP or behind OAuth

docs/configuration-and-deployment.md

Lock down tool exposure for least-privilege deployments

docs/configuration-and-deployment.md#least-privilege-tool-exposure

Learn common AFFiNE workflows and tool sequences

docs/workflow-recipes.md

Browse the tool catalog by domain

docs/tool-reference.md

Quick Start

1. Install the CLI

npm i -g affine-mcp-server
affine-mcp --version

You can also run the package ad hoc:

npx -y -p affine-mcp-server affine-mcp -- --version

2. Or run the server in Docker

docker run -d \
  -p 3000:3000 \
  -e MCP_TRANSPORT=http \
  -e AFFINE_BASE_URL=https://your-affine-instance.com \
  -e AFFINE_EMAIL=you@example.com \
  -e AFFINE_PASSWORD=your-password \
  -e AFFINE_MCP_AUTH_MODE=bearer \
  -e AFFINE_MCP_HTTP_TOKEN=your-strong-secret \
  ghcr.io/dawncr0w/affine-mcp-server:3.13.0

Then point your client at:

{
  "mcpServers": {
    "affine": {
      "type": "http",
      "url": "http://localhost:3000/mcp",
      "headers": {
        "Authorization": "Bearer your-strong-secret"
      }
    }
  }
}

For Docker, health checks, and remote deployment details, see docs/configuration-and-deployment.md#docker.

HTTP deployments have two environment-only session limits:

Variable

Default

Purpose

AFFINE_MCP_HTTP_MAX_SESSIONS

32

Combined Streamable HTTP and legacy SSE session capacity, including sessions being initialized

AFFINE_MCP_HTTP_SESSION_IDLE_TIMEOUT_MS

1800000 (30 minutes)

Idle time before an inactive session is closed

Short-lived Streamable HTTP clients, including cron jobs, should send DELETE /mcp with their Mcp-Session-Id and authentication headers when finished. Exiting the client process alone does not terminate its server-side session. A long idle timeout can let abandoned sessions fill the limit and cause 503 / -32002 errors. Close unused sessions and choose an appropriate idle timeout before raising the cap. See session capacity troubleshooting.

3. Save credentials with interactive login

affine-mcp login

This stores credentials in $XDG_CONFIG_HOME/affine-mcp/config when XDG_CONFIG_HOME is set, otherwise in ~/.config/affine-mcp/config, with mode 600.

The login flow reuses the configured or saved AFFiNE URL when you press Enter at the URL prompt. After authentication, workspace discovery displays the workspace name first and falls back to Workspace name unavailable when profile metadata has no name; the full ID remains visible. An invalid numeric selection is rejected and prompted again. Enter q or send end-of-file to cancel without writing a new config. A failed discovery request is reported as a failure; it is not treated as a completed login.

  • For AFFiNE Cloud, paste the Cookie request header from a signed-in browser session

  • For self-hosted AFFiNE, use email/password (recommended) or a signed-in session cookie

  • AFFINE_API_TOKEN remains available only for deployments that still accept a compatible GraphQL bearer token

The prompt defaults to AFFINE_BASE_URL from the environment or the saved config file, so pressing Enter keeps an already-configured self-hosted URL instead of switching back to AFFiNE Cloud.

For a self-hosted instance reached over plain HTTP on a trusted private network, AFFINE_ALLOW_INSECURE_HTTP=true must be set for the login run as well as for the server. The opt-in is read from the environment first and then from the saved config file.

To avoid re-running login when a session expires, persist the account credentials instead of the session cookie:

affine-mcp login --save-credentials

With the email/password method, this stores AFFINE_EMAIL and AFFINE_PASSWORD so the server signs in on its own and renews the session before it expires. The password is written to the mode-600 config file, so use a dedicated least-privilege AFFiNE account. Without this flag the CLI keeps storing only the session credential, which never renews by itself.

For scripted session-cookie setup, keep the cookie out of process arguments:

affine-mcp login --url https://app.affine.pro --cookie-stdin --workspace-id your-workspace-id --force

Paste the cookie at the hidden prompt, or pipe it from a trusted secret source. The CLI verifies --workspace-id against the authenticated account before saving it. Piped input requires --force when existing credentials would be replaced.

After login, inspect and switch the saved default workspace without signing in again:

affine-mcp workspaces
affine-mcp workspaces --json
affine-mcp workspace <workspace-id>

workspaces lists names first and marks the current default. workspace validates the requested ID with the active credentials and changes only the local default (AFFINE_WORKSPACE_ID); it does not grant access to a workspace or change the authenticated account. With no ID, it uses the same validated selection flow.

4. Register the server with your client

Claude Code project config:

{
  "mcpServers": {
    "affine": {
      "command": "affine-mcp"
    }
  }
}

Codex CLI:

codex mcp add affine -- affine-mcp

More client-specific setup is in docs/client-setup.md.

5. Verify the connection

affine-mcp status
affine-mcp doctor
affine-mcp snippet codex

The recommended Codex form is a command-only registration line that keeps using the current saved login. snippet claude and snippet cursor print JSON configuration instead. Apply the generated snippet, then restart or reconnect the MCP client so it starts a fresh server process.

For an explicit environment snapshot, add --env to any snippet command. It copies the currently resolved URL, authentication, headers, workspace, and relevant OAuth settings; client environment variables win over saved config at runtime. Remove or regenerate copied credentials when they expire instead of expecting a saved login to override them.

If you want to expose the server remotely over HTTP instead of stdio, start with docs/configuration-and-deployment.md. If an HTTP server already runs on the same host as your stdio client, use the private stdio HTTP bridge instead of starting another full server process.

Compatibility Matrix

Node.js 20.18.1 is the minimum supported runtime. CI validates the Node.js 20, 22, 24, and 26 release lines.

Target

Transport

Recommended auth

Recommended path

Claude Code

stdio

Saved config

docs/client-setup.md#claude-code

Claude Desktop

stdio

Saved config or session cookie

docs/client-setup.md#claude-desktop

Codex CLI

stdio

Saved config or self-hosted email/password

docs/client-setup.md#codex-cli

Cursor

stdio

Saved config or session cookie

docs/client-setup.md#cursor

Containerized remote deployment

HTTP

Bearer token or OAuth

docs/getting-started.md#path-c-run-from-the-docker-image

Remote MCP clients

HTTP

Bearer token or OAuth

docs/configuration-and-deployment.md#http-mode

AFFiNE Cloud

stdio or HTTP

Signed-in browser session cookie

docs/configuration-and-deployment.md#auth-strategy-matrix

Self-hosted AFFiNE

stdio or HTTP

Email/password or session cookie

docs/configuration-and-deployment.md#auth-strategy-matrix

Tool Surface

tool-manifest.json is the source of truth for canonical tool names. The MCP server exposes those tools through tools/list and tools/call; tool definitions returned by tools/list include MCP annotations that mark read-only, destructive, idempotent, and external-world behavior for client-side tool selection.

Every canonical tool also declares an MCP outputSchema for its structuredContent. Object results retain their existing top-level fields, while array and scalar results use stable { items }, { text }, or { value } envelopes. The existing text content remains unchanged for compatibility with clients that do not consume structured results.

get_capabilities exposes the full implemented list in server.supportedTools and the currently enabled surface in server.effective.profile and server.effective.enabledTools, after profiles, disabled groups or tools, and auth-mode policy. Inspect tools/list when you need the final set of callable tools.

Advertised input and output schemas omit the SDK-generated draft-07 $schema marker. Schema interpretation follows the client context, allowing clients that reject an explicit draft-07 declaration to consume the tool surface.

Domains:

  • Workspace: create, inspect, update, delete, and traverse workspaces

  • Organization: collections, collection-rule sync, workspace blueprints, and experimental organize or folder helpers

  • 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

  • Databases: create columns, add rows, update rows, inspect schema, add table/kanban views, and compose database structures from intent

  • Comments: list, create, update, delete, and resolve

  • History: version history listing

  • Users and authentication: current user, sign-in, and profile/settings

  • Notifications: list and mark notifications as read

  • Blob storage: upload, delete, and cleanup blobs

For new document content, use create_doc for an optional single plain-text paragraph and create_doc_from_markdown for native headings, lists, links, code blocks, and $$...$$/$...$ math equations. Both tools accept folderId for immediate organize-folder placement. New pages record the authenticated AFFiNE account in the app's Created by property, including semantic pages, template instances, and workspace welcome pages. Existing creator records are preserved; older pages are not backfilled. New database rows also record the authenticated AFFiNE account and creation time, including rows composed from intent or inserted as paragraph blocks. Editing or moving existing rows preserves their creation metadata; older rows are not backfilled.

Use AFFINE_TOOL_PROFILE=read_only, core, or authoring when a deployment should expose a smaller surface than the complete full default. This is the recommended path for hosted, browser-connected, or least-privilege deployments because it reduces agent choice overload while keeping the full tool catalog available as an opt-in surface. You can also combine profiles with AFFINE_DISABLED_GROUPS such as docs.database, destructive, or admin for finer control.

Full-note replacement with replace_doc_with_markdown is destructive and requires full without disabling the destructive group. core and authoring retain incremental editing through append_markdown and update_block.

For the grouped catalog, notes, and operational caveats, see docs/tool-reference.md.

Documentation Map

Document

Purpose

docs/getting-started.md

First-run setup paths and verification

docs/client-setup.md

Client-specific configuration snippets and tips

docs/configuration-and-deployment.md

Environment variables, auth modes, Docker, HTTP mode, and deployment guidance

docs/workflow-recipes.md

End-to-end workflows and example tool sequences

docs/tool-reference.md

Tool catalog grouped by domain

docs/edgeless-canvas-cookbook.md

Edgeless canvas layout helpers and surface elements, worked end-to-end

CONTRIBUTING.md

Contributor workflow

SECURITY.md

Security reporting

Verify Your Setup

Useful CLI commands:

  • affine-mcp status - test the effective configuration

  • affine-mcp status --json - machine-readable status output

  • affine-mcp doctor - diagnose config and connectivity issues

  • affine-mcp workspaces [--json] - list accessible workspaces by name and mark the default

  • affine-mcp workspace [id] - validate and set the local default workspace

  • affine-mcp show-config - print the effective config with secrets redacted

  • affine-mcp config-path - print the config file path

  • affine-mcp snippet <claude|cursor|codex|all> [--env] - generate ready-to-paste client config

  • affine-mcp logout - remove stored credentials

Core configuration uses environment > saved config > defaults. HTTP body/session limits, proxy settings, WebSocket timeouts, and the HTTP compatibility escape hatches are environment-only runtime flags; they are not read from the saved KEY=value file. See configuration precedence and environment scope. For a self-hosted deployment with a non-standard GraphQL route, use affine-mcp login --graphql-path /your/graphql/path or set AFFINE_GRAPHQL_PATH; show-config --json prints the exact resolved graphqlEndpoint without exposing secrets. Generated workspace and document URLs use the configured AFFiNE base URL, so a custom GraphQL path does not become part of a browser link.

For common failures, see:

Security and Scope

  • Never commit passwords, session cookies, or compatible bearer tokens

  • Use a dedicated least-privilege AFFiNE account for unattended deployments

  • Email/password HTTP sessions share one login and never fall back to anonymous backend requests after authentication failure

  • Use HTTPS for non-local deployments

  • Keep remote HTTP MCP listeners authenticated; bearer mode refuses a non-loopback bind without AFFINE_MCP_HTTP_TOKEN

  • Send MCP bearer tokens in the Authorization header, never in the URL

  • Re-run affine-mcp login when a saved browser session expires

  • For GUI clients, verify command -v affine-mcp and command -v node; an absolute script path does not fix a #!/usr/bin/env node shebang when the GUI process has no Node.js directory in PATH. See GUI client PATH troubleshooting.

  • Restrict exposed tools with AFFINE_DISABLED_GROUPS and AFFINE_DISABLED_TOOLS for least-privilege setups

  • Treat OAuth mode as a shared AFFiNE service-account deployment: it defaults to read_only, and write-capable profiles require AFFINE_OAUTH_ALLOW_SERVICE_WRITES=true

  • Use /healthz and /readyz when running the HTTP server behind a container platform or load balancer

  • Set HTTP body, session, idle, and shutdown limits explicitly for high-volume deployments

Development

Run the main quality gates before opening a PR:

npm run ci

Additional validation:

  • npm test verifies tool metadata, test-suite coverage, and the fast regression suite without requiring a live AFFiNE instance

  • npm run test:comprehensive boots a local Docker AFFiNE stack and validates the tool surface

  • npm run test:e2e runs Docker, MCP, and Playwright together

  • npm run test:playwright runs the Playwright suite only

  • 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, npm run test:mutation-ack, 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

Live tests can mutate or delete AFFiNE data. They allow loopback targets by default and refuse non-loopback targets unless the disposable target is explicitly enabled and confirmed as documented in CONTRIBUTING.md. Never run them against production.

Local clone flow:

git clone https://github.com/dawncr0w/affine-mcp-server.git
cd affine-mcp-server
npm install
npm run build
node dist/index.js

Release Notes

License

MIT License - see LICENSE.

Support

Acknowledgments

Available Tools

95 tools
add_database_columnAdd Database ColumnA

Add a column to an existing AFFiNE database block. Supports rich-text, select, multi-select, number, checkbox, link, date types.

Inputs: name (required), docId (required), databaseBlockId (required), type, +3 more.

add_database_rowAdd Database RowA

Add a row to an AFFiNE database block. Provide cell values mapped by column name or column ID. Title column text is stored on the row paragraph block. Select columns auto-create options by label.

Inputs: cells (required), docId (required), databaseBlockId (required), linkedDocId, +1 more.

add_doc_to_collectionAdd Doc To CollectionA

Add an existing document id to an AFFiNE collection allow-list. Use update_collection_rules instead when membership should be rule-driven.

Inputs: docId (required), collectionId (required), workspaceId.

add_surface_elementAdd Surface ElementA

Add a shape, connector, text, or group to the AFFiNE edgeless canvas surface. Shapes support rect/ellipse/diamond/triangle with fill, stroke, and text. Connectors draw arrows between shapes (by id) or between absolute points. Use for building diagrams programmatically. Style fields that don't apply to the chosen element type are reported in the response 'ignored' list (mirrors update_surface_element).

Inputs: type (required), docId (required), x, y, +26 more.

add_tag_to_docAdd Tag To DocumentA

Attach a workspace tag to a document, creating the workspace tag option if needed. This updates workspace metadata and attempts to sync the document's own metadata.

Inputs: tag (required), docId (required), workspaceId.

analyze_doc_fidelityAnalyze Document FidelityA
Read-onlyIdempotent

Inspect a document for markdown export fidelity risk, including unsupported AFFiNE block flavours and risky content paths.

Inputs: docId (required), workspaceId.

append_blockAppend BlockA

Append document blocks with canonical types and legacy aliases (supports placement + strict validation).

Inputs: type (required), docId (required), x, y, +33 more.

append_markdownAppend MarkdownB

Append markdown content to an existing AFFiNE document.

Inputs: docId (required), markdown (required), strict, placement, +1 more.

append_semantic_sectionAppend Semantic SectionB

Append a semantic section to an existing AFFiNE document by heading title and native block composition.

Inputs: docId (required), sectionTitle (required), bullets, callouts, +3 more.

cleanup_blobsCleanup Deleted BlobsA
Destructive

Permanently release blobs that were already marked deleted in a workspace. This is destructive cleanup and should be used only after confirming deleted blobs are no longer needed.

Inputs: workspaceId (required).

clear_doc_propertyClear Document PropertyC

Remove a custom property value from a document (property by id or name).

Inputs: docId (required), property (required), workspaceId.

compose_database_from_intentCompose Database From IntentA

Create a useful AFFiNE database/data-view from declarative intent. Supports task_board and issue_tracker presets with starter schema, kanban view, and optional starter rows.

Inputs: docId (required), intent (required), title, seedRows, +2 more.

create_collectionCreate CollectionA

Create a new AFFiNE sidebar collection with optional rules. This writes workspace sidebar metadata but does not create documents.

Inputs: name (required), rules, workspaceId.

create_commentCreate CommentA

Create a new comment on an existing document. This writes collaboration state; use update_comment when editing an existing comment instead.

Inputs: docId (required), content (required), docMode, docTitle, +2 more.

create_custom_propertyCreate Custom PropertyA

Create a workspace-wide custom property definition (text, number, checkbox, or date). Returns its propertyId.

Inputs: name (required), type (required), icon, workspaceId.

create_docCreate DocumentA

Create a new AFFiNE document with optional content. If parentDocId is provided, the new doc is linked into the sidebar tree immediately. If folderId is provided, the doc is placed inside that folder in the sidebar.

Inputs: title, content, folderId, parentDocId, +1 more.

create_doc_from_markdownCreate Document From MarkdownA

Create a new AFFiNE document and import markdown content. Use parentDocId to automatically embed the new doc into a parent, making it visible in the sidebar instead of being an orphan.

Inputs: markdown (required), title, strict, parentDocId, +1 more.

create_folderCreate FolderA

Experimental: create an AFFiNE organize folder node in the sidebar tree. This only changes sidebar organization, not document content.

Inputs: name (required), index, parentId, workspaceId.

create_semantic_pageCreate Semantic PageA

Create an AFFiNE-native page with intentional section structure and native block composition.

Inputs: title, pageType, sections, parentDocId, +1 more.

create_tagCreate TagA

Create a workspace-level tag entry for future reuse. This does not attach the tag to a document; use add_tag_to_doc for that.

Inputs: tag (required), workspaceId.

create_workspaceCreate WorkspaceA

Create a new workspace with initial document (accessible in UI)

Inputs: name (required), avatar.

create_workspace_blueprintCreate Workspace BlueprintA

Create a simple AFFiNE organize folder blueprint with one root folder and optional child folders. This is a convenience wrapper around create_folder.

Inputs: rootFolderName (required), workspaceId, childFolderNames.

current_userCurrent UserA
Read-onlyIdempotent

Return the currently authenticated AFFiNE user profile. Use this read-only check to verify credentials before workspace or document operations.

Inputs: no named parameters.

delete_blobDelete BlobA
Destructive

Delete a blob from AFFiNE workspace storage. Set permanently only when the blob should bypass recoverable deletion.

Inputs: key (required), workspaceId (required), permanently.

delete_blockDelete BlockA
Destructive

Delete a block by id. Removes descendants and unlinks from the parent's sys:children by default; set deleteChildren=false to keep descendants orphaned (for re-parenting), or pruneConnectors=true to also drop surface connectors referencing any deleted id. Refuses affine:page — use delete_doc for whole docs.

Inputs: docId (required), blockId (required), workspaceId, deleteChildren, +1 more.

delete_collectionDelete CollectionA
Destructive

Delete an AFFiNE sidebar collection. This removes the collection metadata but does not delete the documents it referenced.

Inputs: collectionId (required), workspaceId.

delete_commentDelete CommentA
Destructive

Delete an existing comment by id. This is destructive for that comment; use resolve_comment when you only want to mark a thread resolved.

Inputs: id (required).

delete_custom_propertyDelete Custom PropertyA
Destructive

Soft-delete a workspace custom property definition (by propertyId or name). Existing values are hidden.

Inputs: property (required), workspaceId.

delete_database_rowDelete Database RowB
Destructive

Delete a row from an AFFiNE database block.

Inputs: docId (required), rowBlockId (required), databaseBlockId (required), workspaceId.

delete_docDelete DocumentA
Destructive

Delete a document by removing its workspace metadata entry and sending the AFFiNE WebSocket delete request for its content. This is destructive; use revoke_doc when you only need to remove public access.

Inputs: docId (required), workspaceId.

delete_folderDelete FolderA
Destructive

Experimental: delete an AFFiNE organize folder and every nested folder or link node. This is destructive for sidebar organization but does not delete target documents, tags, or collections.

Inputs: folderId (required), workspaceId.

delete_surface_elementDelete Surface ElementA
Destructive

Delete a surface element by id. Set pruneConnectors=true to also delete any connectors whose source or target referenced the deleted element.

Inputs: docId (required), elementId (required), workspaceId, pruneConnectors.

delete_tagDelete TagA
Destructive

Delete a workspace-level tag and remove it from every document that references it. Accepts a tag id or name; an ambiguous name is rejected with the candidate ids.

Inputs: tag (required), workspaceId.

delete_workspaceDelete WorkspaceA
Destructive

Delete a workspace permanently

Inputs: id (required).

export_doc_markdownExport Document MarkdownB
Read-onlyIdempotent

Export AFFiNE document content to markdown.

Inputs: docId (required), workspaceId, includeFrontmatter.

export_with_fidelity_reportExport With Fidelity ReportB
Read-onlyIdempotent

Export document markdown together with a structured fidelity report that highlights markdown loss risk and unsupported AFFiNE-native content.

Inputs: docId (required), workspaceId, includeFrontmatter.

find_doc_by_titleFind Doc by TitleA
Read-onlyIdempotent

Resolve docs by exact title. Returns ALL matches up to limit (callers handle ambiguity). Case-sensitive by default; pass caseInsensitive: true to fold case. Reads workspace metadata — fast, no per-doc fetch. Unlike search_docs (which is always case-insensitive and capped at limit 20), this tool defaults to case-sensitive matching and returns up to limit matches (default 50, max 200). Prefer this over search_docs when you know the exact title and want every match. Returns: { query, caseInsensitive, matches: [{ id, title, createdAt, updatedAt, inTrash }], workspaceDocCount, truncated }.

Inputs: title (required), limit, workspaceId, caseInsensitive.

generate_access_tokenGenerate Access TokenA

Generate a new personal access token and return its one-time secret. This creates a credential; store the returned token securely because it may not be shown again.

Inputs: name (required), expiresAt.

get_capabilitiesGet CapabilitiesA
Read-onlyIdempotent

Return machine-readable capability flags for this MCP server, including block, database, collaboration, and export support.

Inputs: no named parameters.

get_collectionGet CollectionA
Read-onlyIdempotent

Read one AFFiNE sidebar collection by id, including rules and allow-list. Use list_collections first when the id is unknown.

Inputs: collectionId (required), workspaceId.

get_docGet DocumentA
Read-onlyIdempotent

Read GraphQL metadata for one document, such as title, summary, public state, roles, and timestamps. Use read_doc when you need block content.

Inputs: docId (required), workspaceId.

get_doc_iconGet Document IconA
Read-onlyIdempotent

Read the current sidebar icon of a document. Returns null when none is set.

Inputs: docId (required), workspaceId.

get_edgeless_canvasGet Edgeless CanvasA
Read-onlyIdempotent

Read the full edgeless canvas: all edgeless-positioned blocks (notes, frames, edgeless-text) with their xywh, plus all surface elements (shapes, connectors, text, groups). Includes aggregate bounding box and per-type element counts. Use this when you need to understand canvas layout end-to-end before placing new elements.

Inputs: docId (required), workspaceId.

get_folder_iconGet Folder IconA
Read-onlyIdempotent

Read the current sidebar icon of an organize folder. Returns null when none is set. Experimental.

Inputs: folderId (required), workspaceId.

get_orphan_docsGet Orphan DocumentsA
Read-onlyIdempotent

Find all documents that have no parent (not linked from any other doc via embed_linked_doc / embed_synced_doc blocks or inline LinkedPage references). Useful for workspace hygiene. Note: scans all docs — O(n). Each doc includes an inTrash flag.

Inputs: workspaceId.

get_workspaceGet WorkspaceA
Read-onlyIdempotent

Get details of a specific workspace

Inputs: id (required).

inspect_template_structureInspect Template StructureA
Read-onlyIdempotent

Inspect a template doc's native structure, tags, and fallback risk before instantiation.

Inputs: templateDocId (required), workspaceId.

instantiate_template_nativeInstantiate Template NativelyA

Instantiate a template using native AFFiNE block cloning when supported, falling back to markdown materialization only when necessary.

Inputs: templateDocId (required), title, variables, parentDocId, +3 more.

list_access_tokensList Access TokensA
Read-onlyIdempotent

List metadata for the current user's personal access tokens. Token secrets are not returned; use generate_access_token only when a new secret is needed.

Inputs: no named parameters.

list_childrenList Document ChildrenA
Read-onlyIdempotent

List the direct children of a document in the sidebar (embed_linked_doc / embed_synced_doc blocks and inline LinkedPage references). Returns docId, title, URL, and inTrash for each child.

Inputs: docId (required), workspaceId.

list_collectionsList CollectionsA
Read-onlyIdempotent

List AFFiNE sidebar collections and their rules. Use this read-only tool before updating, deleting, or adding documents to a collection.

Inputs: workspaceId.

list_commentsList CommentsA
Read-onlyIdempotent

List paginated comments for a document, including nested replies and resolution state. Use this before update_comment, delete_comment, or resolve_comment when you need the existing comment ids.

Inputs: docId (required), after, first, offset, +1 more.

list_doc_propertiesList Document PropertiesA
Read-onlyIdempotent

List the workspace custom-property definitions and a document's current values for them.

Inputs: docId (required), workspaceId.

list_docsList DocumentsA
Read-onlyIdempotent

List documents in a workspace (GraphQL). Each doc includes an inTrash flag.

Inputs: after, first, offset, workspaceId.

list_docs_by_tagList Documents By TagA
Read-onlyIdempotent

List documents that contain the requested tag. This is read-only and each result includes title metadata and an inTrash flag.

Inputs: tag (required), ignoreCase, workspaceId.

list_historiesList HistoriesB
Read-onlyIdempotent

List doc histories (timestamps) for a doc.

Inputs: guid (required), take, before, workspaceId.

list_notificationsList NotificationsA
Read-onlyIdempotent

List the current user's AFFiNE notifications with optional unread filtering. This is read-only and returns notification ids, read state, level, and timestamps.

Inputs: after, first, offset, unreadOnly.

list_organize_nodesList Organize NodesA
Read-onlyIdempotent

Experimental: list AFFiNE sidebar organize folder and link nodes from the folders workspace DB. Use this before moving or deleting organize nodes.

Inputs: workspaceId.

list_surface_elementsList Surface ElementsA
Read-onlyIdempotent

List all shape/connector/text/group elements on the AFFiNE edgeless canvas surface. Returns raw xywh strings plus parsed {x,y,width,height} bounds, with Y.Text fields serialized to plain strings. Optional filters by element type or id.

Inputs: docId (required), type, elementId, workspaceId.

list_tagsList TagsA
Read-onlyIdempotent

List all workspace-level tags and the number of documents attached to each tag. Use this before tag mutation when the exact tag name or id is unknown.

Inputs: workspaceId.

list_workspacesList WorkspacesA
Read-onlyIdempotent

List all available AFFiNE workspaces

Inputs: no named parameters.

list_workspace_treeList Workspace TreeA
Read-onlyIdempotent

Returns the full document hierarchy as a tree (roots → children → grandchildren). Use depth to limit nesting (default: 3). Note: loads all docs — may be slow on large workspaces. Each node includes an inTrash flag.

Inputs: depth, workspaceId.

move_docMove Document in SidebarA

Move a doc in the AFFiNE sidebar by embedding it under a new parent. Optionally removes it from the old parent (fromParentDocId). If fromParentDocId is omitted, the doc is added to the new parent but not removed from the old one.

Inputs: docId (required), toParentDocId (required), workspaceId, fromParentDocId.

move_organize_nodeMove Organize NodeA

Experimental: move an AFFiNE organize folder or link node to another folder or root. This preserves the target document, tag, or collection and changes only sidebar placement.

Inputs: nodeId (required), index, parentId, workspaceId.

publish_docPublish DocumentA

Make a document publicly accessible through AFFiNE public sharing. This changes sharing state; use revoke_doc to disable public access later.

Inputs: docId (required), mode, workspaceId.

read_all_notificationsMark All Notifications ReadA

Mark every current-user notification as read. This mutates notification state; use list_notifications first when you need to inspect unread items.

Inputs: no named parameters.

read_database_cellsRead Database CellsA
Read-onlyIdempotent

Read row titles and database cell values from an AFFiNE database block.

Inputs: docId (required), databaseBlockId (required), columns, rowBlockIds, +1 more.

read_database_columnsRead Database ColumnsA
Read-onlyIdempotent

Read schema metadata for an AFFiNE database block, including columns, select options, and view column mappings. Useful for empty databases before any rows exist.

Inputs: docId (required), databaseBlockId (required), workspaceId.

read_docRead Document ContentA
Read-onlyIdempotent

Read document block content via WebSocket snapshot (blocks + plain text). Set includeMarkdown: true to also get the rendered markdown — useful when you need to read content without a separate export_doc_markdown call.

Inputs: docId (required), workspaceId, includeMarkdown.

remove_doc_from_collectionRemove Doc From CollectionA

Remove a document id from an AFFiNE collection allow-list. This does not delete the document or change collection rules.

Inputs: docId (required), collectionId (required), workspaceId.

remove_tag_from_docRemove Tag From DocumentA

Detach a tag from one document without deleting the workspace-level tag. Use delete_tag only when the tag should be removed from every document.

Inputs: tag (required), docId (required), workspaceId.

rename_folderRename FolderA

Experimental: rename an AFFiNE organize folder node. This changes sidebar metadata only and does not rename documents inside the folder.

Inputs: name (required), folderId (required), workspaceId.

replace_doc_with_markdownReplace Document With MarkdownA

Replace the main note content of a document with markdown content.

Inputs: docId (required), markdown (required), strict, workspaceId.

resolve_commentResolve CommentA

Set a comment thread's resolved state without changing its content. Use delete_comment only when the comment should be removed.

Inputs: id (required), resolved (required).

revoke_access_tokenRevoke Access TokenA
Destructive

Revoke an existing personal access token by id. This is destructive for API clients using that token; list tokens first if the id is unknown.

Inputs: id (required).

revoke_docRevoke DocumentA
Destructive

Disable public sharing for a document without deleting the document. Use delete_doc only when the document itself should be removed.

Inputs: docId (required), workspaceId.

search_docsSearch Documents by TitleA
Read-onlyIdempotent

Fast search for documents by title using workspace metadata. Much faster than exporting each doc. Returns docId, title, direct URL, and inTrash for each match.

Inputs: query (required), tag, limit, sortBy, +3 more.

set_doc_propertySet Document PropertyA

Set a document's custom property value (property by id or name). Value is validated against the property type (text/number/checkbox/date).

Inputs: docId (required), value (required), property (required), workspaceId.

sign_inSign InA

Sign in to AFFiNE using email and password; sets session cookies for subsequent calls.

Inputs: email (required), password (required).

update_collectionUpdate CollectionA

Rename an existing AFFiNE sidebar collection without changing its rules or allow-list.

Inputs: collectionId (required), name, workspaceId.

update_collection_rulesUpdate Collection RulesA

Replace an AFFiNE collection's rules and rebuild its allow-list from current workspace documents. This can change which docs appear in the collection.

Inputs: rules (required), collectionId (required), workspaceId.

update_commentUpdate CommentA

Replace the content of an existing comment. This preserves the comment thread; use create_comment for a new thread.

Inputs: id (required), content (required).

update_database_rowUpdate Database RowA

Batch update multiple cells on an existing AFFiNE database row. Include title in the cells map to update the Kanban row title.

Inputs: cells (required), docId (required), rowBlockId (required), databaseBlockId (required), +3 more.

update_doc_iconUpdate Document IconA

Set or clear the sidebar icon (the Notion-style emoji slot) on a document. Pass an emoji string, a full icon object, or null to remove it.

Inputs: icon (required), docId (required), workspaceId.

update_doc_titleUpdate Document TitleA

Rename a document — updates both the sidebar title (workspace metadata) and the doc's internal page block title.

Inputs: docId (required), title (required), workspaceId.

update_edgeless_blockUpdate Edgeless BlockA

Partially update a note/frame/edgeless-text block by id. x/y/width/height merge with current prop:xywh (move without resizing, or vice versa). background replaces prop:background (AFFiNE token or {light, dark} hex object). Fields that don't apply to the block's flavour come back under ignored.

Inputs: docId (required), blockId (required), x, y, +4 more.

update_folder_iconUpdate Folder IconA

Set or clear the sidebar icon on an organize folder. Pass an emoji string, a full icon object, or null to remove it. Experimental.

Inputs: icon (required), folderId (required), workspaceId.

update_frame_childrenUpdate Frame ChildrenA

Replace a frame block's contents wholesale. Accepts ids of surface elements (shapes/connectors/groups) AND edgeless blocks (notes/frames/edgeless-text) — all go into BlockSuite's prop:childElementIds map, matching what the editor writes when you drag members into a frame. Dragging the frame drags every owned member. Ids that don't resolve come back under 'missing'. By default the frame is resized to fit its new contents (plus padding + title band); set resizeToFit=false to leave xywh untouched. Pass [] to clear ownership (resize is skipped in that case).

Inputs: docId (required), blockId (required), childElementIds (required), padding, +2 more.

update_profileUpdate ProfileA

Update current user's profile information.

Inputs: name, avatarUrl.

update_settingsUpdate SettingsC

Update user settings and preferences.

Inputs: settings (required).

update_surface_elementUpdate Surface ElementA

Partially update a surface element by id. x/y/width/height merge with the element's current xywh (move without resizing, or vice versa). text/label/title replace their Y.Text wholesale. Fields that don't apply to the element's type are reported in the response 'ignored' list.

Inputs: docId (required), elementId (required), x, y, +26 more.

update_workspaceUpdate WorkspaceB

Update workspace settings

Inputs: id (required), public, enableAi.

upload_blobUpload BlobA

Upload a file or blob into AFFiNE workspace storage and return its blob key. This creates stored content but does not attach it to a document by itself.

Inputs: content (required), workspaceId (required), filename, contentType.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 95 tools

    Disambiguation5/5

    Each tool targets a specific resource and action, with clear differentiation (e.g., add_database_column vs add_database_row, read_doc vs get_doc). Overlapping operations like create_comment vs update_comment vs delete_comment are distinctly named.

    Naming Consistency4/5

    Tools follow a mostly consistent verb_noun pattern (e.g., create_doc, list_tags). A few outliers like current_user and sign_in break the pattern, but overall the naming is predictable and readable.

    Tool Count2/5

    95 tools is excessive for any server, even one covering broad functionality. Many specialized tools could be consolidated (e.g., multiple creation variants). The large count likely overwhelms agents.

    Completeness5/5

    The tool set covers all major workflows: CRUD for docs, databases, comments, tags, collections, surface elements, workspaces, auth, and blobs. Edge cases like orphan docs, fidelity reports, and template instantiation are included. No obvious gaps.

    Maintenance

    ActivityActive
    ResponsivenessUsually handled

    Related MCP Connectors

    Related MCP Servers