Skip to main content

docmost-mcp-remote

A self-hosted remote MCP server for Docmost Community Edition. Run it next to your Docmost, put it behind your domain, and let AI clients such as Claude, Claude Code, Cursor or VS Code read and edit the wiki on behalf of each member, with that member's own Docmost account and rights.

  • One server, one Docmost, many members. Each member signs in with their own Docmost account in the browser. Everything their AI client does is done as them in Docmost.
  • Authorization is mandatory. OAuth 2.1 for remote connectors and personal bearer tokens for scripts. There is no unauthenticated mode.
  • Safe by default. read is the only scope granted without asking. Writing and moving to Trash need explicit scopes. Nothing is deleted permanently, and the server keeps an encrypted snapshot of a page body before every change.
  • Self-hosted. One container plus Postgres, with a ready compose.yaml.

Supported Docmost versions

Docmost Community Edition 0.70.0 or newer. Older versions are refused at sign-in. CI runs the integration tests against Docmost 0.70.3, 0.96.0 and 0.97.0; newer releases work in most cases and log a warning until they are added to the tested range. Docmost users who sign in with SSO only (an Enterprise feature) cannot use this server: it signs in with email and password.

Tools

Tool Scope What it does
whoami read The Docmost account, scopes and Docmost version of this connection
list_spaces read Spaces you can access, with your role
list_pages read Page tree of a space, one level at a time
recent_pages read Recently updated pages
get_page read One page as Markdown, by id, slug id or Docmost URL
search read Full-text search with snippets
list_comments read Comments and replies on a page
list_trash read Pages in the Trash of a space
list_snapshots read Snapshots this server saved before your changes to a page
create_page write New page with a Markdown body, optionally under a parent
update_page write Append, prepend or replace a page body with Markdown
rename_page write Change a page title
move_page write Move a page within its space, to the start, the end or after a sibling
add_comment write Comment on a page or reply to a comment
restore_snapshot write Put a page body back to a snapshot
trash_page delete Move a page to the Trash
restore_page delete Restore a page from the Trash

Page bodies, comments and snippets are returned inside a <docmost-untrusted-content> envelope so the model treats them as data.

Before every body change the server saves the current body as a snapshot in its own database, encrypted, for SNAPSHOT_RETENTION_DAYS; if the snapshot cannot be saved, nothing is written. After the change it reads the page back and reports an error if Docmost did not apply it (this happens when Docmost runs with COLLAB_DISABLE_REDIS). Snapshots can hold content of pages that were later deleted in Docmost until they expire.

Security model

Members sign in with their own Docmost account; everything a client does is done as that member, with their Docmost rights. The server keeps the Docmost session token encrypted with a key derived from SECRET_KEY and does not store passwords unless RELOGIN_MODE=stored_password. Personal tokens are stored as hashes and shown once. The sign-in page has CSRF protection and per-IP and per-email rate limits.

The server is its own OAuth 2.1 authorization server, following the MCP authorization spec: dynamic client registration (limited per IP, redirect URIs must be https, loopback http or a native app scheme), authorization code with PKCE (S256 only), exact redirect URI match, tokens bound to this server's /mcp resource, one-hour access tokens and 30-day refresh tokens that rotate on every use. A reused refresh token or a replayed code revokes the whole grant. OAuth grants and personal tokens are listed and revoked on the same account page.

Page content is treated as untrusted data and labelled as such for the model. Prompt injection in a page can still make a client act within the scopes the member granted, so grant write and delete only to clients you trust. Tool calls are limited per member and recorded in an audit log. See the threat model, SECURITY.md for how to report a vulnerability and the design.

Quick start

