Compare commits

..

3 Commits

Author SHA1 Message Date
Max Nikitin
a8c8e0f40b feat: add check_new_comments tool for efficient comment polling
New tool that checks for comments created after a given timestamp.
Scoping options:
- By space (spaceId) — checks all pages in the space
- By subtree (parentPageId) — checks only descendants of a page

Uses updatedAt pre-filtering to minimize API calls: only pages
updated after the target timestamp are checked for comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 14:24:49 +03:00
Max Nikitin
6cde1fddfc fix: stringify comment content and use cursor-based pagination
Docmost comment API requires `content` as a JSON string, not a JSON object.
Also, comments use cursor-based pagination (nextCursor/prevCursor) instead
of page-based pagination used by other endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:59:39 +03:00
Max Nikitin
a739d62318 feat: add comment support (list, get, create, update, delete)
Add 5 new MCP tools for managing Docmost page comments:
- list_comments: list all comments on a page (paginated)
- get_comment: retrieve a single comment by ID
- create_comment: create page-level or inline comments with replies
- update_comment: update existing comment content
- delete_comment: remove a comment

Comment content is automatically converted between Markdown (agent-facing)
and ProseMirror/TipTap JSON (Docmost API). Uses the existing
markdown-converter for reading and a new markdown-to-json utility
for writing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 12:33:39 +03:00
7 changed files with 186 additions and 156 deletions

View File

@@ -1,52 +0,0 @@
name: Docker Build & Push
on:
push:
branches: [ main ]
env:
REGISTRY: gitea.p-lao.com
jobs:
build-and-push:
if: gitea.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
github-server-url: https://gitea.p-lao.com
token: ${{ secrets.TOKEN_GITEA }}
- name: Get version
id: get_version
run: |
VERSION=$(node -p "require('./package.json').version")
if [ "$VERSION" != "" ]; then
echo "version=$VERSION" >> $GITEA_ENV
else
echo "Version not found in package.json"
exit 1
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
gitea.p-lao.com/aleleba/docmost-mcp:${{ env.version }}
gitea.p-lao.com/aleleba/docmost-mcp:latest

View File

@@ -1,15 +0,0 @@
FROM node:22-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-slim
WORKDIR /app
COPY --from=builder /app/build ./build
COPY package*.json ./
RUN npm ci --omit=dev
ENV MCP_WORKERS=4
EXPOSE 8080
CMD ["node", "build/index.js"]

189
package-lock.json generated
View File

