docmost-mcp-oss 0.1.0__tar.gz
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.
- docmost_mcp_oss-0.1.0/.env.example +16 -0
- docmost_mcp_oss-0.1.0/.github/workflows/ci.yml +82 -0
- docmost_mcp_oss-0.1.0/.github/workflows/release.yml +59 -0
- docmost_mcp_oss-0.1.0/.gitignore +14 -0
- docmost_mcp_oss-0.1.0/CONTRIBUTING.md +89 -0
- docmost_mcp_oss-0.1.0/LICENSE +21 -0
- docmost_mcp_oss-0.1.0/PKG-INFO +237 -0
- docmost_mcp_oss-0.1.0/README.md +206 -0
- docmost_mcp_oss-0.1.0/docmost_mcp_oss/__init__.py +6 -0
- docmost_mcp_oss-0.1.0/docmost_mcp_oss/client.py +580 -0
- docmost_mcp_oss-0.1.0/docmost_mcp_oss/collab.py +437 -0
- docmost_mcp_oss-0.1.0/docmost_mcp_oss/server.py +451 -0
- docmost_mcp_oss-0.1.0/docs/DOCMOST-API.md +298 -0
- docmost_mcp_oss-0.1.0/docs/YJS-EDITING.md +208 -0
- docmost_mcp_oss-0.1.0/pyproject.toml +57 -0
- docmost_mcp_oss-0.1.0/tests/smoke_live.py +233 -0
- docmost_mcp_oss-0.1.0/tests/smoke_mcp_live.py +136 -0
- docmost_mcp_oss-0.1.0/tests/smoke_yjs_live.py +133 -0
- docmost_mcp_oss-0.1.0/tests/test_client.py +363 -0
- docmost_mcp_oss-0.1.0/tests/test_tools.py +78 -0
- docmost_mcp_oss-0.1.0/uv.lock +2109 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# URL of your Docmost instance.
|
|
2
|
+
# - Self-hosted: the base of your installation, e.g. https://docmost.example.com
|
|
3
|
+
# - Cloud: includes your workspace's subdomain, e.g. https://mi-org.docmost.com
|
|
4
|
+
DOCMOST_URL=https://docmost.example.com
|
|
5
|
+
|
|
6
|
+
# --- Option A: API Key (requires an Enterprise license) ---
|
|
7
|
+
# The key is sent as `Authorization: Bearer <key>`.
|
|
8
|
+
DOCMOST_API_KEY=
|
|
9
|
+
|
|
10
|
+
# --- Option B: login with email/password (works on OSS) ---
|
|
11
|
+
# The server obtains the `authToken` cookie and reuses it.
|
|
12
|
+
DOCMOST_EMAIL=
|
|
13
|
+
DOCMOST_PASSWORD=
|
|
14
|
+
|
|
15
|
+
# HTTP timeout in seconds (optional, defaults to 30)
|
|
16
|
+
DOCMOST_TIMEOUT=30
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Lint and format
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --frozen
|
|
27
|
+
|
|
28
|
+
- name: Ruff lint
|
|
29
|
+
run: uv run ruff check .
|
|
30
|
+
|
|
31
|
+
- name: Ruff format
|
|
32
|
+
run: uv run ruff format --check .
|
|
33
|
+
|
|
34
|
+
test:
|
|
35
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python-version: ["3.10", "3.12", "3.13"]
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
|
|
44
|
+
- name: Install uv
|
|
45
|
+
uses: astral-sh/setup-uv@v5
|
|
46
|
+
with:
|
|
47
|
+
python-version: ${{ matrix.python-version }}
|
|
48
|
+
enable-cache: true
|
|
49
|
+
|
|
50
|
+
# The `yjs` extra is installed so the collab module is imported too.
|
|
51
|
+
- name: Install dependencies
|
|
52
|
+
run: uv sync --frozen --extra yjs
|
|
53
|
+
|
|
54
|
+
# Offline suites: they run against a simulated Docmost and need no
|
|
55
|
+
# credentials. The `smoke_*` scripts hit a real instance and are
|
|
56
|
+
# intentionally not run here.
|
|
57
|
+
- name: Client tests
|
|
58
|
+
run: uv run python tests/test_client.py
|
|
59
|
+
|
|
60
|
+
- name: Tool registry
|
|
61
|
+
run: uv run python tests/test_tools.py
|
|
62
|
+
|
|
63
|
+
build:
|
|
64
|
+
name: Build distribution
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
|
|
69
|
+
- name: Install uv
|
|
70
|
+
uses: astral-sh/setup-uv@v5
|
|
71
|
+
|
|
72
|
+
- name: Build sdist and wheel
|
|
73
|
+
run: uv build
|
|
74
|
+
|
|
75
|
+
- name: Inspect the wheel
|
|
76
|
+
run: |
|
|
77
|
+
python -m zipfile -l dist/*.whl > /tmp/wheel.txt
|
|
78
|
+
cat /tmp/wheel.txt
|
|
79
|
+
grep -q "docmost_mcp_oss/client.py" /tmp/wheel.txt
|
|
80
|
+
grep -q "docmost_mcp_oss/collab.py" /tmp/wheel.txt
|
|
81
|
+
grep -q "licenses/LICENSE" /tmp/wheel.txt
|
|
82
|
+
echo "OK: wheel contains the package and the LICENSE"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build distribution
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
|
|
21
|
+
# Guard against a tag that does not match the packaged version.
|
|
22
|
+
- name: Check the tag matches the project version
|
|
23
|
+
run: |
|
|
24
|
+
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
25
|
+
TAG="${GITHUB_REF_NAME#v}"
|
|
26
|
+
if [ "$TAG" != "$VERSION" ]; then
|
|
27
|
+
echo "::error::tag v$TAG does not match project version $VERSION"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
echo "OK: tag v$TAG matches version $VERSION"
|
|
31
|
+
|
|
32
|
+
- name: Build sdist and wheel
|
|
33
|
+
run: uv build
|
|
34
|
+
|
|
35
|
+
- name: Upload artifacts
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
publish:
|
|
42
|
+
name: Publish to PyPI
|
|
43
|
+
needs: build
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
environment:
|
|
46
|
+
name: pypi
|
|
47
|
+
# Update this if the PyPI project name differs from the repository name.
|
|
48
|
+
url: https://pypi.org/p/docmost-mcp-oss
|
|
49
|
+
permissions:
|
|
50
|
+
id-token: write # Trusted Publishing (OIDC): no API token needed
|
|
51
|
+
steps:
|
|
52
|
+
- name: Download artifacts
|
|
53
|
+
uses: actions/download-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
|
|
58
|
+
- name: Publish
|
|
59
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving `docmost-mcp-oss`. This project is small, so
|
|
4
|
+
the process is intentionally light.
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
The project is managed with [uv](https://docs.astral.sh/uv/):
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
git clone https://github.com/abelsr/docmost-mcp-oss
|
|
12
|
+
cd docmost-mcp-oss
|
|
13
|
+
uv sync --extra yjs # --extra yjs is needed for the collaboration module
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
There is no need to activate the environment: prefix commands with `uv run`.
|
|
17
|
+
|
|
18
|
+
## Checks
|
|
19
|
+
|
|
20
|
+
These are the same checks CI runs, and they need **no credentials**:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv run ruff check . # lint
|
|
24
|
+
uv run ruff format --check . # formatting
|
|
25
|
+
uv run python tests/test_client.py # client against a simulated Docmost
|
|
26
|
+
uv run python tests/test_tools.py # MCP tool registry
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`ruff format` also formats Python code blocks inside Markdown files, so run it
|
|
30
|
+
before committing if you touch the docs.
|
|
31
|
+
|
|
32
|
+
## Live tests
|
|
33
|
+
|
|
34
|
+
Three suites hit a **real** Docmost instance and are therefore not run in CI.
|
|
35
|
+
They read credentials from a `.docmost-creds.json` file in the repo root, which
|
|
36
|
+
is git-ignored:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"url": "https://docmost.example.com",
|
|
41
|
+
"email": "you@example.com",
|
|
42
|
+
"password": "your-password"
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv run python tests/smoke_live.py # read-only
|
|
48
|
+
uv run python tests/smoke_live.py --write # create/rename/delete
|
|
49
|
+
uv run python tests/smoke_mcp_live.py --write # through the MCP layer
|
|
50
|
+
uv run --extra yjs python tests/smoke_yjs_live.py # body editing over Yjs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Use an API key (`"api_key": "..."`) instead of a password if your account has
|
|
54
|
+
MFA enabled. Prefer a throwaway account or a test space: `--write` creates and
|
|
55
|
+
deletes pages.
|
|
56
|
+
|
|
57
|
+
## Guidance
|
|
58
|
+
|
|
59
|
+
- **Nothing about this API is documented upstream.** Everything in
|
|
60
|
+
[`docs/DOCMOST-API.md`](docs/DOCMOST-API.md) and
|
|
61
|
+
[`docs/YJS-EDITING.md`](docs/YJS-EDITING.md) was derived from source code and
|
|
62
|
+
from probing real instances. If you change a behaviour, say *how you verified
|
|
63
|
+
it* — observed facts, not assumptions.
|
|
64
|
+
- **Several behaviours differ between Docmost builds.** The client normalizes
|
|
65
|
+
them (raw lists vs `{items, meta}`, cookie vs token login, optional vs required
|
|
66
|
+
`spaceId`). Keep those normalizations tolerant rather than assuming one build.
|
|
67
|
+
- **Tool docstrings are the LLM's interface.** They must stay accurate about
|
|
68
|
+
limitations (for example, that `/pages/update` only changes titles).
|
|
69
|
+
- **Typed signatures matter.** FastMCP only fills a tool's structured output
|
|
70
|
+
(`.data`) when the return type is annotated, hence `-> list[dict]` / `-> dict`.
|
|
71
|
+
`tests/test_tools.py` guards against a regression here.
|
|
72
|
+
|
|
73
|
+
## Adding a tool
|
|
74
|
+
|
|
75
|
+
1. Add the method to `docmost_mcp_oss/client.py` (or `collab.py`).
|
|
76
|
+
2. Register it in `docmost_mcp_oss/server.py` with `@mcp.tool`, a **typed**
|
|
77
|
+
signature, and a docstring with an `Args:` section.
|
|
78
|
+
3. Add it to `EXPECTED_TOOLS` in `tests/test_tools.py`.
|
|
79
|
+
|
|
80
|
+
## Commits
|
|
81
|
+
|
|
82
|
+
Short imperative subject lines, English, in the style of
|
|
83
|
+
`Add page history tool` or `Fix spaceId requirement in search`. Explain the
|
|
84
|
+
*why* in the body when the change is not obvious.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
By contributing you agree that your contributions are licensed under the
|
|
89
|
+
[MIT License](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abel Santillan Rodriguez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: docmost-mcp-oss
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server (FastMCP) for Docmost: read and edit pages, spaces and comments over the REST API and the Yjs collaboration WebSocket
|
|
5
|
+
Project-URL: Homepage, https://github.com/abelsr/docmost-mcp-oss
|
|
6
|
+
Project-URL: Repository, https://github.com/abelsr/docmost-mcp-oss
|
|
7
|
+
Project-URL: Issues, https://github.com/abelsr/docmost-mcp-oss/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/abelsr/docmost-mcp-oss/commits/main
|
|
9
|
+
Author: Abel Santillan Rodriguez
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: docmost,fastmcp,mcp,model-context-protocol,wiki,yjs
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Documentation
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: fastmcp<5,>=4.0
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: python-dotenv>=1.0
|
|
27
|
+
Provides-Extra: yjs
|
|
28
|
+
Requires-Dist: pycrdt>=0.14; extra == 'yjs'
|
|
29
|
+
Requires-Dist: websockets>=13; extra == 'yjs'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# docmost-mcp-oss
|
|
33
|
+
|
|
34
|
+
[](https://github.com/abelsr/docmost-mcp-oss/actions/workflows/ci.yml)
|
|
35
|
+
[](https://github.com/abelsr/docmost-mcp-oss/blob/main/LICENSE)
|
|
36
|
+
[](pyproject.toml)
|
|
37
|
+
|
|
38
|
+
An [MCP](https://modelcontextprotocol.io) server built with **FastMCP** that exposes the REST API of [Docmost](https://docmost.com) as tools for AI assistants (Claude Desktop, Claude Code, Cursor, VS Code…).
|
|
39
|
+
|
|
40
|
+
Managed with **[uv](https://docs.astral.sh/uv/)**.
|
|
41
|
+
|
|
42
|
+
> 📄 Full API research: [`docs/DOCMOST-API.md`](https://github.com/abelsr/docmost-mcp-oss/blob/main/docs/DOCMOST-API.md).
|
|
43
|
+
> Verified against a real Docmost instance (not just the documentation).
|
|
44
|
+
|
|
45
|
+
## Why a custom MCP?
|
|
46
|
+
|
|
47
|
+
Docmost ships with an **official** MCP, but it requires a *Business/Enterprise* license and is enabled from *Settings → AI settings → MCP*. This project uses the **internal API** (the same one the web UI consumes), which is also available in the **self-hosted OSS** edition.
|
|
48
|
+
|
|
49
|
+
**About the name:** the `-oss` suffix distinguishes this package from the unrelated
|
|
50
|
+
[`docmost-mcp`](https://pypi.org/project/docmost-mcp/) already on PyPI. They are different
|
|
51
|
+
projects by different authors, and they would collide if installed side by side (both used to
|
|
52
|
+
ship the same import package). This one targets self-hosted Docmost and can edit page bodies;
|
|
53
|
+
install it as `docmost-mcp-oss`.
|
|
54
|
+
|
|
55
|
+
## Exposed tools (20)
|
|
56
|
+
|
|
57
|
+
| Category | Tools |
|
|
58
|
+
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
59
|
+
| Pages | `search_pages`, `get_page`, `create_page`, `update_page`, **`update_page_content`**, `delete_page`, `restore_page`, `move_page`, `list_recent_pages`, `list_child_pages`, `get_page_breadcrumbs`, `get_page_history` |
|
|
60
|
+
| Spaces | `list_spaces`, `get_space`, `create_space` |
|
|
61
|
+
| Comments | `get_comments`, `create_comment`, `update_comment` |
|
|
62
|
+
| User | `get_current_user`, `list_workspace_members` |
|
|
63
|
+
|
|
64
|
+
Two tools stand out:
|
|
65
|
+
|
|
66
|
+
- **`get_page`** returns **metadata and content** in Markdown (it combines `/pages/info` with `/pages/export`, because the former **does not** return the body).
|
|
67
|
+
- **`update_page_content`** **replaces the body of an existing page**. The REST API can't do this: it writes directly to the Yjs document over the collaboration WebSocket. Requires the `yjs` extra and takes ~13 s (Docmost persists with a 10 s *debounce*).
|
|
68
|
+
|
|
69
|
+
## Editing the body of an existing page
|
|
70
|
+
|
|
71
|
+
Docmost's REST API **ignores the `content` field** in `/pages/create` and `/pages/update` (they respond 200 but save nothing): the body lives in the **Yjs** collaboration server.
|
|
72
|
+
|
|
73
|
+
That's why there are two distinct paths:
|
|
74
|
+
|
|
75
|
+
| Operation | How |
|
|
76
|
+
| ---------------------------------------------- | ------------------------------------------------- |
|
|
77
|
+
| Read content | `get_page` |
|
|
78
|
+
| Create page**with** content | `create_page` (uses `/pages/import`) |
|
|
79
|
+
| **Replace the body of an existing page** | **`update_page_content`** (Yjs WebSocket) |
|
|
80
|
+
| Rename | `update_page` |
|
|
81
|
+
| Delete / restore / move | ✅ |
|
|
82
|
+
|
|
83
|
+
`update_page_content` opens the `wss://<host>/collab` WebSocket, syncs the document, replaces the content and waits for it to persist. Markdown is
|
|
84
|
+
converted using Docmost's own converter, so it supports the full schema (tables, lists, code, quotes, images…).
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv sync --extra yjs # enables update_page_content
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Technical details of the protocol: [`docs/YJS-EDITING.md`](https://github.com/abelsr/docmost-mcp-oss/blob/main/docs/YJS-EDITING.md).
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
`uv` creates the virtual environment and installs the dependencies (pinned in `uv.lock`):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
uv sync
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
No need to activate the environment: use `uv run …`.
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
cp .env.example .env # then edit the values
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```dotenv
|
|
109
|
+
DOCMOST_URL=https://docmost.example.com
|
|
110
|
+
|
|
111
|
+
# Option A — API Key
|
|
112
|
+
DOCMOST_API_KEY=dm_xxx
|
|
113
|
+
|
|
114
|
+
# Option B — login (works on OSS)
|
|
115
|
+
DOCMOST_EMAIL=tu@email.com
|
|
116
|
+
DOCMOST_PASSWORD=tu_password
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Verify the connection:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv run docmost-mcp-oss --check
|
|
123
|
+
# -> OK: authenticated as tu@email.com (https://docmost.example.com)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Usage
|
|
127
|
+
|
|
128
|
+
### stdio (recommended for local use)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
uv run docmost-mcp-oss
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### HTTP (for remote access)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
uv run docmost-mcp-oss --http --port 8000
|
|
138
|
+
# MCP endpoint: http://127.0.0.1:8000/mcp
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Connecting to MCP clients
|
|
142
|
+
|
|
143
|
+
### Claude Desktop (`claude_desktop_config.json`)
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"mcpServers": {
|
|
148
|
+
"docmost": {
|
|
149
|
+
"command": "uv",
|
|
150
|
+
"args": ["--directory", "/ruta/a/docmost-mcp-oss", "run", "docmost-mcp-oss"],
|
|
151
|
+
"env": {
|
|
152
|
+
"DOCMOST_URL": "https://docmost.example.com",
|
|
153
|
+
"DOCMOST_EMAIL": "tu@email.com",
|
|
154
|
+
"DOCMOST_PASSWORD": "tu_password"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Claude Code
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
claude mcp add docmost -- uv --directory /ruta/a/docmost-mcp-oss run docmost-mcp-oss
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Cursor (`.cursor/mcp.json`)
|
|
168
|
+
|
|
169
|
+
Same as Claude Desktop, inside the `mcpServers` key.
|
|
170
|
+
|
|
171
|
+
## Tests
|
|
172
|
+
|
|
173
|
+
| Command | What it validates | Needs instance |
|
|
174
|
+
| ----------------------------------------------------- | ------------------------------------------ | -------------- |
|
|
175
|
+
| `uv run python tests/test_client.py` | Client against a mocked Docmost (12 cases) | No |
|
|
176
|
+
| `uv run python tests/test_tools.py` | MCP tool registry | No |
|
|
177
|
+
| `uv run python tests/smoke_live.py` | Read-only against a real instance | Yes |
|
|
178
|
+
| `uv run python tests/smoke_mcp_live.py` | Tools through the MCP layer | Yes |
|
|
179
|
+
| `uv run --extra yjs python tests/smoke_yjs_live.py` | **Body editing via Yjs** (7 checks) | Yes |
|
|
180
|
+
| `uv run ruff check .` | Lint | No |
|
|
181
|
+
|
|
182
|
+
The live tests read credentials from `.docmost-creds.json` (ignored by git):
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"url": "https://docmost.example.com",
|
|
187
|
+
"email": "you@example.com",
|
|
188
|
+
"password": "your-password"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`--write` adds a **create → update → get → delete** cycle over a test page
|
|
193
|
+
(use `--write keep` to keep it).
|
|
194
|
+
|
|
195
|
+
## Implementation notes
|
|
196
|
+
|
|
197
|
+
Things that are **not** obvious and that the client already handles:
|
|
198
|
+
|
|
199
|
+
- **All endpoints are `POST`** and respond with `{data, success, status}`; the client unwraps `data`.
|
|
200
|
+
- **Content does not come from `/pages/info`**: it is fetched via `/pages/export` (`markdown`|`html`), which responds with the raw file, no wrapper.
|
|
201
|
+
- **Only `/pages/import` persists content over REST**; `/pages/create` and `/pages/update` ignore `content`. To edit the body of an already-created page you must write to the Yjs document (see above).
|
|
202
|
+
- **Authentication:** both real variants are supported — the `authToken` cookie (httpOnly) and `data.tokens.accessToken` forwarded as `Bearer`.
|
|
203
|
+
- **`/search` requires `query` and `spaceId`**; if you don't provide a space, it fans out across all accessible spaces and merges by `rank`.
|
|
204
|
+
- **`/pages/sidebar-pages` requires `spaceId`**; if you only provide a page, its space is resolved first.
|
|
205
|
+
- **Lexical search (PostgreSQL FTS):** stopwords ("a", "de", "the") return 0 results.
|
|
206
|
+
- **Heterogeneous response shapes:** some builds return `{items, meta}` and others raw lists; the client normalizes both.
|
|
207
|
+
- **Permissions:** the MCP acts *as* the authenticated user; it can never do more than the user can.
|
|
208
|
+
|
|
209
|
+
## Structure
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
docmost-mcp-oss/
|
|
213
|
+
├── docmost_mcp_oss/
|
|
214
|
+
│ ├── __init__.py
|
|
215
|
+
│ ├── client.py # async HTTP client for the Docmost REST API
|
|
216
|
+
│ ├── collab.py # Yjs WebSocket: read/write page bodies
|
|
217
|
+
│ └── server.py # FastMCP server + tools
|
|
218
|
+
├── docs/
|
|
219
|
+
│ ├── DOCMOST-API.md # API research (OSS + real instance)
|
|
220
|
+
│ └── YJS-EDITING.md # collaboration WebSocket protocol
|
|
221
|
+
├── tests/
|
|
222
|
+
│ ├── test_client.py # mocked, no network
|
|
223
|
+
│ ├── test_tools.py # MCP tool registry, no network
|
|
224
|
+
│ ├── smoke_live.py # real instance, read-only
|
|
225
|
+
│ ├── smoke_mcp_live.py # MCP layer against a real instance
|
|
226
|
+
│ └── smoke_yjs_live.py # body editing via Yjs
|
|
227
|
+
├── .github/workflows/ci.yml # lint, tests and packaging
|
|
228
|
+
├── CONTRIBUTING.md
|
|
229
|
+
├── LICENSE # MIT
|
|
230
|
+
├── pyproject.toml # metadata, dependencies and ruff config
|
|
231
|
+
├── uv.lock # reproducible resolution (it is versioned)
|
|
232
|
+
└── .env.example
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
[MIT](https://github.com/abelsr/docmost-mcp-oss/blob/main/LICENSE) © 2026 Abel Santillan Rodriguez
|