You need Docker with Compose, a Docmost reachable from the server, and a host name with https for the server itself, for example mcp.example.com.

  1. Download the compose file and the settings template:

    mkdir docmost-mcp && cd docmost-mcp
    curl -fsSLO https://raw.githubusercontent.com/bbik52/docmost-mcp-remote/master/compose.yaml
    curl -fsSL -o .env https://raw.githubusercontent.com/bbik52/docmost-mcp-remote/master/.env.example
    
  2. Edit .env: set PUBLIC_URL (the https URL of this server), DOCMOST_URL, a long random POSTGRES_PASSWORD and SECRET_KEY (openssl rand -hex 32 gives a good value for both). Consider ALLOWED_EMAILS to limit who can sign in.

  3. Start it and check that it is up:

    docker compose up -d
    curl http://127.0.0.1:8000/healthz
    
  4. Put a reverse proxy with https in front of 127.0.0.1:8000 (see below) and open PUBLIC_URL/login in a browser to check that you can sign in with your Docmost account.

  5. Connect a client (see Connecting a client).

The server applies its database migrations at startup. Back up the Postgres volume like you back up Docmost: it holds the encrypted sessions, the snapshots and the audit log. Losing SECRET_KEY signs every member out and makes the snapshots unreadable.

The compose.yaml
# docmost-mcp-remote: the server and its Postgres.
# Copy .env.example to .env and fill in the required values first, then put a
# reverse proxy with https in front of port 8000.

services:
  server:
    # "1" follows 1.x releases; set IMAGE_TAG to a full version such as 1.0.0 to pin one.
    image: ghcr.io/bbik52/docmost-mcp-remote:${IMAGE_TAG:-1}
    restart: unless-stopped
    environment:
      PUBLIC_URL: ${PUBLIC_URL:?set PUBLIC_URL to the public https URL of this server}
      DOCMOST_URL: ${DOCMOST_URL:?set DOCMOST_URL to the base URL of your Docmost}
      DOCMOST_ALLOW_INSECURE_HTTP: ${DOCMOST_ALLOW_INSECURE_HTTP:-false}
      DATABASE_URL: postgresql://docmost_mcp:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}@postgres:5432/docmost_mcp
      SECRET_KEY: ${SECRET_KEY:?set SECRET_KEY to a random value of at least 32 characters}
      SECRET_KEY_PREVIOUS: ${SECRET_KEY_PREVIOUS:-}
      ALLOWED_EMAILS: ${ALLOWED_EMAILS:-}
      ALLOWED_SPACES: ${ALLOWED_SPACES:-}
      RELOGIN_MODE: ${RELOGIN_MODE:-ask}
      SNAPSHOT_RETENTION_DAYS: ${SNAPSHOT_RETENTION_DAYS:-30}
      AUDIT_RETENTION_DAYS: ${AUDIT_RETENTION_DAYS:-90}
      TOOL_CALLS_PER_MINUTE: ${TOOL_CALLS_PER_MINUTE:-60}
      MAX_PAGE_CHARS: ${MAX_PAGE_CHARS:-60000}
      # The port is published on 127.0.0.1 only, so every client is a local proxy.
      FORWARDED_ALLOW_IPS: ${FORWARDED_ALLOW_IPS:-*}
      LOG_LEVEL: ${LOG_LEVEL:-info}
    ports:
      - "127.0.0.1:8000:8000"
    depends_on:
      postgres:
        condition: service_healthy
    read_only: true
    tmpfs:
      - /tmp
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

  postgres:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: docmost_mcp
      POSTGRES_DB: docmost_mcp
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U docmost_mcp -d docmost_mcp"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres-data:

The server is also on PyPI (pip install docmost-mcp-remote or uvx docmost-mcp-remote) for setups without containers; it needs Postgres and the same settings as environment variables.

Reverse proxy

The server listens on plain http and expects a reverse proxy that terminates https. Serve it at the root of its own host name; a sub-path is not supported. The proxy must not buffer responses, because MCP streams them.

Caddy (gets certificates by itself):

mcp.example.com {
    reverse_proxy 127.0.0.1:8000
}

nginx:

