docmost-mcp-remote 1.0.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_remote-1.0.0/.dockerignore +10 -0
- docmost_mcp_remote-1.0.0/.env.example +46 -0
- docmost_mcp_remote-1.0.0/.github/dependabot.yml +17 -0
- docmost_mcp_remote-1.0.0/.github/workflows/ci.yml +145 -0
- docmost_mcp_remote-1.0.0/.github/workflows/release.yml +163 -0
- docmost_mcp_remote-1.0.0/.gitignore +220 -0
- docmost_mcp_remote-1.0.0/CHANGELOG.md +43 -0
- docmost_mcp_remote-1.0.0/CONTRIBUTING.md +55 -0
- docmost_mcp_remote-1.0.0/Dockerfile +27 -0
- docmost_mcp_remote-1.0.0/LICENSE +201 -0
- docmost_mcp_remote-1.0.0/NOTICE +5 -0
- docmost_mcp_remote-1.0.0/PKG-INFO +434 -0
- docmost_mcp_remote-1.0.0/README.md +400 -0
- docmost_mcp_remote-1.0.0/README.ru.md +123 -0
- docmost_mcp_remote-1.0.0/SECURITY.md +41 -0
- docmost_mcp_remote-1.0.0/compose.yaml +56 -0
- docmost_mcp_remote-1.0.0/docs/DESIGN.md +161 -0
- docmost_mcp_remote-1.0.0/docs/GLOSSARY.md +27 -0
- docmost_mcp_remote-1.0.0/docs/RELEASING.md +62 -0
- docmost_mcp_remote-1.0.0/docs/ROADMAP.md +201 -0
- docmost_mcp_remote-1.0.0/docs/adr/0001-new-repository-from-scratch.md +23 -0
- docmost_mcp_remote-1.0.0/docs/adr/0002-per-member-docmost-identity.md +16 -0
- docmost_mcp_remote-1.0.0/docs/adr/0003-oauth-and-bearer-with-postgres.md +15 -0
- docmost_mcp_remote-1.0.0/docs/adr/0004-scoped-writes-no-permanent-delete.md +18 -0
- docmost_mcp_remote-1.0.0/docs/adr/0005-python-fastmcp-uv.md +25 -0
- docmost_mcp_remote-1.0.0/docs/adr/0006-apache-2-license.md +44 -0
- docmost_mcp_remote-1.0.0/docs/docmost-api-notes.md +94 -0
- docmost_mcp_remote-1.0.0/docs/security-model.md +95 -0
- docmost_mcp_remote-1.0.0/pyproject.toml +81 -0
- docmost_mcp_remote-1.0.0/server.json +91 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/__init__.py +10 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/__main__.py +54 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/admin.py +174 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/auth.py +20 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/config.py +167 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/crypto.py +96 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/__init__.py +1 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/admin_store.py +257 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/engine.py +81 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/migrations/env.py +41 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/migrations/script.py.mako +22 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/migrations/versions/0001_members_sessions_grants_and_tokens.py +111 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/migrations/versions/0002_oauth_clients_requests_and_codes.py +74 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/migrations/versions/0003_snapshots.py +49 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/migrations/versions/0004_audit_log.py +47 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/oauth_store.py +336 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/schema.py +209 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/snapshot_store.py +118 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/db/store.py +273 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/__init__.py +28 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/backend.py +101 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/ce.py +335 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/errors.py +39 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/models.py +133 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/positions.py +145 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/prosemirror.py +58 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/docmost/version.py +49 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/guard.py +80 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/members.py +164 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/oauth.py +345 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/py.typed +0 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/ratelimit.py +56 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/server.py +146 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/tools/__init__.py +1 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/tools/account.py +38 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/tools/common.py +35 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/tools/read.py +254 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/tools/spaces.py +47 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/tools/untrusted.py +35 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/tools/write.py +442 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/__init__.py +1 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/routes.py +418 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/style.css +19 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/templates/account.html +58 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/templates/base.html +15 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/templates/consent.html +22 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/templates/login.html +14 -0
- docmost_mcp_remote-1.0.0/src/docmost_mcp_remote/web/templates/message.html +6 -0
- docmost_mcp_remote-1.0.0/tests/__init__.py +0 -0
- docmost_mcp_remote-1.0.0/tests/conftest.py +92 -0
- docmost_mcp_remote-1.0.0/tests/fakes.py +84 -0
- docmost_mcp_remote-1.0.0/tests/harness.py +91 -0
- docmost_mcp_remote-1.0.0/tests/integration/__init__.py +0 -0
- docmost_mcp_remote-1.0.0/tests/integration/compose.yaml +33 -0
- docmost_mcp_remote-1.0.0/tests/integration/conftest.py +112 -0
- docmost_mcp_remote-1.0.0/tests/integration/test_docmost_ce.py +153 -0
- docmost_mcp_remote-1.0.0/tests/integration/test_oauth_flow.py +27 -0
- docmost_mcp_remote-1.0.0/tests/integration/test_sign_in.py +32 -0
- docmost_mcp_remote-1.0.0/tests/integration/test_tools.py +85 -0
- docmost_mcp_remote-1.0.0/tests/integration/test_write_tools.py +106 -0
- docmost_mcp_remote-1.0.0/tests/test_admin.py +154 -0
- docmost_mcp_remote-1.0.0/tests/test_config.py +94 -0
- docmost_mcp_remote-1.0.0/tests/test_crypto.py +65 -0
- docmost_mcp_remote-1.0.0/tests/test_docmost_client.py +262 -0
- docmost_mcp_remote-1.0.0/tests/test_docmost_helpers.py +58 -0
- docmost_mcp_remote-1.0.0/tests/test_guard.py +94 -0
- docmost_mcp_remote-1.0.0/tests/test_members.py +144 -0
- docmost_mcp_remote-1.0.0/tests/test_migrations.py +102 -0
- docmost_mcp_remote-1.0.0/tests/test_oauth.py +552 -0
- docmost_mcp_remote-1.0.0/tests/test_positions.py +86 -0
- docmost_mcp_remote-1.0.0/tests/test_ratelimit.py +40 -0
- docmost_mcp_remote-1.0.0/tests/test_read_tools.py +194 -0
- docmost_mcp_remote-1.0.0/tests/test_release_metadata.py +62 -0
- docmost_mcp_remote-1.0.0/tests/test_server.py +57 -0
- docmost_mcp_remote-1.0.0/tests/test_store.py +98 -0
- docmost_mcp_remote-1.0.0/tests/test_untrusted.py +55 -0
- docmost_mcp_remote-1.0.0/tests/test_web.py +134 -0
- docmost_mcp_remote-1.0.0/tests/test_write_tools.py +440 -0
- docmost_mcp_remote-1.0.0/uv.lock +2808 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Public https URL where MCP clients reach this server (behind your reverse proxy).
|
|
2
|
+
PUBLIC_URL=https://mcp.example.com
|
|
3
|
+
|
|
4
|
+
# Base URL of your Docmost instance.
|
|
5
|
+
DOCMOST_URL=https://docs.example.com
|
|
6
|
+
# Set to true only for a Docmost reachable over http on a trusted private network.
|
|
7
|
+
DOCMOST_ALLOW_INSECURE_HTTP=false
|
|
8
|
+
|
|
9
|
+
# Password for the bundled Postgres. Use a long random value.
|
|
10
|
+
POSTGRES_PASSWORD=
|
|
11
|
+
|
|
12
|
+
# Encrypts stored Docmost sessions and snapshots and signs cookies. At least 32 characters,
|
|
13
|
+
# for example the output of: openssl rand -hex 32
|
|
14
|
+
# Losing it signs every member out; leaking it together with the database
|
|
15
|
+
# exposes their Docmost sessions.
|
|
16
|
+
SECRET_KEY=
|
|
17
|
+
# During a key rotation: the old key, so existing sessions stay readable.
|
|
18
|
+
SECRET_KEY_PREVIOUS=
|
|
19
|
+
|
|
20
|
+
# Comma-separated emails allowed to sign in. Empty allows every Docmost user.
|
|
21
|
+
ALLOWED_EMAILS=
|
|
22
|
+
|
|
23
|
+
# Comma-separated space slugs or ids members may use through this server.
|
|
24
|
+
# Empty allows every space the member can see in Docmost.
|
|
25
|
+
ALLOWED_SPACES=
|
|
26
|
+
|
|
27
|
+
# When a member's Docmost session expires: "ask" them to sign in again (default),
|
|
28
|
+
# or "stored_password" to keep their password encrypted and sign in automatically.
|
|
29
|
+
RELOGIN_MODE=ask
|
|
30
|
+
|
|
31
|
+
# Days page snapshots (copies of page bodies taken before every change) are kept.
|
|
32
|
+
SNAPSHOT_RETENTION_DAYS=30
|
|
33
|
+
# Days audit log entries (who called which tool on which page) are kept.
|
|
34
|
+
AUDIT_RETENTION_DAYS=90
|
|
35
|
+
# Tool calls one member may make per minute, over all their clients.
|
|
36
|
+
TOOL_CALLS_PER_MINUTE=60
|
|
37
|
+
|
|
38
|
+
# Reverse proxies whose X-Forwarded-For header is trusted for rate limiting.
|
|
39
|
+
# The compose file publishes the port on 127.0.0.1 only and trusts every
|
|
40
|
+
# client by default ("*"). Set the proxy's address if you publish it elsewhere.
|
|
41
|
+
# FORWARDED_ALLOW_IPS=*
|
|
42
|
+
|
|
43
|
+
LOG_LEVEL=info
|
|
44
|
+
|
|
45
|
+
# Image tag. "1" follows 1.x releases; a full version such as 1.0.0 pins one.
|
|
46
|
+
# IMAGE_TAG=1
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: uv
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
groups:
|
|
8
|
+
python:
|
|
9
|
+
patterns: ["*"]
|
|
10
|
+
- package-ecosystem: github-actions
|
|
11
|
+
directory: /
|
|
12
|
+
schedule:
|
|
13
|
+
interval: weekly
|
|
14
|
+
- package-ecosystem: docker
|
|
15
|
+
directory: /
|
|
16
|
+
schedule:
|
|
17
|
+
interval: weekly
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
lint:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v5
|
|
20
|
+
- uses: astral-sh/setup-uv@v7
|
|
21
|
+
with:
|
|
22
|
+
enable-cache: true
|
|
23
|
+
- run: uv sync --locked
|
|
24
|
+
- run: uv run ruff format --check .
|
|
25
|
+
- run: uv run ruff check .
|
|
26
|
+
- run: uv run mypy
|
|
27
|
+
|
|
28
|
+
test:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
services:
|
|
31
|
+
postgres:
|
|
32
|
+
image: postgres:17-alpine
|
|
33
|
+
env:
|
|
34
|
+
POSTGRES_USER: test
|
|
35
|
+
POSTGRES_PASSWORD: test
|
|
36
|
+
POSTGRES_DB: test
|
|
37
|
+
ports:
|
|
38
|
+
- 5432:5432
|
|
39
|
+
options: >-
|
|
40
|
+
--health-cmd "pg_isready -U test" --health-interval 2s --health-timeout 3s
|
|
41
|
+
--health-retries 30
|
|
42
|
+
env:
|
|
43
|
+
DATABASE_TEST_URL: postgresql://test:test@localhost:5432/test
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false
|
|
46
|
+
matrix:
|
|
47
|
+
python: ["3.12", "3.13"]
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v5
|
|
50
|
+
- uses: astral-sh/setup-uv@v7
|
|
51
|
+
with:
|
|
52
|
+
enable-cache: true
|
|
53
|
+
python-version: ${{ matrix.python }}
|
|
54
|
+
- run: uv sync --locked
|
|
55
|
+
- run: uv run pytest
|
|
56
|
+
|
|
57
|
+
audit:
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v5
|
|
61
|
+
- uses: astral-sh/setup-uv@v7
|
|
62
|
+
with:
|
|
63
|
+
enable-cache: true
|
|
64
|
+
- run: uv sync --locked
|
|
65
|
+
- name: Audit locked runtime dependencies
|
|
66
|
+
run: |
|
|
67
|
+
uv export --frozen --no-dev --no-emit-project --format requirements-txt -o requirements.txt
|
|
68
|
+
uv run pip-audit --strict --disable-pip --require-hashes -r requirements.txt
|
|
69
|
+
|
|
70
|
+
integration:
|
|
71
|
+
name: integration (Docmost ${{ matrix.docmost }})
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
continue-on-error: ${{ matrix.canary }}
|
|
74
|
+
strategy:
|
|
75
|
+
fail-fast: false
|
|
76
|
+
matrix:
|
|
77
|
+
# Oldest supported, previous and newest tested release. "latest" warns early
|
|
78
|
+
# when a new Docmost release is out and the tested range needs a bump.
|
|
79
|
+
docmost: ["0.70.3", "0.96.0", "0.97.0"]
|
|
80
|
+
canary: [false]
|
|
81
|
+
include:
|
|
82
|
+
- docmost: latest
|
|
83
|
+
canary: true
|
|
84
|
+
services:
|
|
85
|
+
postgres:
|
|
86
|
+
image: postgres:17-alpine
|
|
87
|
+
env:
|
|
88
|
+
POSTGRES_USER: test
|
|
89
|
+
POSTGRES_PASSWORD: test
|
|
90
|
+
POSTGRES_DB: test
|
|
91
|
+
ports:
|
|
92
|
+
- 5433:5432
|
|
93
|
+
options: >-
|
|
94
|
+
--health-cmd "pg_isready -U test" --health-interval 2s --health-timeout 3s
|
|
95
|
+
--health-retries 30
|
|
96
|
+
env:
|
|
97
|
+
DOCMOST_VERSION: ${{ matrix.docmost }}
|
|
98
|
+
DOCMOST_TEST_URL: http://localhost:3000
|
|
99
|
+
DATABASE_TEST_URL: postgresql://test:test@localhost:5433/test
|
|
100
|
+
steps:
|
|
101
|
+
- uses: actions/checkout@v5
|
|
102
|
+
- uses: astral-sh/setup-uv@v7
|
|
103
|
+
with:
|
|
104
|
+
enable-cache: true
|
|
105
|
+
- name: Start Docmost
|
|
106
|
+
run: docker compose -f tests/integration/compose.yaml up -d --wait --wait-timeout 180
|
|
107
|
+
- name: Wait for Docmost
|
|
108
|
+
run: |
|
|
109
|
+
for i in $(seq 1 90); do
|
|
110
|
+
if curl -fsS http://localhost:3000/api/health; then exit 0; fi
|
|
111
|
+
sleep 2
|
|
112
|
+
done
|
|
113
|
+
docker compose -f tests/integration/compose.yaml logs docmost
|
|
114
|
+
exit 1
|
|
115
|
+
- run: uv sync --locked
|
|
116
|
+
- run: uv run pytest tests/integration
|
|
117
|
+
- name: Docmost logs
|
|
118
|
+
if: failure()
|
|
119
|
+
run: docker compose -f tests/integration/compose.yaml logs docmost
|
|
120
|
+
|
|
121
|
+
docker:
|
|
122
|
+
runs-on: ubuntu-latest
|
|
123
|
+
steps:
|
|
124
|
+
- uses: actions/checkout@v5
|
|
125
|
+
- name: Prepare .env
|
|
126
|
+
run: |
|
|
127
|
+
cp .env.example .env
|
|
128
|
+
sed -i "s/^POSTGRES_PASSWORD=$/POSTGRES_PASSWORD=ci-only/" .env
|
|
129
|
+
sed -i "s/^SECRET_KEY=$/SECRET_KEY=$(openssl rand -hex 32)/" .env
|
|
130
|
+
docker compose config --quiet
|
|
131
|
+
- name: Build the image
|
|
132
|
+
run: docker build -t ghcr.io/bbik52/docmost-mcp-remote:ci .
|
|
133
|
+
- name: Start the compose stack
|
|
134
|
+
run: IMAGE_TAG=ci docker compose up -d --wait --wait-timeout 120
|
|
135
|
+
- name: Smoke test
|
|
136
|
+
run: |
|
|
137
|
+
curl -fsS http://127.0.0.1:8000/healthz
|
|
138
|
+
curl -fsS -o /dev/null http://127.0.0.1:8000/login
|
|
139
|
+
test "$(curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:8000/mcp)" = 401
|
|
140
|
+
curl -fsS http://127.0.0.1:8000/.well-known/oauth-protected-resource/mcp
|
|
141
|
+
docker compose exec -T server docmost-mcp-remote admin members
|
|
142
|
+
test "$(docker image inspect -f '{{index .Config.Labels "io.modelcontextprotocol.server.name"}}' ghcr.io/bbik52/docmost-mcp-remote:ci)" = io.github.bbik52/docmost-mcp-remote
|
|
143
|
+
- name: Logs
|
|
144
|
+
if: always()
|
|
145
|
+
run: docker compose logs
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Runs when a version tag such as v1.0.0 is pushed to master. Publishes the
|
|
4
|
+
# package to PyPI, the image to ghcr.io, a GitHub release and the entry in the
|
|
5
|
+
# official MCP Registry. See docs/RELEASING.md.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: release
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
env:
|
|
19
|
+
IMAGE: ghcr.io/bbik52/docmost-mcp-remote
|
|
20
|
+
MCP_PUBLISHER_VERSION: v1.8.4
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
check:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
outputs:
|
|
26
|
+
version: ${{ steps.version.outputs.version }}
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v5
|
|
29
|
+
with:
|
|
30
|
+
fetch-depth: 0
|
|
31
|
+
- uses: astral-sh/setup-uv@v7
|
|
32
|
+
with:
|
|
33
|
+
enable-cache: true
|
|
34
|
+
- name: Tag matches the project version and is on master
|
|
35
|
+
id: version
|
|
36
|
+
run: |
|
|
37
|
+
version="${GITHUB_REF_NAME#v}"
|
|
38
|
+
project="$(uv version --short)"
|
|
39
|
+
if [ "$version" != "$project" ]; then
|
|
40
|
+
echo "Tag $GITHUB_REF_NAME does not match project version $project" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
git fetch --no-tags origin master
|
|
44
|
+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/master; then
|
|
45
|
+
echo "Tag $GITHUB_REF_NAME is not on master" >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
49
|
+
- run: uv sync --locked
|
|
50
|
+
- name: Release metadata is consistent
|
|
51
|
+
run: uv run pytest tests/test_release_metadata.py
|
|
52
|
+
- name: Changelog has an entry for this version
|
|
53
|
+
run: grep -q "^## \[${{ steps.version.outputs.version }}\]" CHANGELOG.md
|
|
54
|
+
|
|
55
|
+
build:
|
|
56
|
+
needs: check
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v5
|
|
60
|
+
- uses: astral-sh/setup-uv@v7
|
|
61
|
+
- run: uv build
|
|
62
|
+
- uses: actions/upload-artifact@v7
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
if-no-files-found: error
|
|
67
|
+
|
|
68
|
+
pypi:
|
|
69
|
+
needs: build
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
environment:
|
|
72
|
+
name: pypi
|
|
73
|
+
url: https://pypi.org/project/docmost-mcp-remote/
|
|
74
|
+
permissions:
|
|
75
|
+
id-token: write
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/download-artifact@v8
|
|
78
|
+
with:
|
|
79
|
+
name: dist
|
|
80
|
+
path: dist/
|
|
81
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
82
|
+
|
|
83
|
+
image:
|
|
84
|
+
needs: check
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
permissions:
|
|
87
|
+
contents: read
|
|
88
|
+
packages: write
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v5
|
|
91
|
+
- uses: docker/setup-qemu-action@v4
|
|
92
|
+
- uses: docker/setup-buildx-action@v4
|
|
93
|
+
- uses: docker/login-action@v4
|
|
94
|
+
with:
|
|
95
|
+
registry: ghcr.io
|
|
96
|
+
username: ${{ github.actor }}
|
|
97
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
98
|
+
- id: meta
|
|
99
|
+
uses: docker/metadata-action@v6
|
|
100
|
+
with:
|
|
101
|
+
images: ${{ env.IMAGE }}
|
|
102
|
+
tags: |
|
|
103
|
+
type=semver,pattern={{version}}
|
|
104
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
105
|
+
type=semver,pattern={{major}}
|
|
106
|
+
- uses: docker/build-push-action@v7
|
|
107
|
+
with:
|
|
108
|
+
context: .
|
|
109
|
+
platforms: linux/amd64,linux/arm64
|
|
110
|
+
push: true
|
|
111
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
112
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
113
|
+
annotations: ${{ steps.meta.outputs.annotations }}
|
|
114
|
+
|
|
115
|
+
github-release:
|
|
116
|
+
needs: [check, build, pypi, image]
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
permissions:
|
|
119
|
+
contents: write
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v5
|
|
122
|
+
- uses: actions/download-artifact@v8
|
|
123
|
+
with:
|
|
124
|
+
name: dist
|
|
125
|
+
path: dist/
|
|
126
|
+
- name: Create the release
|
|
127
|
+
env:
|
|
128
|
+
GH_TOKEN: ${{ github.token }}
|
|
129
|
+
VERSION: ${{ needs.check.outputs.version }}
|
|
130
|
+
run: |
|
|
131
|
+
awk -v v="$VERSION" '
|
|
132
|
+
$0 ~ "^## \\[" v "\\]" { found = 1; next }
|
|
133
|
+
found && /^## \[/ { exit }
|
|
134
|
+
found { print }
|
|
135
|
+
' CHANGELOG.md > notes.md
|
|
136
|
+
printf '\nImage: `%s:%s`\n' "$IMAGE" "$VERSION" >> notes.md
|
|
137
|
+
gh release create "$GITHUB_REF_NAME" dist/* --title "$VERSION" --notes-file notes.md --verify-tag
|
|
138
|
+
|
|
139
|
+
mcp-registry:
|
|
140
|
+
needs: [pypi, image]
|
|
141
|
+
runs-on: ubuntu-latest
|
|
142
|
+
permissions:
|
|
143
|
+
contents: read
|
|
144
|
+
id-token: write
|
|
145
|
+
steps:
|
|
146
|
+
- uses: actions/checkout@v5
|
|
147
|
+
- uses: sigstore/cosign-installer@v4
|
|
148
|
+
- name: Download and verify mcp-publisher
|
|
149
|
+
run: |
|
|
150
|
+
set -euo pipefail
|
|
151
|
+
archive="mcp-publisher_linux_amd64.tar.gz"
|
|
152
|
+
base_url="https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}"
|
|
153
|
+
curl -fsSL -o "$archive" "$base_url/$archive"
|
|
154
|
+
curl -fsSL -o "$archive.sigstore.json" "$base_url/$archive.sigstore.json"
|
|
155
|
+
cosign verify-blob \
|
|
156
|
+
--bundle "$archive.sigstore.json" \
|
|
157
|
+
--certificate-identity "https://github.com/modelcontextprotocol/registry/.github/workflows/release.yml@refs/tags/${MCP_PUBLISHER_VERSION}" \
|
|
158
|
+
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
|
159
|
+
"$archive"
|
|
160
|
+
tar xzf "$archive" mcp-publisher
|
|
161
|
+
- run: ./mcp-publisher validate
|
|
162
|
+
- run: ./mcp-publisher login github-oidc
|
|
163
|
+
- run: ./mcp-publisher publish
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# uv
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs/Zensical documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
|
|
201
|
+
# can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer, you
|
|
203
|
+
# could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are listed here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[semantic versioning](https://semver.org/). A release that needs attention
|
|
6
|
+
when upgrading lists it under "Upgrade notes".
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.0.0] - 2026-10-25
|
|
11
|
+
|
|
12
|
+
First release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Remote MCP server (streamable HTTP at `/mcp`) for Docmost Community Edition
|
|
17
|
+
0.70.0 or newer.
|
|
18
|
+
- Per-member sign-in with the member's own Docmost account; every tool call
|
|
19
|
+
runs as that member with their Docmost rights. Optional `ALLOWED_EMAILS`
|
|
20
|
+
and `ALLOWED_SPACES` limits.
|
|
21
|
+
- OAuth 2.1 authorization server following the MCP authorization spec:
|
|
22
|
+
dynamic client registration, PKCE (S256), consent page with `read`,
|
|
23
|
+
`write` and `delete` scopes, resource-bound tokens, rotating refresh tokens
|
|
24
|
+
with reuse detection. Personal bearer tokens for scripts. Grants and tokens
|
|
25
|
+
are listed and revoked on the account page.
|
|
26
|
+
- Read tools: `whoami`, `list_spaces`, `list_pages`, `recent_pages`,
|
|
27
|
+
`get_page` (Markdown), `search`, `list_comments`, `list_trash`,
|
|
28
|
+
`list_snapshots`. Page content is returned inside an untrusted-content
|
|
29
|
+
envelope.
|
|
30
|
+
- Write tools: `create_page`, `update_page` (append, prepend, replace),
|
|
31
|
+
`rename_page`, `move_page`, `add_comment`, `restore_snapshot`; delete-scope
|
|
32
|
+
tools `trash_page` and `restore_page`. No permanent delete.
|
|
33
|
+
- Encrypted snapshot of a page body before every change, kept for
|
|
34
|
+
`SNAPSHOT_RETENTION_DAYS`; read-back after every write to catch changes
|
|
35
|
+
Docmost did not apply.
|
|
36
|
+
- Per-member rate limit (`TOOL_CALLS_PER_MINUTE`) and an audit log of tool
|
|
37
|
+
calls without content (`AUDIT_RETENTION_DAYS`).
|
|
38
|
+
- `admin` command line: list members and grants, revoke grants or members,
|
|
39
|
+
disable members, read the audit log, rotate `SECRET_KEY`.
|
|
40
|
+
- Database schema migrations applied at startup under a lock; an older
|
|
41
|
+
server refuses to start on a schema upgraded by a newer one.
|
|
42
|
+
- Container image for linux/amd64 and linux/arm64, `compose.yaml` with
|
|
43
|
+
Postgres, and a PyPI package.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest. Issues and pull requests are welcome.
|
|
4
|
+
|
|
5
|
+
## Ground rules
|
|
6
|
+
|
|
7
|
+
- **Clean implementation.** Do not copy code, structure or tool descriptions
|
|
8
|
+
from docmost-mcp-oss or from Docmost (AGPL-3.0). Write from the observed
|
|
9
|
+
behavior of the Docmost HTTP API. See
|
|
10
|
+
[ADR 0001](docs/adr/0001-new-repository-from-scratch.md).
|
|
11
|
+
- **English** for code, comments, docs, commit messages and pull requests.
|
|
12
|
+
- Security-relevant changes need tests. Never log passwords, tokens, page
|
|
13
|
+
content or tool arguments.
|
|
14
|
+
- Larger design changes start with an issue or an ADR in `docs/adr/`.
|
|
15
|
+
- Database changes come with an Alembic migration in the same pull request
|
|
16
|
+
and follow "Database migrations" in [docs/DESIGN.md](docs/DESIGN.md):
|
|
17
|
+
never edit a released migration, keep existing data.
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
Requires [uv](https://docs.astral.sh/uv/) and Python 3.12 or newer.
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
uv sync
|
|
25
|
+
docker run -d -p 5433:5432 -e POSTGRES_USER=test -e POSTGRES_PASSWORD=test postgres:17-alpine
|
|
26
|
+
DATABASE_TEST_URL=postgresql://test:test@localhost:5433/test uv run pytest
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Before pushing:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
uv run ruff format .
|
|
33
|
+
uv run ruff check .
|
|
34
|
+
uv run mypy
|
|
35
|
+
uv run pytest
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Pull requests
|
|
39
|
+
|
|
40
|
+
- The repository uses git flow. Branch from `develop` as `feature/<topic>`
|
|
41
|
+
(or `bugfix/<topic>`) and open the pull request against `develop`.
|
|
42
|
+
`master` only receives `release/*` and `hotfix/*` merges and holds tagged
|
|
43
|
+
releases.
|
|
44
|
+
- One topic per pull request.
|
|
45
|
+
- Describe what changed and why; link the roadmap phase or issue.
|
|
46
|
+
- CI must be green.
|
|
47
|
+
- Changes to authentication, OAuth, tokens, cryptography, the sign-in and
|
|
48
|
+
consent pages or the write path are reviewed with extra care; see
|
|
49
|
+
"Contributions and project security" in [docs/DESIGN.md](docs/DESIGN.md).
|
|
50
|
+
|
|
51
|
+
## License of contributions
|
|
52
|
+
|
|
53
|
+
The project is licensed under the [Apache License 2.0](LICENSE). By opening a
|
|
54
|
+
pull request you agree that your contribution is licensed under the same
|
|
55
|
+
terms (section 5 of the license). There is no separate contributor agreement.
|