affine-mcp-server 1.11.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A Model Context Protocol (MCP) server that integrates with AFFiNE (self‑hosted or cloud). It exposes AFFiNE workspaces and documents to AI assistants over stdio (default) or HTTP (`/mcp`).
4
4
 
5
- [![Version](https://img.shields.io/badge/version-1.11.0-blue)](https://github.com/dawncr0w/affine-mcp-server/releases)
5
+ [![Version](https://img.shields.io/badge/version-1.11.2-blue)](https://github.com/dawncr0w/affine-mcp-server/releases)
6
6
  [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.17.2-green)](https://github.com/modelcontextprotocol/typescript-sdk)
7
7
  [![CI](https://github.com/dawncr0w/affine-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/dawncr0w/affine-mcp-server/actions/workflows/ci.yml)
8
8
  [![License](https://img.shields.io/badge/license-MIT-yellow)](LICENSE)
@@ -19,7 +19,7 @@ A Model Context Protocol (MCP) server that integrates with AFFiNE (self‑hosted
19
19
  - Tools: 76 focused tools with WebSocket-based document editing
20
20
  - Status: Active
21
21
 
22
- > New in v1.11.0: Added sidebar organize tools, configurable tool filtering, `delete_database_row`, and richer markdown import formatting for lists and table cells.
22
+ > New in v1.11.2: Corrected stale deleted-document visibility in `list_docs` after `delete_doc`, completing the `v1.11.1` delete-metadata fix.
23
23
 
24
24
  ## Features
25
25
 
@@ -1985,6 +1985,9 @@ export function registerDocTools(server, gql, defaults) {
1985
1985
  const docs = data.workspace.docs;
1986
1986
  const tagsByDocId = new Map();
1987
1987
  const titlesByDocId = new Map();
1988
+ let workspacePageCount = null;
1989
+ let workspacePageIds = null;
1990
+ const deletedDocIds = new Set();
1988
1991
  try {
1989
1992
  const { endpoint, cookie, bearer } = await getCookieAndEndpoint();
1990
1993
  const wsUrl = wsUrlFromGraphQLEndpoint(endpoint);
@@ -1997,6 +2000,8 @@ export function registerDocTools(server, gql, defaults) {
1997
2000
  Y.applyUpdate(wsDoc, Buffer.from(snapshot.missing, "base64"));
1998
2001
  const meta = wsDoc.getMap("meta");
1999
2002
  const pages = getWorkspacePageEntries(meta);
2003
+ workspacePageCount = pages.length;
2004
+ workspacePageIds = new Set(pages.map(page => page.id));
2000
2005
  const { byId } = getWorkspaceTagOptionMaps(meta);
2001
2006
  for (const page of pages) {
2002
2007
  if (page.title) {
@@ -2006,6 +2011,20 @@ export function registerDocTools(server, gql, defaults) {
2006
2011
  tagsByDocId.set(page.id, resolveTagLabels(tagEntries, byId));
2007
2012
  }
2008
2013
  }
2014
+ const graphEdges = Array.isArray(docs?.edges) ? docs.edges : [];
2015
+ if (workspacePageIds && graphEdges.length > workspacePageIds.size) {
2016
+ for (const edge of graphEdges) {
2017
+ const nodeId = edge?.node?.id;
2018
+ if (typeof nodeId !== "string" || workspacePageIds.has(nodeId)) {
2019
+ continue;
2020
+ }
2021
+ const edgeSnapshot = await loadDoc(socket, workspaceId, nodeId);
2022
+ const edgeExists = Boolean(edgeSnapshot.missing || edgeSnapshot.state || edgeSnapshot.timestamp);
2023
+ if (!edgeExists) {
2024
+ deletedDocIds.add(nodeId);
2025
+ }
2026
+ }
2027
+ }
2009
2028
  }
2010
2029
  finally {
2011
2030
  socket.disconnect();
@@ -2014,24 +2033,46 @@ export function registerDocTools(server, gql, defaults) {
2014
2033
  catch {
2015
2034
  // Keep list_docs available even when workspace snapshot fetch fails.
2016
2035
  }
2036
+ const mergedEdges = Array.isArray(docs?.edges)
2037
+ ? docs.edges.map((edge) => {
2038
+ const node = edge?.node;
2039
+ if (!node || typeof node.id !== "string") {
2040
+ return edge;
2041
+ }
2042
+ return {
2043
+ ...edge,
2044
+ node: {
2045
+ ...node,
2046
+ title: titlesByDocId.get(node.id) || node.title,
2047
+ tags: tagsByDocId.get(node.id) || [],
2048
+ },
2049
+ };
2050
+ })
2051
+ : [];
2052
+ const visibleEdges = deletedDocIds.size > 0
2053
+ ? mergedEdges.filter((edge) => !deletedDocIds.has(edge?.node?.id))
2054
+ : mergedEdges;
2055
+ const correctedTotalCount = typeof docs?.totalCount === "number" &&
2056
+ typeof workspacePageCount === "number" &&
2057
+ (deletedDocIds.size > 0 ||
2058
+ visibleEdges.length === workspacePageCount) &&
2059
+ workspacePageCount < docs.totalCount
2060
+ ? workspacePageCount
2061
+ : docs?.totalCount;
2062
+ const correctedPageInfo = docs?.pageInfo
2063
+ ? {
2064
+ ...docs.pageInfo,
2065
+ endCursor: visibleEdges.length > 0 ? visibleEdges[visibleEdges.length - 1]?.cursor ?? null : null,
2066
+ hasNextPage: typeof correctedTotalCount === "number" && !parsed.after
2067
+ ? (parsed.offset ?? 0) + visibleEdges.length < correctedTotalCount
2068
+ : docs.pageInfo.hasNextPage,
2069
+ }
2070
+ : docs?.pageInfo;
2017
2071
  const mergedDocs = {
2018
2072
  ...docs,
2019
- edges: Array.isArray(docs?.edges)
2020
- ? docs.edges.map((edge) => {
2021
- const node = edge?.node;
2022
- if (!node || typeof node.id !== "string") {
2023
- return edge;
2024
- }
2025
- return {
2026
- ...edge,
2027
- node: {
2028
- ...node,
2029
- title: titlesByDocId.get(node.id) || node.title,
2030
- tags: tagsByDocId.get(node.id) || [],
2031
- },
2032
- };
2033
- })
2034
- : [],
2073
+ totalCount: correctedTotalCount,
2074
+ pageInfo: correctedPageInfo,
2075
+ edges: visibleEdges,
2035
2076
  };
2036
2077
  return text(mergedDocs);
2037
2078
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "affine-mcp-server",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
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.",