An MCP server that lets AI agents read and manage a Docmost workspace — search, read, create, and organise pages, spaces and comments — over authenticated MCP Streamable HTTP.
Docmost's own MCP endpoint is an Enterprise feature. This is an independent server built for self-hosted and Community deployments that want agent access without an enterprise licence. It is not affiliated with Docmost.
Security is the point of this project: writes are off by default, Docmost passwords never live in a client manifest, and the HTTP surface can bind every MCP access token to the Docmost account that authorized it.
MCP is one protocol, so this is one server — not a different build per client. Only the config file differs.
| Client | Config |
|---|---|
| Claude Code | claude mcp add --transport http docmost http://127.0.0.1:8787/mcp --header "Authorization: Bearer $DOCMOST_MCP_BEARER_TOKEN" |
| Claude Desktop | add an mcpServers entry pointing at http://127.0.0.1:8787/mcp |
| Codex | uses the bundled .codex-plugin/plugin.json → .mcp.json |
| Cursor | .cursor/mcp.json, same URL and header |
Remote deployments can expose OAuth discovery so compatible clients open a normal
browser login. The user signs in with their own Docmost account; the password is
validated once and discarded, while tool calls inherit that account's space roles.
The static DOCMOST_MCP_BEARER_TOKEN remains available for local and break-glass
administrative compatibility.
Rust 1.88 or newer.
git clone https://github.com/Rahulk644/docmost-mcp.git
cd docmost-mcp
export DOCMOST_BASE_URL="https://docs.example.com"
export DOCMOST_API_TOKEN="your-docmost-api-token"
export DOCMOST_MCP_BEARER_TOKEN="$(openssl rand -hex 32)"
cargo run --locked --releaseDocmost Community Edition may not issue API tokens. Use a dedicated, least-privilege Docmost account instead:
export DOCMOST_EMAIL="[email protected]"
export DOCMOST_PASSWORD="use-a-secret-manager"Do not set both modes — the API token wins when present.
For a multi-user remote server, do not configure a shared Docmost credential. Enable account authorization instead:
export DOCMOST_BASE_URL="https://docs.example.com"
export DOCMOST_MCP_ACCOUNT_AUTH=true
export DOCMOST_MCP_PUBLIC_URL="https://mcp.example.com"
export DOCMOST_MCP_BEARER_TOKEN="$(openssl rand -hex 32)" # break-glass onlyAn OAuth-capable MCP client pointed at https://mcp.example.com/mcp discovers the
login page automatically. Authorization uses code flow with PKCE and dynamic
client registration. Access and refresh grants are held in memory, so a server
restart intentionally requires users to sign in again.
Verify:
curl http://127.0.0.1:8787/health
curl -i http://127.0.0.1:8787/mcp # expect 401 without the Bearer tokenFor browser session authentication, set DOCMOST_MCP_BROWSER_URL to the
public Docmost origin, such as https://docs.example.com. Keep
DOCMOST_MCP_PUBLIC_URL set to your MCP origin. Forward /oauth/authorize
and /oauth/resume on the Docmost origin to this MCP service, preserving
Host and cookies. Add the Docmost host and origin to the MCP allowlists.
Do not forward Docmost's /login or /api routes to MCP.
OAuth discovery advertises authorization on the Docmost origin. Users open
the ordinary Docmost login screen in a tab, then return to the authorization
tab, which detects their session and displays the account and registered
client before asking for consent. Existing signed-in users proceed directly
to consent. Docmost handles passwords, MFA and SSO; MCP validates the
HttpOnly session through /api/users/me and never asks for a password in this
mode. Switching accounts requires fresh consent. PKCE and per-call write
confirmation remain required.
Example operator configuration (use your own domains):
DOCMOST_MCP_ACCOUNT_AUTH=true
DOCMOST_BASE_URL=http://docmost:3000
DOCMOST_MCP_PUBLIC_URL=https://mcp.example.com
DOCMOST_MCP_BROWSER_URL=https://docs.example.com
DOCMOST_MCP_BRAND=Docmost
DOCMOST_MCP_ALLOWED_HOSTS=mcp.example.com,docs.example.com
DOCMOST_MCP_ALLOWED_ORIGINS=https://mcp.example.com,https://docs.example.comWithout DOCMOST_MCP_BROWSER_URL, the existing credential relay remains
available for deployments that cannot route OAuth on the Docmost origin.
Use the generic OSS image for personal deployments; a company-branded image
may have compiled-in company text or assets even when its environment is
changed. Reconnect clients after restarting MCP because OAuth grants are
held in memory.
All tools are prefixed docmost_ so they never collide with other MCP servers
loaded alongside this one.
Read — docmost_list_spaces, docmost_get_space, docmost_search_docs,
docmost_search_pages, docmost_get_page, docmost_list_pages,
docmost_list_child_pages, docmost_get_comments,
docmost_list_workspace_members, docmost_get_current_user
Write — docmost_create_page, docmost_update_page, docmost_duplicate_page,
docmost_copy_page_to_space, docmost_move_page, docmost_move_page_to_space,
docmost_create_space, docmost_update_space, docmost_create_comment,
docmost_update_comment
Write tools are gated twice — see docs/write-tools.md for the confirmation contract.
List tools accept limit and cursor, and their output states how many results
were shown, the total when the server reports one, and the exact cursor to pass
next. Where the server gives no signal either way, the output says the next page
is unknown rather than guessing — so an agent never concludes it has everything
when it hasn't, and never pages forever off a bad inference.
- Binds to
127.0.0.1:8787unless changed. /mcprequires either an account-bound OAuth Bearer token or the static break-glass token. Static-token comparison is constant-time, and obvious placeholder values are rejected outright.- Account authorization uses OAuth authorization code + PKCE, exact registered redirects, CSRF protection, per-account failed-login throttling, one-time codes, and short-lived opaque tokens. Docmost passwords are never persisted.
- Host and Origin allowlists are enforced (DNS-rebinding protection).
/healthis public and returns only service status.- Mutating tools are disabled unless
DOCMOST_MCP_ENABLE_WRITES=true, and every mutation additionally requiresconfirm: truein that individual call. - Mutation metadata is appended to a mode-
0600JSONL audit log. Page and comment bodies are never logged. - Credentials come from environment variables or the OS keychain. The encrypted-file fallback is opt-in.
See SECURITY.md for deployment boundaries and how to report issues.
| Variable | Default | Purpose |
|---|---|---|
DOCMOST_BASE_URL |
required | Docmost origin, e.g. https://docs.example.com |
DOCMOST_API_TOKEN |
— | Preferred Docmost API token |
DOCMOST_EMAIL, DOCMOST_PASSWORD |
— | Community-edition fallback; both must be set |
DOCMOST_MCP_BEARER_TOKEN |
required unless account auth is enabled | Static local/break-glass token for /mcp |
DOCMOST_MCP_ACCOUNT_AUTH |
false |
Enables per-account browser login and OAuth discovery |
DOCMOST_MCP_PUBLIC_URL |
— | Public HTTPS origin; required when account auth is enabled |
DOCMOST_MCP_BRAND |
Docmost |
Product name on the sign-in pages, for rebranded deployments |
DOCMOST_MCP_BIND |
127.0.0.1:8787 |
HTTP listen address |
DOCMOST_MCP_ALLOWED_HOSTS |
loopback | Comma-separated Host allowlist |
DOCMOST_MCP_ALLOWED_ORIGINS |
loopback | Comma-separated browser Origin allowlist |
DOCMOST_MCP_ENABLE_WRITES |
false |
Enables mutations; calls still require confirm: true |
DOCMOST_MCP_AUDIT_LOG |
~/.docmost-local-mcp/mutations.jsonl |
Mutation audit file |
DOCMOST_ALLOW_FILE_CREDENTIALS |
false |
Opts into the encrypted-file credential fallback |
The on-disk state directory is still
~/.docmost-local-mcp/, retained from before this project was renamed so existing credentials and audit logs are not orphaned.
A stdio transport is available for local development via --transport stdio; its
first-run interactive login opens the system browser. HTTP is the default.
cp .env.example .env
# Generate a token — never leave DOCMOST_MCP_BEARER_TOKEN empty:
# openssl rand -hex 32
docker compose -f docker-compose.example.yml up --buildThe example Compose file publishes to the host loopback interface only. For a
remote deployment: terminate TLS in a reverse proxy, bind deliberately, and set
exact DOCMOST_MCP_ALLOWED_HOSTS / DOCMOST_MCP_ALLOWED_ORIGINS. Never expose
plain HTTP or wildcard allowlists on an untrusted network.
cargo fmt --all -- --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --lockedCI runs all three on every push and pull request, plus a dependency-advisory audit. Contributions welcome — see CONTRIBUTING.md and CODE_OF_CONDUCT.md.
The application core began as a fork of
wisflux/docmost-local-mcp (MIT).
This project has since diverged substantially — authenticated Streamable HTTP on
rmcp 3.1.0, host/origin validation, headless environment-based authentication,
default-deny writes with per-call confirmation, mutation auditing, keychain-first
credential storage, prefixed tools, pagination metadata, Docker packaging, and CI.
The upstream npm launcher and its install-time binary download are deliberately not
carried over; this builds from source instead.
UPSTREAM.md records the exact upstream commit. See CHANGELOG.md for what changed since.
MIT.