server {
    listen 443 ssl;
    server_name mcp.example.com;
    ssl_certificate     /etc/letsencrypt/live/mcp.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_read_timeout 1h;
    }
}

The server uses X-Forwarded-For for its per-IP rate limits. The compose file publishes the port on 127.0.0.1 only and trusts the header from every client; if you publish the port elsewhere, set FORWARDED_ALLOW_IPS to the proxy's address.

Connecting a client

The MCP endpoint is PUBLIC_URL/mcp, for example https://mcp.example.com/mcp. Clients that support MCP authorization open a browser window: sign in with your Docmost email and password and choose what the client may do. read is always granted; tick write or delete only for clients you trust.

Claude.ai and Claude Desktop: Settings, Connectors, Add custom connector, then enter the endpoint URL. The server must be reachable from the internet over https for Claude.ai.

Claude Code:

claude mcp add --transport http docmost https://mcp.example.com/mcp

Then run /mcp in Claude Code to sign in.

Cursor (~/.cursor/mcp.json):

{
  "mcpServers": {
    "docmost": { "url": "https://mcp.example.com/mcp" }
  }
}

VS Code (.vscode/mcp.json):

{
  "servers": {
    "docmost": { "type": "http", "url": "https://mcp.example.com/mcp" }
  }
}

With a personal token (scripts and clients without OAuth):

  1. Open PUBLIC_URL/login and sign in with your Docmost email and password.

  2. On the account page, create a personal token with the scopes you need.

  3. Configure the client with the endpoint and the header Authorization: Bearer <token>:

    claude mcp add --transport http docmost https://mcp.example.com/mcp \
      --header "Authorization: Bearer dmr_..."
    

Connected apps and tokens are listed and revoked on the account page, PUBLIC_URL/account.

Administration

The admin command runs where the server runs, with the same environment:

docker compose exec server docmost-mcp-remote admin members
docker compose exec server docmost-mcp-remote admin migrate
docker compose exec server docmost-mcp-remote admin grants anna@example.com
docker compose exec server docmost-mcp-remote admin revoke-grant <grant id>
docker compose exec server docmost-mcp-remote admin revoke-member anna@example.com
docker compose exec server docmost-mcp-remote admin disable-member anna@example.com
docker compose exec server docmost-mcp-remote admin audit --email anna@example.com

revoke-member revokes every grant of the member and drops their stored Docmost session; disable-member blocks them until enable-member. Every tool call is recorded in the audit log with the tool, the page or space id and the outcome, never with content or arguments.

To rotate SECRET_KEY: set the old value as SECRET_KEY_PREVIOUS and a new SECRET_KEY, restart, run admin rotate-key, then remove SECRET_KEY_PREVIOUS and restart again.

Upgrading

  1. Read the changelog for "Upgrade notes".

  2. Back up the database:

    docker compose exec -T postgres pg_dump -U docmost_mcp -Fc docmost_mcp > docmost-mcp-$(date +%F).dump
    
  3. Pull the new image and restart:

    docker compose pull && docker compose up -d
    

The server upgrades its database schema at startup, in one transaction and one process at a time, so a failed upgrade leaves the old schema in place. docker compose exec server docmost-mcp-remote admin migrate shows the schema revision.

The image tag 1 follows 1.x releases; set IMAGE_TAG in .env to pin a version. Going back to an older version after its schema was upgraded is not supported: an older server refuses to start on a newer schema. To roll back, restore the backup with the old image:

docker compose stop server
docker compose exec -T postgres pg_restore -U docmost_mcp -d docmost_mcp --clean --if-exists < docmost-mcp-2026-01-31.dump
IMAGE_TAG=1.0.0 docker compose up -d

Configuration

