A standalone mock of a Confluence-shaped knowledge API: spaces, pages, versions, labels, and
comments. See PRD.md for the full product requirements.
Bring up the whole stack with one command from the repo root:
docker compose build # builds the knowledge-api and mcp-server images
docker compose up # starts knowledge-api first, waits for it to be healthy,
# then starts mcp-serverknowledge-apiis reachable at http://localhost:8040 (health check:GET /; it also servespage-browser-ui's static assets at/static/, if that module has been built into the image).mcp-serveris reachable at http://localhost:8041/mcp (MCP streamable-http endpoint).- Neither port is exposed beyond
localhostby default.
Confirm the stack is healthy:
docker compose ps # both services should show "healthy"
docker compose logs # combined output of both containersStop the stack. The SQLite database persists across a plain down (it lives in a named Docker
volume) and is only wiped by down -v:
docker compose down # stack stops; data survives
docker compose down -v # stack stops; the knowledge-api-data volume is deleted| Variable | Service | Default | Purpose |
|---|---|---|---|
PORT |
both | 8040 (knowledge-api) / 8041 (mcp-server) |
Listen port each service is reachable on |
KNOWLEDGE_DB_PATH |
knowledge-api | /data/knowledge.db |
SQLite file path, bound to the named volume (app/db.py reads this — not DATABASE_PATH, an earlier planning-time assumption corrected once the real code existed) |
API_BASE_URL |
mcp-server | http://knowledge-api:8040 |
Base URL mcp-server calls for every tool |
KNOWLEDGE_API_HOST_PORT / KNOWLEDGE_API_INTERNAL_PORT |
knowledge-api | 8040 / 8040 |
Host-side vs. container-side port remap |
MCP_SERVER_HOST_PORT / MCP_SERVER_INTERNAL_PORT |
mcp-server | 8041 / 8041 |
Host-side vs. container-side port remap |
Note: if you remap KNOWLEDGE_API_INTERNAL_PORT, also set API_BASE_URL explicitly to match
— the default does not automatically follow that remap.
Known internal detail: mcp_server/server.py always binds the MCP SDK's default host
(127.0.0.1, loopback-only) — it never passes a host kwarg, so no environment variable can
override it. docker/mcp-server.Dockerfile and docker/mcp-forward.py work around this at the
packaging layer (the real process listens on an internal-only port, and a small forwarder is
what actually listens on the published $PORT), so http://localhost:8041/mcp still works from
the host. This is transparent to normal use; it only matters if you're debugging the container's
process list directly.
The page-browser-ui end-to-end suite drives a real Chromium browser (via Playwright) against a
real knowledge-api server process. One-time local setup:
pip install -r requirements-e2e.txt
playwright install chromium
Then run it:
.venv/bin/python3 -m pytest -q tests_e2e/