@@ -15,7 +15,6 @@
"@tiptap/core": "^3.18.0",
"@tiptap/extension-image": "^3.18.0",
"@tiptap/extension-link": "^3.18.0",
"@tiptap/extension-table": "^3.18.0",
"@tiptap/html": "^3.18.0",
"@tiptap/starter-kit": "^3.18.0",
"@types/jsdom": "^27.0.0",
@@ -316,10 +315,16 @@
}
}
},
"node_modules/@remirror/core-constants": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
"license": "MIT"
},
"node_modules/@tiptap/core": {
"version": "3.24.0",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.24.0.tgz",
"integrity": "sha512-GTAsXAI32p4hEZgPzvUv2RPrObxamy9AFhmhG10fXSvN/cDUs8naEYVIqDV3Sh99jMwQEbTFKW1E1mcspsY6ow==",
"version": "3.18.0",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.18.0.tgz",
"integrity": "sha512-Gczd4GbK1DNgy/QUPElMVozoa0GW9mW8E31VIi7Q4a9PHHz8PcrxPmuWwtJ2q0PF8MWpOSLuBXoQTWaXZRPRnQ==",
"license": "MIT",
"peer": true,
"funding": {
@@ -327,7 +332,7 @@
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/pm": "3.24.0"
"@tiptap/pm": "^3.18.0"
}
},
"node_modules/@tiptap/extension-blockquote": {
@@ -598,20 +603,6 @@
"@tiptap/core": "^3.18.0"
}
},
"node_modules/@tiptap/extension-table": {
"version": "3.24.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.24.0.tgz",
"integrity": "sha512-lr5elob3uJnB+ltgqPDEeVQmIPRx6JoS0I6z93tOgKsI2mIsaw5ErghteeiCTpExdyax7aWR0fn5pZzLVDQL8A==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "3.24.0",
"@tiptap/pm": "3.24.0"
}
},
"node_modules/@tiptap/extension-text": {
"version": "3.18.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.18.0.tgz",
@@ -669,23 +660,28 @@
}
},
"node_modules/@tiptap/pm": {
"version": "3.24.0",
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.24.0.tgz",
"integrity": "sha512-QQP/78ryOZDN99gNBV7dgh69/8AYaOYQYFklq/iR+ZRFaaL3+qqHFvPVJapGkzPdymBgNJ34xjFM8n5pJ4QmMg==",
"version": "3.18.0",
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.18.0.tgz",
"integrity": "sha512-8RoI5gW0xBVCsuxahpK8vx7onAw6k2/uR3hbGBBnH+HocDMaAZKot3nTyY546ij8ospIC1mnQ7k4BhVUZesZDQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"prosemirror-changeset": "^2.3.0",
"prosemirror-collab": "^1.3.1",
"prosemirror-commands": "^1.6.2",
"prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.2",
"prosemirror-history": "^1.4.1",
"prosemirror-inputrules": "^1.4.0",
"prosemirror-keymap": "^1.2.2",
"prosemirror-markdown": "^1.13.1",
"prosemirror-menu": "^1.2.4",
"prosemirror-model": "^1.24.1",
"prosemirror-schema-basic": "^1.2.3",
"prosemirror-schema-list": "^1.5.0",
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.6.4",
"prosemirror-trailing-node": "^3.0.0",
"prosemirror-transform": "^1.10.2",
"prosemirror-view": "^1.38.1"
},
@@ -752,6 +748,28 @@
"parse5": "^7.0.0"
}
},
"node_modules/@types/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
"license": "MIT"
},
"node_modules/@types/markdown-it": {
"version": "14.1.2",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
"license": "MIT",
"dependencies": {
"@types/linkify-it": "^5",
"@types/mdurl": "^2"
}
},
"node_modules/@types/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
@@ -837,6 +855,12 @@
}
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -994,6 +1018,12 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"license": "MIT"
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1191,6 +1221,18 @@
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -1754,6 +1796,15 @@
"url": "https://github.com/sponsors/dmonad"
}
},
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/linkifyjs": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
@@ -1769,6 +1820,23 @@
"node": "20 || >=22"
}
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
},
"bin": {
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/marked": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz",
@@ -1796,6 +1864,12 @@
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
"license": "CC0-1.0"
},
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"license": "MIT"
},
"node_modules/media-typer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
@@ -1975,6 +2049,15 @@
"prosemirror-transform": "^1.0.0"
}
},
"node_modules/prosemirror-collab": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
"license": "MIT",
"dependencies": {
"prosemirror-state": "^1.0.0"
}
},
"node_modules/prosemirror-commands": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
@@ -2041,6 +2124,29 @@
"w3c-keyname": "^2.2.0"
}
},
"node_modules/prosemirror-markdown": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.3.tgz",
"integrity": "sha512-3E+Et6cdXIH0EgN2tGYQ+EBT7N4kMiZFsW+hzx+aPtOmADDHWCdd2uUQb7yklJrfUYUOjEEu22BiN6UFgPe4cQ==",
"license": "MIT",
"dependencies": {
"@types/markdown-it": "^14.0.0",
"markdown-it": "^14.0.0",
"prosemirror-model": "^1.25.0"
}
},
"node_modules/prosemirror-menu": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz",
"integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==",
"license": "MIT",
"dependencies": {
"crelt": "^1.0.0",
"prosemirror-commands": "^1.0.0",
"prosemirror-history": "^1.0.0",
"prosemirror-state": "^1.0.0"
}
},
"node_modules/prosemirror-model": {
"version": "1.25.4",
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
@@ -2051,6 +2157,15 @@
"orderedmap": "^2.0.0"
}
},
"node_modules/prosemirror-schema-basic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
"integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
"license": "MIT",
"dependencies": {
"prosemirror-model": "^1.25.0"
}
},
"node_modules/prosemirror-schema-list": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
@@ -2087,6 +2202,21 @@
"prosemirror-view": "^1.41.4"
}
},
"node_modules/prosemirror-trailing-node": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
"license": "MIT",
"dependencies": {
"@remirror/core-constants": "3.0.0",
"escape-string-regexp": "^4.0.0"
},
"peerDependencies": {
"prosemirror-model": "^1.22.1",
"prosemirror-state": "^1.4.2",
"prosemirror-view": "^1.33.8"
}
},
"node_modules/prosemirror-transform": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz",
@@ -2136,6 +2266,15 @@
"node": ">=6"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
@@ -2471,6 +2610,12 @@
"node": ">=14.17"
}
},
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"license": "MIT"
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",

View File

@@ -29,7 +29,6 @@
"@tiptap/core": "^3.18.0",
"@tiptap/extension-image": "^3.18.0",
"@tiptap/extension-link": "^3.18.0",
"@tiptap/extension-table": "^3.18.0",
"@tiptap/html": "^3.18.0",
"@tiptap/starter-kit": "^3.18.0",
"@types/jsdom": "^27.0.0",

View File