Variable Required Default Meaning
PUBLIC_URL yes Public https URL of this server
DOCMOST_URL yes Base URL of your Docmost
DOCMOST_ALLOW_INSECURE_HTTP no false Allow an http:// DOCMOST_URL on a trusted private network
DATABASE_URL yes Postgres URL (postgresql://user:password@host/db)
SECRET_KEY yes At least 32 characters; encrypts stored Docmost sessions and signs cookies
SECRET_KEY_PREVIOUS no The old key during a rotation
ALLOWED_EMAILS no empty Comma-separated emails allowed to sign in; empty allows every Docmost user
ALLOWED_SPACES no empty Comma-separated space slugs or ids members may use through this server; empty allows all
MAX_PAGE_CHARS no 60000 Longest page body returned; longer bodies are truncated
SNAPSHOT_RETENTION_DAYS no 30 Days a snapshot of a page body is kept
AUDIT_RETENTION_DAYS no 90 Days an audit log entry is kept
TOOL_CALLS_PER_MINUTE no 60 Tool calls one member may make per minute, over all their clients
RELOGIN_MODE no ask ask: members sign in again when their Docmost session expires; stored_password: the password is kept encrypted and used to sign in again
DOCMOST_TIMEOUT_SECONDS no 15 Timeout for one request to Docmost
DOCMOST_MAX_RESPONSE_BYTES no 5242880 Largest Docmost response the server reads
FORWARDED_ALLOW_IPS no 127.0.0.1 Proxies trusted for X-Forwarded-For (* in the compose file)
HOST / PORT no 0.0.0.0 / 8000 Listen address
LOG_LEVEL no info debug, info, warning or error
IMAGE_TAG no 1 Image tag used by compose.yaml

Development

uv sync
uv run ruff check . && uv run ruff format --check .
uv run mypy
DATABASE_TEST_URL=postgresql://test:test@localhost:5433/test uv run pytest

Tests that need Postgres are skipped without DATABASE_TEST_URL; for example docker run -d -p 5433:5432 -e POSTGRES_USER=test -e POSTGRES_PASSWORD=test postgres:17-alpine.

Integration tests run against a real Docmost:

DOCMOST_VERSION=0.97.0 docker compose -f tests/integration/compose.yaml up -d
DOCMOST_TEST_URL=http://localhost:3000 uv run pytest tests/integration

To run the compose stack with a local build:

docker build -t ghcr.io/bbik52/docmost-mcp-remote:dev .
IMAGE_TAG=dev docker compose up -d

See CONTRIBUTING.md and the roadmap. Русская версия: README.ru.md.

License

Apache License 2.0. The license does not cover the project name: forks are welcome under another name. This project is an independent implementation and is not affiliated with Docmost. It was inspired by docmost-mcp-oss.

Metadata

Release files for docmost-mcp-remote 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for docmost-mcp-remote 1.0.1
File Size Uploaded Provenance
docmost_mcp_remote-1.0.1.tar.gz 285.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for docmost-mcp-remote 1.0.1
File Interpreter ABI Platform
docmost_mcp_remote-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 364.3 kB

Release files / docmost_mcp_remote-1.0.1.tar.gz

Download URL docmost_mcp_remote-1.0.1.tar.gz
Size 285.3 kB
Tags Source
SHA-256 checksum
How to use checksums
58c81a9a34bf3ab7054e974377676f48fb5b8c4c04b2487bc4e4b6212fd7ebe0
BLAKE2b-256 checksum
How to use checksums
c0412930fa78c1d5b08f884a78d4e1199eb2976ecb7d4be1e8f15cd359085f2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / docmost_mcp_remote-1.0.1-py3-none-any.whl

Download URL docmost_mcp_remote-1.0.1-py3-none-any.whl
Size 79.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df1445cb1c3ce9bc14b9ab1c8ec02a494566735b321b59cbb10d79539942253b
BLAKE2b-256 checksum
How to use checksums
d346c0572403146b0eda69d887cb5c600b5a7555dd6cfe35fb989dbb8b0550db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for docmost-mcp-remote 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for docmost_mcp_remote-1.0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page