Self-hosting the MCP server
If you run DocSpace yourself, you can start the MCP server from a checkout of the repository and connect a client to it over stdio, without agent keys. This is the mode for local development and single-user installations. To connect to DocSpace in the cloud, use the quickstart instead.
The stdio server and the HTTP endpoint share one tool registry and the same data layer as the REST API, so anything an agent changes is visible in the web app immediately, and vice versa. The tool reference applies to both.
How stdio mode differs
Hosted / HTTP (/mcp) |
Self-hosted stdio (npm run mcp) |
|
|---|---|---|
| Credential | Agent key in Authorization |
The server's own SUPABASE_SERVICE_ROLE_KEY |
| Space | The key's space | The personal space of DOCSPACE_MCP_OWNER |
| Scope ∧ role check | Every call | None: the process runs as the owner |
| Audit rows | Every call | None |
| Rate limits | Yes | The owner is exempt |
Because the stdio process holds the service-role key (full database access), run it only on a machine you trust, inside the same perimeter as your database, and never ship that key to an end user's laptop.
Requirements
- Node.js 24 and a checkout of the repository (
npm install). - A Supabase project with the DocSpace schema applied (
npm run migrate, see the main README).
Environment variables
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
Yes | URL of your Supabase project. |
SUPABASE_ANON_KEY |
Yes | Anon key of the project. |
SUPABASE_SERVICE_ROLE_KEY |
Yes | Service-role key. The MCP server refuses to start without it. Server-side secret: never commit it. |
DOCSPACE_MCP_OWNER |
Recommended | User id the server acts as. Its personal space is the default space. Defaults to DOCSPACE_DEV_OWNER. |
BRAIN_KEK |
For encrypted spaces | Master key for spaces with the encrypted or strict protection level. Server-side secret. |
CLERK_SECRET_KEY |
Only for _token |
Needed to verify the optional per-call _token (see Multi-user tokens below). |
Put them in .env (copied from .env.example, git-ignored):
npm install
cp .env.example .env # fill in at least SUPABASE_URL, SUPABASE_ANON_KEY and SUPABASE_SERVICE_ROLE_KEY
npm run mcp # starts the MCP server on stdio
npm run mcp loads .env itself. The clients below start the same process; pass the variables
through the client configuration only if the client does not run from the repository directory.
Connect a client over stdio
The snippets use obvious placeholders (/path/to/docspace-web, REPLACE_ME_…). Replace them with
your own values, and keep real keys out of any file you commit (for example add the project
.mcp.json to .gitignore, or rely on .env).
Claude Code
claude mcp add docspace -- node /path/to/docspace-web/mcp/server.js
Or a project .mcp.json at the repository root, which reads .env because the server starts there:
{
"mcpServers": {
"docspace": {
"command": "node",
"args": ["mcp/server.js"]
}
}
}
Claude Desktop
claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/,
Windows: %APPDATA%\Claude\). Claude Desktop does not start in the repository, so pass the
variables explicitly:
{
"mcpServers": {
"docspace": {
"command": "node",
"args": ["/path/to/docspace-web/mcp/server.js"],
"env": {
"SUPABASE_URL": "https://your-project-ref.supabase.co",
"SUPABASE_ANON_KEY": "REPLACE_ME_anon_key",
"SUPABASE_SERVICE_ROLE_KEY": "REPLACE_ME_service_role_key",
"DOCSPACE_MCP_OWNER": "00000000-0000-0000-0000-000000000000"
}
}
}
}
Cursor
~/.cursor/mcp.json (global) or .cursor/mcp.json in a project, with the same command, args
and env as Claude Desktop.
Local HTTP endpoint (development)
npm run dev:server also mounts the Streamable HTTP endpoint at http://localhost:4000/mcp, so you
can try the hosted flow (agent keys, scopes, audit) on your machine:
npm run dev:server # API on :4000 with POST /mcp
claude mcp add --transport http docspace-local 'http://localhost:4000/mcp' \
--header 'Authorization: Bearer dsk_live_REPLACE_ME'
Create the key in your local app under Settings → Knowledge → Agents and keys. Safety gates on this endpoint:
- It exists only when
NODE_ENVisdevelopmentortestand the process is not on a Fly machine.npm start, the Docker image and docker-compose do not mount it:/mcpis404there until remote MCP access ships. - Every request must come from a loopback address with a
localhost,127.0.0.1or[::1]Host(andOrigin, if sent). Anything else gets403 mcp.forbidden_host, which blocks LAN peers and DNS-rebinding pages. - Every request needs an agent key (
401 mcp.unauthorizedotherwise). WithoutSUPABASE_SERVICE_ROLE_KEYon the server, requests get503 mcp.service_role_missing.
Multi-user tokens (legacy)
Over stdio, the catalog, sync and planner tools accept an optional _token argument: a short-lived
user session token. With it, the call runs as that user and checks their membership of the target
space (and the write rate limit); without it, the call runs as DOCSPACE_MCP_OWNER. Prefer agent
keys over HTTP: _token travels in the tool arguments, so MCP client logs may record it. With an
agent key, _token is ignored and can never widen the session.
Protocol versions
The server uses the MCP TypeScript SDK 1.x and speaks protocol version 2025-11-25 (older versions
back to 2024-10-07 are accepted). A client that asks for a newer version in initialize is
answered with 2025-11-25, as the version negotiation rules allow. initialize also returns
short server instructions (spaces, citations, scopes) for the model.
Related
- Errors and Scopes and roles.