@@ -1,7 +1,5 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { createServer, IncomingMessage, ServerResponse } from "node:http";
import cluster from "node:cluster";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import FormData from "form-data";
import axios, { AxiosInstance } from "axios";
import { z } from "zod";
@@ -14,12 +12,12 @@ import {
filterSearchResult,
} from "./lib/filters.js";
import { convertProseMirrorToMarkdown } from "./lib/markdown-converter.js";
import { markdownToTiptapJson } from "./lib/markdown-to-json.js";
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import { updatePageContentRealtime } from "./lib/collaboration.js";
import { getCollabToken, performLogin } from "./lib/auth-utils.js";
import { markdownToTiptapJson } from "./lib/markdown-to-json.js";
// Read version from package.json
const __filename = fileURLToPath(import.meta.url);
@@ -287,11 +285,8 @@ class DocmostClient {
spaceId,
});
// Normalize search response for Docmost 0.25+ compatibility
// Before 0.25: response.data.data was a direct array
// After 0.25: response.data.data is { items: [...], meta: {...} }
const data = response.data?.data;
const items = Array.isArray(data) ? data : (data?.items || []);
// Filter search results (data is directly an array)
const items = response.data?.data || [];
const filteredItems = items.map((item: any) => filterSearchResult(item));
return {
@@ -406,7 +401,7 @@ class DocmostClient {
async updateComment(commentId: string, content: string) {
await this.ensureAuthenticated();
const jsonContent = await markdownToTiptapJson(content);
await this.client.post("/comments/update", {
const response = await this.client.post("/comments/update", {
commentId,
content: JSON.stringify(jsonContent),
});
@@ -430,7 +425,11 @@ class DocmostClient {
* 2. If parentPageId given, keep only descendants of that page
* 3. For matching pages, fetch comments and filter by createdAt > since
*/
async checkNewComments(spaceId: string, since: string, parentPageId?: string) {
async checkNewComments(
spaceId: string,
since: string,
parentPageId?: string,
) {
await this.ensureAuthenticated();
const sinceDate = new Date(since);
@@ -442,6 +441,7 @@ class DocmostClient {
let allowedPageIds: Set<string> | null = null;
if (parentPageId) {
allowedPageIds = new Set<string>();
// BFS to collect all descendants
const pageMap = new Map<string, any[]>();
for (const page of allPages) {
const pid = page.parentPageId || "__root__";
@@ -494,7 +494,9 @@ class DocmostClient {
return {
since,
scope: parentPageId ? `subtree of ${parentPageId}` : `space ${spaceId}`,
scope: parentPageId
? `subtree of ${parentPageId}`
: `space ${spaceId}`,
checkedPages: recentlyUpdated.length,
pagesWithNewComments: results.length,
totalNewComments,
@@ -859,57 +861,11 @@ server.registerTool(
},
);
const NUM_WORKERS = parseInt(process.env.MCP_WORKERS ?? "4", 10);
if (cluster.isPrimary) {
// ── Proceso primario: solo hace fork de workers ──
console.log(`[docmost-mcp] Primary PID=${process.pid}, starting ${NUM_WORKERS} workers`);
for (let i = 0; i < NUM_WORKERS; i++) {
cluster.fork();
}
cluster.on("exit", (worker, code) => {
console.log(`[docmost-mcp] Worker PID=${worker.process.pid} died (code=${code}), restarting...`);
cluster.fork();
});
} else {
// ── Worker: cada uno tiene su propio McpServer singleton ──
// Sin mutex: cada request se atiende concurrentemente dentro del worker.
// StreamableHTTPServerTransport es stateless (uno por request), sin estado compartido.
async function handleMcpRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless: un transport por request
});
async function run() {
const transport = new StdioServerTransport();
await server.connect(transport);
try {
await transport.handleRequest(req, res);
} finally {
await transport.close();
}
}
const httpServer = createServer((req: IncomingMessage, res: ServerResponse) => {
if (!req.url?.startsWith("/mcp")) {
res.writeHead(404).end("Not found");
return;
}
handleMcpRequest(req, res).catch((err) => {
console.error(`[docmost-mcp] Worker PID=${process.pid} error:`, err);
if (!res.headersSent) {
res.writeHead(500, { "Content-Type": "application/json" }).end(
JSON.stringify({ jsonrpc: "2.0", error: { code: -32603, message: String(err) }, id: null })
);
}
});
});
httpServer.listen(8080, () => {
console.log(`[docmost-mcp] Worker PID=${process.pid} listening on port 8080, path /mcp`);
});
}
// run() vacía — el cluster ya se inició en el top-level de arriba
async function run() {}
run().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);

View File

@@ -67,7 +67,7 @@ export async function updatePageContentRealtime(
const timer = setTimeout(() => {
if (provider) provider.destroy();
reject(new Error("Connection timeout to collaboration server"));
}, 120000);
}, 25000);
const provider = new HocuspocusProvider({
url: wsUrl,

View File

@@ -1,11 +1,9 @@
import StarterKit from "@tiptap/starter-kit";
import Image from "@tiptap/extension-image";
import Link from "@tiptap/extension-link";
import { TableKit } from "@tiptap/extension-table";
// Define extensions compatible with standard Markdown features
// We use the default Tiptap extensions to handle basic content
// TableKit is required for generateJSON to parse HTML tables from marked (GFM tables)
export const tiptapExtensions = [
StarterKit.configure({
// Explicitly enable features that might be disabled in some contexts
@@ -13,10 +11,9 @@ export const tiptapExtensions = [
heading: {},
}),
Image.configure({
inline: false,
inline: true,
}),
Link.configure({
openOnClick: false,
}),
TableKit,
];