Compare commits
1 Commits
main
...
andersou/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbd5200d96 |
@@ -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
|
|
||||||
15
Dockerfile
15
Dockerfile
@@ -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"]
|
|
||||||
@@ -20,15 +20,6 @@ A Model Context Protocol (MCP) server for [Docmost](https://docmost.com/), enabl
|
|||||||
- **`list_pages`**: List pages within a space (ordered by `updatedAt` descending).
|
- **`list_pages`**: List pages within a space (ordered by `updatedAt` descending).
|
||||||
- **`get_page`**: Retrieve full content and metadata of a specific page.
|
- **`get_page`**: Retrieve full content and metadata of a specific page.
|
||||||
|
|
||||||
### Comments
|
|
||||||
|
|
||||||
- **`list_comments`**: List all comments on a page with content converted to Markdown.
|
|
||||||
- **`get_comment`**: Retrieve a single comment by ID.
|
|
||||||
- **`create_comment`**: Create a page-level or inline comment. Content is provided as Markdown and automatically converted to ProseMirror JSON. Supports replies via `parentCommentId`.
|
|
||||||
- **`update_comment`**: Update an existing comment's content (creator only).
|
|
||||||
- **`delete_comment`**: Delete a comment (creator or space admin only).
|
|
||||||
- **`check_new_comments`**: Check for new comments across a space (or a page subtree) since a given timestamp. Efficiently filters by `updatedAt` before fetching comments.
|
|
||||||
|
|
||||||
### Technical Details
|
### Technical Details
|
||||||
|
|
||||||
- **Automatic Markdown Conversion**: Page content is automatically converted from Docmost's internal ProseMirror/TipTap JSON format to clean Markdown for easy agent consumption. Supports all Docmost extensions including callouts, task lists, math blocks, embeds, and more.
|
- **Automatic Markdown Conversion**: Page content is automatically converted from Docmost's internal ProseMirror/TipTap JSON format to clean Markdown for easy agent consumption. Supports all Docmost extensions including callouts, task lists, math blocks, embeds, and more.
|
||||||
|
|||||||
201
package-lock.json
generated
201
package-lock.json
generated
@@ -158,7 +158,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
@@ -197,7 +196,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@@ -316,18 +314,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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": {
|
"node_modules/@tiptap/core": {
|
||||||
"version": "3.24.0",
|
"version": "3.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.1.tgz",
|
||||||
"integrity": "sha512-GTAsXAI32p4hEZgPzvUv2RPrObxamy9AFhmhG10fXSvN/cDUs8naEYVIqDV3Sh99jMwQEbTFKW1E1mcspsY6ow==",
|
"integrity": "sha512-SwkPEWIfaDEZjC8SEIi4kZjqIYUbRgLUHUuQezo5GbphUNC8kM1pi3C3EtoOPtxXrEbY6e4pWEzW54Pcrd+rVA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/pm": "3.24.0"
|
"@tiptap/pm": "^3.20.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-blockquote": {
|
"node_modules/@tiptap/extension-blockquote": {
|
||||||
@@ -523,7 +526,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.18.0.tgz",
|
||||||
"integrity": "sha512-9lQBo45HNqIFcLEHAk+CY3W51eMMxIJjWbthm2CwEWr4PB3+922YELlvq8JcLH1nVFkBVpmBFmQe/GxgnCkzwQ==",
|
"integrity": "sha512-9lQBo45HNqIFcLEHAk+CY3W51eMMxIJjWbthm2CwEWr4PB3+922YELlvq8JcLH1nVFkBVpmBFmQe/GxgnCkzwQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
@@ -599,17 +601,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-table": {
|
"node_modules/@tiptap/extension-table": {
|
||||||
"version": "3.24.0",
|
"version": "3.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.20.1.tgz",
|
||||||
"integrity": "sha512-lr5elob3uJnB+ltgqPDEeVQmIPRx6JoS0I6z93tOgKsI2mIsaw5ErghteeiCTpExdyax7aWR0fn5pZzLVDQL8A==",
|
"integrity": "sha512-y2o1De3P/FH80nst1D9pYSwtRvxF87WkNAYGurqCkZ6SjwFq1Ifeh9eY+o79R6SCwcfpvQRytCYd+Yw9o5XAtA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "3.24.0",
|
"@tiptap/core": "^3.20.1",
|
||||||
"@tiptap/pm": "3.24.0"
|
"@tiptap/pm": "^3.20.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-text": {
|
"node_modules/@tiptap/extension-text": {
|
||||||
@@ -643,7 +645,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.18.0.tgz",
|
||||||
"integrity": "sha512-uSRIE9HGshBN6NRFR3LX2lZqBLvX92SgU5A9AvUbJD4MqU63E+HdruJnRjsVlX3kPrmbIDowxrzXlUcg3K0USQ==",
|
"integrity": "sha512-uSRIE9HGshBN6NRFR3LX2lZqBLvX92SgU5A9AvUbJD4MqU63E+HdruJnRjsVlX3kPrmbIDowxrzXlUcg3K0USQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
@@ -669,23 +670,27 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/pm": {
|
"node_modules/@tiptap/pm": {
|
||||||
"version": "3.24.0",
|
"version": "3.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.1.tgz",
|
||||||
"integrity": "sha512-QQP/78ryOZDN99gNBV7dgh69/8AYaOYQYFklq/iR+ZRFaaL3+qqHFvPVJapGkzPdymBgNJ34xjFM8n5pJ4QmMg==",
|
"integrity": "sha512-6kCiGLvpES4AxcEuOhb7HR7/xIeJWMjZlb6J7e8zpiIh5BoQc7NoRdctsnmFEjZvC19bIasccshHQ7H2zchWqw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-changeset": "^2.3.0",
|
"prosemirror-changeset": "^2.3.0",
|
||||||
|
"prosemirror-collab": "^1.3.1",
|
||||||
"prosemirror-commands": "^1.6.2",
|
"prosemirror-commands": "^1.6.2",
|
||||||
"prosemirror-dropcursor": "^1.8.1",
|
"prosemirror-dropcursor": "^1.8.1",
|
||||||
"prosemirror-gapcursor": "^1.3.2",
|
"prosemirror-gapcursor": "^1.3.2",
|
||||||
"prosemirror-history": "^1.4.1",
|
"prosemirror-history": "^1.4.1",
|
||||||
"prosemirror-inputrules": "^1.4.0",
|
"prosemirror-inputrules": "^1.4.0",
|
||||||
"prosemirror-keymap": "^1.2.2",
|
"prosemirror-keymap": "^1.2.2",
|
||||||
|
"prosemirror-markdown": "^1.13.1",
|
||||||
|
"prosemirror-menu": "^1.2.4",
|
||||||
"prosemirror-model": "^1.24.1",
|
"prosemirror-model": "^1.24.1",
|
||||||
|
"prosemirror-schema-basic": "^1.2.3",
|
||||||
"prosemirror-schema-list": "^1.5.0",
|
"prosemirror-schema-list": "^1.5.0",
|
||||||
"prosemirror-state": "^1.4.3",
|
"prosemirror-state": "^1.4.3",
|
||||||
"prosemirror-tables": "^1.6.4",
|
"prosemirror-tables": "^1.6.4",
|
||||||
|
"prosemirror-trailing-node": "^3.0.0",
|
||||||
"prosemirror-transform": "^1.10.2",
|
"prosemirror-transform": "^1.10.2",
|
||||||
"prosemirror-view": "^1.38.1"
|
"prosemirror-view": "^1.38.1"
|
||||||
},
|
},
|
||||||
@@ -752,6 +757,28 @@
|
|||||||
"parse5": "^7.0.0"
|
"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": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.19.30",
|
"version": "20.19.30",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
|
||||||
@@ -771,13 +798,15 @@
|
|||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
|
||||||
"integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
|
"integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/ws": {
|
"node_modules/@types/ws": {
|
||||||
"version": "8.18.1",
|
"version": "8.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -837,6 +866,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": {
|
"node_modules/asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
@@ -994,6 +1029,12 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"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": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -1191,6 +1232,18 @@
|
|||||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/etag": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
@@ -1226,7 +1279,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.2.1",
|
"body-parser": "^2.2.1",
|
||||||
@@ -1754,6 +1806,15 @@
|
|||||||
"url": "https://github.com/sponsors/dmonad"
|
"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": {
|
"node_modules/linkifyjs": {
|
||||||
"version": "4.3.2",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
|
||||||
@@ -1769,6 +1830,23 @@
|
|||||||
"node": "20 || >=22"
|
"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": {
|
"node_modules/marked": {
|
||||||
"version": "17.0.1",
|
"version": "17.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz",
|
||||||
@@ -1796,6 +1874,12 @@
|
|||||||
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
|
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
|
||||||
"license": "CC0-1.0"
|
"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": {
|
"node_modules/media-typer": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
@@ -1975,6 +2059,15 @@
|
|||||||
"prosemirror-transform": "^1.0.0"
|
"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": {
|
"node_modules/prosemirror-commands": {
|
||||||
"version": "1.7.1",
|
"version": "1.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
|
||||||
@@ -2041,16 +2134,47 @@
|
|||||||
"w3c-keyname": "^2.2.0"
|
"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": {
|
"node_modules/prosemirror-model": {
|
||||||
"version": "1.25.4",
|
"version": "1.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"orderedmap": "^2.0.0"
|
"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": {
|
"node_modules/prosemirror-schema-list": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
|
||||||
@@ -2067,7 +2191,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.0.0",
|
"prosemirror-model": "^1.0.0",
|
||||||
"prosemirror-transform": "^1.0.0",
|
"prosemirror-transform": "^1.0.0",
|
||||||
@@ -2087,6 +2210,21 @@
|
|||||||
"prosemirror-view": "^1.41.4"
|
"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": {
|
"node_modules/prosemirror-transform": {
|
||||||
"version": "1.11.0",
|
"version": "1.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz",
|
||||||
@@ -2101,7 +2239,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.5.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.5.tgz",
|
||||||
"integrity": "sha512-UDQbIPnDrjE8tqUBbPmCOZgtd75htE6W3r0JCmY9bL6W1iemDM37MZEKC49d+tdQ0v/CKx4gjxLoLsfkD2NiZA==",
|
"integrity": "sha512-UDQbIPnDrjE8tqUBbPmCOZgtd75htE6W3r0JCmY9bL6W1iemDM37MZEKC49d+tdQ0v/CKx4gjxLoLsfkD2NiZA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.20.0",
|
"prosemirror-model": "^1.20.0",
|
||||||
"prosemirror-state": "^1.0.0",
|
"prosemirror-state": "^1.0.0",
|
||||||
@@ -2136,6 +2273,15 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.1",
|
"version": "6.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||||
@@ -2471,6 +2617,12 @@
|
|||||||
"node": ">=14.17"
|
"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": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
@@ -2527,6 +2679,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
||||||
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -2652,7 +2805,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.29.tgz",
|
"resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.29.tgz",
|
||||||
"integrity": "sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==",
|
"integrity": "sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.99"
|
"lib0": "^0.2.99"
|
||||||
},
|
},
|
||||||
@@ -2670,7 +2822,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
373
src/index.ts
373
src/index.ts
@@ -1,7 +1,5 @@
|
|||||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
import { createServer, IncomingMessage, ServerResponse } from "node:http";
|
|
||||||
import cluster from "node:cluster";
|
|
||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
import axios, { AxiosInstance } from "axios";
|
import axios, { AxiosInstance } from "axios";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -10,11 +8,9 @@ import {
|
|||||||
filterSpace,
|
filterSpace,
|
||||||
filterGroup,
|
filterGroup,
|
||||||
filterPage,
|
filterPage,
|
||||||
filterComment,
|
|
||||||
filterSearchResult,
|
filterSearchResult,
|
||||||
} from "./lib/filters.js";
|
} from "./lib/filters.js";
|
||||||
import { convertProseMirrorToMarkdown } from "./lib/markdown-converter.js";
|
import { convertProseMirrorToMarkdown } from "./lib/markdown-converter.js";
|
||||||
import { markdownToTiptapJson } from "./lib/markdown-to-json.js";
|
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
@@ -287,11 +283,8 @@ class DocmostClient {
|
|||||||
spaceId,
|
spaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Normalize search response for Docmost 0.25+ compatibility
|
// Filter search results (data is directly an array)
|
||||||
// Before 0.25: response.data.data was a direct array
|
const items = response.data?.data || [];
|
||||||
// After 0.25: response.data.data is { items: [...], meta: {...} }
|
|
||||||
const data = response.data?.data;
|
|
||||||
const items = Array.isArray(data) ? data : (data?.items || []);
|
|
||||||
const filteredItems = items.map((item: any) => filterSearchResult(item));
|
const filteredItems = items.map((item: any) => filterSearchResult(item));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -335,172 +328,6 @@ class DocmostClient {
|
|||||||
);
|
);
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Comment Methods ---
|
|
||||||
|
|
||||||
async listComments(pageId: string) {
|
|
||||||
await this.ensureAuthenticated();
|
|
||||||
let allComments: any[] = [];
|
|
||||||
let cursor: string | null = null;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const payload: Record<string, any> = { pageId, limit: 100 };
|
|
||||||
if (cursor) payload.cursor = cursor;
|
|
||||||
|
|
||||||
const response = await this.client.post("/comments", payload);
|
|
||||||
const data = response.data.data || response.data;
|
|
||||||
const items = data.items || [];
|
|
||||||
allComments = allComments.concat(items);
|
|
||||||
cursor = data.meta?.nextCursor || null;
|
|
||||||
} while (cursor);
|
|
||||||
|
|
||||||
return allComments.map((comment: any) => {
|
|
||||||
const markdown = comment.content
|
|
||||||
? convertProseMirrorToMarkdown(comment.content)
|
|
||||||
: "";
|
|
||||||
return filterComment(comment, markdown);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getComment(commentId: string) {
|
|
||||||
await this.ensureAuthenticated();
|
|
||||||
const response = await this.client.post("/comments/info", { commentId });
|
|
||||||
const comment = response.data.data || response.data;
|
|
||||||
const markdown = comment.content
|
|
||||||
? convertProseMirrorToMarkdown(comment.content)
|
|
||||||
: "";
|
|
||||||
return {
|
|
||||||
data: filterComment(comment, markdown),
|
|
||||||
success: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async createComment(
|
|
||||||
pageId: string,
|
|
||||||
content: string,
|
|
||||||
type: "page" | "inline" = "page",
|
|
||||||
selection?: string,
|
|
||||||
parentCommentId?: string,
|
|
||||||
) {
|
|
||||||
await this.ensureAuthenticated();
|
|
||||||
const jsonContent = await markdownToTiptapJson(content);
|
|
||||||
const payload: Record<string, any> = {
|
|
||||||
pageId,
|
|
||||||
content: JSON.stringify(jsonContent),
|
|
||||||
type,
|
|
||||||
};
|
|
||||||
if (selection) payload.selection = selection;
|
|
||||||
if (parentCommentId) payload.parentCommentId = parentCommentId;
|
|
||||||
|
|
||||||
const response = await this.client.post("/comments/create", payload);
|
|
||||||
const comment = response.data.data || response.data;
|
|
||||||
const markdown = comment.content
|
|
||||||
? convertProseMirrorToMarkdown(comment.content)
|
|
||||||
: content;
|
|
||||||
return {
|
|
||||||
data: filterComment(comment, markdown),
|
|
||||||
success: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateComment(commentId: string, content: string) {
|
|
||||||
await this.ensureAuthenticated();
|
|
||||||
const jsonContent = await markdownToTiptapJson(content);
|
|
||||||
await this.client.post("/comments/update", {
|
|
||||||
commentId,
|
|
||||||
content: JSON.stringify(jsonContent),
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
commentId,
|
|
||||||
message: "Comment updated successfully.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteComment(commentId: string) {
|
|
||||||
await this.ensureAuthenticated();
|
|
||||||
return this.client
|
|
||||||
.post("/comments/delete", { commentId })
|
|
||||||
.then((res) => res.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for new comments across pages in a space (optionally scoped to a subtree).
|
|
||||||
* 1. Fetch all pages in space, filter by updatedAt > since
|
|
||||||
* 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) {
|
|
||||||
await this.ensureAuthenticated();
|
|
||||||
|
|
||||||
const sinceDate = new Date(since);
|
|
||||||
|
|
||||||
// 1. Get all pages in the space
|
|
||||||
const allPages = await this.paginateAll<any>("/pages/recent", { spaceId });
|
|
||||||
|
|
||||||
// 2. If parentPageId specified, build set of descendant page IDs
|
|
||||||
let allowedPageIds: Set<string> | null = null;
|
|
||||||
if (parentPageId) {
|
|
||||||
allowedPageIds = new Set<string>();
|
|
||||||
const pageMap = new Map<string, any[]>();
|
|
||||||
for (const page of allPages) {
|
|
||||||
const pid = page.parentPageId || "__root__";
|
|
||||||
if (!pageMap.has(pid)) pageMap.set(pid, []);
|
|
||||||
pageMap.get(pid)!.push(page);
|
|
||||||
}
|
|
||||||
const queue = [parentPageId];
|
|
||||||
allowedPageIds.add(parentPageId);
|
|
||||||
while (queue.length > 0) {
|
|
||||||
const current = queue.shift()!;
|
|
||||||
const children = pageMap.get(current) || [];
|
|
||||||
for (const child of children) {
|
|
||||||
allowedPageIds.add(child.id);
|
|
||||||
queue.push(child.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Filter pages by updatedAt > since and optional subtree
|
|
||||||
const recentlyUpdated = allPages.filter((page: any) => {
|
|
||||||
if (new Date(page.updatedAt) <= sinceDate) return false;
|
|
||||||
if (allowedPageIds && !allowedPageIds.has(page.id)) return false;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4. Fetch comments for each updated page and filter by createdAt > since
|
|
||||||
const results: any[] = [];
|
|
||||||
for (const page of recentlyUpdated) {
|
|
||||||
try {
|
|
||||||
const comments = await this.listComments(page.id);
|
|
||||||
const newComments = comments.filter(
|
|
||||||
(c: any) => new Date(c.createdAt) > sinceDate,
|
|
||||||
);
|
|
||||||
if (newComments.length > 0) {
|
|
||||||
results.push({
|
|
||||||
pageId: page.id,
|
|
||||||
pageTitle: page.title,
|
|
||||||
comments: newComments,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
// Skip pages with errors (e.g. deleted between calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalNewComments = results.reduce(
|
|
||||||
(sum, r) => sum + r.comments.length,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
since,
|
|
||||||
scope: parentPageId ? `subtree of ${parentPageId}` : `space ${spaceId}`,
|
|
||||||
checkedPages: recentlyUpdated.length,
|
|
||||||
pagesWithNewComments: results.length,
|
|
||||||
totalNewComments,
|
|
||||||
comments: results,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const docmostClient = new DocmostClient(API_URL);
|
const docmostClient = new DocmostClient(API_URL);
|
||||||
@@ -717,199 +544,11 @@ server.registerTool(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- Comment Tools ---
|
async function run() {
|
||||||
|
const transport = new StdioServerTransport();
|
||||||
// Tool: list_comments
|
await server.connect(transport);
|
||||||
server.registerTool(
|
|
||||||
"list_comments",
|
|
||||||
{
|
|
||||||
description:
|
|
||||||
"List all comments on a page. Returns comments with content converted to Markdown.",
|
|
||||||
inputSchema: {
|
|
||||||
pageId: z.string().describe("ID of the page to list comments for"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ pageId }) => {
|
|
||||||
const comments = await docmostClient.listComments(pageId);
|
|
||||||
return jsonContent(comments);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Tool: get_comment
|
|
||||||
server.registerTool(
|
|
||||||
"get_comment",
|
|
||||||
{
|
|
||||||
description: "Get a single comment by ID with content as Markdown.",
|
|
||||||
inputSchema: {
|
|
||||||
commentId: z.string().describe("ID of the comment"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ commentId }) => {
|
|
||||||
const comment = await docmostClient.getComment(commentId);
|
|
||||||
return jsonContent(comment);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Tool: create_comment
|
|
||||||
server.registerTool(
|
|
||||||
"create_comment",
|
|
||||||
{
|
|
||||||
description:
|
|
||||||
"Create a new comment on a page. Content is provided as Markdown and automatically converted to the required format.",
|
|
||||||
inputSchema: {
|
|
||||||
pageId: z.string().describe("ID of the page to comment on"),
|
|
||||||
content: z.string().describe("Comment content in Markdown format"),
|
|
||||||
type: z
|
|
||||||
.enum(["page", "inline"])
|
|
||||||
.optional()
|
|
||||||
.describe(
|
|
||||||
"Comment type: 'page' for general page comment (default), 'inline' for text selection comment",
|
|
||||||
),
|
|
||||||
selection: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.describe(
|
|
||||||
"Selected text for inline comments (max 250 chars). Required when type is 'inline'.",
|
|
||||||
),
|
|
||||||
parentCommentId: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.describe("Parent comment ID to create a reply (max 2 nesting levels)"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ pageId, content, type, selection, parentCommentId }) => {
|
|
||||||
const result = await docmostClient.createComment(
|
|
||||||
pageId,
|
|
||||||
content,
|
|
||||||
type || "page",
|
|
||||||
selection,
|
|
||||||
parentCommentId,
|
|
||||||
);
|
|
||||||
return jsonContent(result);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Tool: update_comment
|
|
||||||
server.registerTool(
|
|
||||||
"update_comment",
|
|
||||||
{
|
|
||||||
description:
|
|
||||||
"Update an existing comment's content. Only the comment creator can update it. Content is provided as Markdown.",
|
|
||||||
inputSchema: {
|
|
||||||
commentId: z.string().describe("ID of the comment to update"),
|
|
||||||
content: z.string().describe("New comment content in Markdown format"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ commentId, content }) => {
|
|
||||||
const result = await docmostClient.updateComment(commentId, content);
|
|
||||||
return jsonContent(result);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Tool: delete_comment
|
|
||||||
server.registerTool(
|
|
||||||
"delete_comment",
|
|
||||||
{
|
|
||||||
description:
|
|
||||||
"Delete a comment. Only the comment creator or space admin can delete it.",
|
|
||||||
inputSchema: {
|
|
||||||
commentId: z.string().describe("ID of the comment to delete"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ commentId }) => {
|
|
||||||
await docmostClient.deleteComment(commentId);
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{ type: "text", text: `Successfully deleted comment ${commentId}` },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Tool: check_new_comments
|
|
||||||
server.registerTool(
|
|
||||||
"check_new_comments",
|
|
||||||
{
|
|
||||||
description:
|
|
||||||
"Check for new comments across pages in a space since a given timestamp. " +
|
|
||||||
"Optionally scope to a page subtree (folder). Returns only comments created after the specified time.",
|
|
||||||
inputSchema: {
|
|
||||||
spaceId: z.string().describe("Space ID to check for new comments"),
|
|
||||||
since: z
|
|
||||||
.string()
|
|
||||||
.describe(
|
|
||||||
"ISO 8601 timestamp — only return comments created after this time (e.g. '2026-03-10T00:00:00Z')",
|
|
||||||
),
|
|
||||||
parentPageId: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.describe(
|
|
||||||
"Optional root page ID to scope the check to a subtree (folder). " +
|
|
||||||
"Only pages under this parent will be checked.",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ spaceId, since, parentPageId }) => {
|
|
||||||
const result = await docmostClient.checkNewComments(
|
|
||||||
spaceId,
|
|
||||||
since,
|
|
||||||
parentPageId,
|
|
||||||
);
|
|
||||||
return jsonContent(result);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
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) => {
|
run().catch((error) => {
|
||||||
console.error("Fatal error running server:", error);
|
console.error("Fatal error running server:", error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export async function updatePageContentRealtime(
|
|||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
if (provider) provider.destroy();
|
if (provider) provider.destroy();
|
||||||
reject(new Error("Connection timeout to collaboration server"));
|
reject(new Error("Connection timeout to collaboration server"));
|
||||||
}, 120000);
|
}, 25000);
|
||||||
|
|
||||||
const provider = new HocuspocusProvider({
|
const provider = new HocuspocusProvider({
|
||||||
url: wsUrl,
|
url: wsUrl,
|
||||||
|
|||||||
@@ -60,21 +60,6 @@ export function filterPage(page: any, content?: string, subpages?: any[]) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterComment(comment: any, markdownContent?: string) {
|
|
||||||
return {
|
|
||||||
id: comment.id,
|
|
||||||
pageId: comment.pageId,
|
|
||||||
content: markdownContent ?? comment.content,
|
|
||||||
selection: comment.selection || null,
|
|
||||||
type: comment.type || "page",
|
|
||||||
parentCommentId: comment.parentCommentId || null,
|
|
||||||
creatorId: comment.creatorId,
|
|
||||||
creatorName: comment.creator?.name || null,
|
|
||||||
createdAt: comment.createdAt,
|
|
||||||
editedAt: comment.editedAt || null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function filterSearchResult(result: any) {
|
export function filterSearchResult(result: any) {
|
||||||
return {
|
return {
|
||||||
id: result.id,
|
id: result.id,
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* Convert Markdown text to TipTap/ProseMirror JSON format
|
|
||||||
* Used for creating and updating comments via Docmost API
|
|
||||||
*/
|
|
||||||
import { marked } from "marked";
|
|
||||||
import { generateJSON } from "@tiptap/html";
|
|
||||||
import { JSDOM } from "jsdom";
|
|
||||||
import { tiptapExtensions } from "./tiptap-extensions.js";
|
|
||||||
|
|
||||||
// Ensure DOM environment is available (may already be set by collaboration.ts)
|
|
||||||
if (typeof global.document === "undefined") {
|
|
||||||
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");
|
|
||||||
global.window = dom.window as any;
|
|
||||||
global.document = dom.window.document;
|
|
||||||
// @ts-ignore
|
|
||||||
global.Element = dom.window.Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert markdown string to TipTap-compatible ProseMirror JSON
|
|
||||||
*/
|
|
||||||
export async function markdownToTiptapJson(markdown: string): Promise<any> {
|
|
||||||
const html = await marked.parse(markdown);
|
|
||||||
return generateJSON(html, tiptapExtensions);
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ export const tiptapExtensions = [
|
|||||||
heading: {},
|
heading: {},
|
||||||
}),
|
}),
|
||||||
Image.configure({
|
Image.configure({
|
||||||
inline: false,
|
inline: true,
|
||||||
}),
|
}),
|
||||||
Link.configure({
|
Link.configure({
|
||||||
openOnClick: false,
|
openOnClick: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user