--- category: reference tags: [meta, design, prd] last_updated: 2026-03-12 confidence: high --- # Original PRD Overview > This page is part of the original single-tenant PRD, split across five wiki pages: > [[Design/Research_Wiki]] | [[Design/Rest Api]] | [[Design/Semantic_Search]] | [[Design/Mcp Server]] | [[Design/Note_Schema]] --- ## Overview Build a research knowledge base system that gives both a human (via web browser) and an AI assistant (via MCP over Claude.ai) full read/write access to a shared wiki. The wiki stores short, atomic, cross-linked markdown notes that serve as a living analytical framework for ongoing research. The system has three components built on top of an existing Otterwiki fork: 1. **REST API plugin** for Otterwiki — programmatic CRUD on wiki pages 2. **Chroma semantic search plugin** for Otterwiki — vector index maintained in sync with page saves/deletes 3. **MCP server** — exposes wiki operations as MCP tools over SSE, connecting to Claude.ai All three deploy together via docker-compose alongside Otterwiki itself. --- ## Context ### What is Otterwiki? [Otterwiki](https://github.com/redimp/otterwiki) is a minimalistic, self-hosted wiki powered by Python, Flask, Markdown, and Git. Pages are stored as markdown files in a bare Git repository. It has a plugin system that loads from `/plugins` and `/app-data/plugins`. ### Why this system? We currently maintain analytical research documents as PDFs in a Claude.ai project. This is clunky: PDFs are write-once, can't be cross-referenced programmatically, and are expensive to ingest. Decomposing the analysis into atomic wiki notes lets the AI pull only what's relevant per session, and both human and AI can maintain the knowledge base over time. ### Key users - **Human researcher** — browses, edits, and organizes notes via Otterwiki's web UI - **Claude AI assistant** — reads, writes, searches, and cross-references notes via MCP tools in Claude.ai --- ## Architecture ``` ┌─────────────┐ ┌──────────────────────────────────────────────┐ │ Claude.ai │────▶│ MCP Server (SSE) │ │ (iOS/Web) │◀────│ Python / FastMCP │ └─────────────┘ │ Translates MCP tool calls → HTTP API calls │ └──────────────┬───────────────────────────────┘ │ HTTP ┌──────────────▼───────────────────────────────┐ │ Otterwiki (Flask) │ │ ┌─────────────────┐ ┌────────────────────┐ │ │ │ REST API Plugin │ │ Chroma Plugin │ │ │ │ /api/v1/* │ │ Vector index sync │ │ │ │ JSON CRUD │ │ Semantic search │ │ │ └────────┬────────┘ └────────┬───────────┘ │ │ │ │ │ │ ┌────────▼─────────────────────▼───────────┐ │ │ │ Otterwiki Core (Page, PageIndex, etc.) │ │ │ └────────┬─────────────────────────────────┘ │ │ │ │ │ ┌────────▼──────┐ ┌───────────────────┐ │ │ │ Git Repo │ │ Chroma DB │ │ │ │ (markdown) │ │ (vector index) │ │ │ └───────────────┘ └───────────────────┘ │ └──────────────────────────────────────────────┘ ▲ ┌──────────────┘ │ HTTP (browser) ┌─────┴─────┐ │ Human │ │ Browser │ └───────────┘ ``` ### Deployment Single docker-compose stack: - `otterwiki` — Otterwiki with plugins mounted - `chromadb` — Chroma vector database (persistent volume) - `mcp-server` — MCP SSE server All on the same Docker network. The MCP server and Otterwiki communicate over internal HTTP. Only the MCP server's SSE endpoint and Otterwiki's web UI are exposed externally (behind a reverse proxy with TLS). --- ## Docker Compose ```yaml services: otterwiki: image: redimp/otterwiki:2 # OR build from fork if modifying core: # build: ./otterwiki restart: unless-stopped ports: - "8080:80" volumes: - wiki-data:/app-data - ./plugins:/app-data/plugins environment: OTTERWIKI_API_KEY: "${OTTERWIKI_API_KEY}" CHROMADB_HOST: "chromadb" CHROMADB_PORT: "8000" chromadb: image: chromadb/chroma:latest restart: unless-stopped volumes: - chroma-data:/chroma/chroma ports: - "8000:8000" mcp-server: build: ./mcp-server restart: unless-stopped ports: - "8090:8090" environment: OTTERWIKI_API_URL: "http://otterwiki:80" OTTERWIKI_API_KEY: "${OTTERWIKI_API_KEY}" MCP_PORT: "8090" volumes: wiki-data: chroma-data: ``` --- ## Implementation Sequence ### Phase 1: Investigate plugin system (30 min) 1. Read `otterwiki/plugins.py` in the fork 2. Read `docs/plugin_examples/` 3. Determine: Can plugins register Flask blueprints? What hooks are available? 4. Decision: plugin path vs. core modification path ### Phase 2: REST API (core deliverable) 1. Implement the API endpoints (blueprint or plugin, per Phase 1 findings) 2. API key authentication middleware 3. WikiLink parsing and link graph 4. Test with curl ### Phase 3: Chroma integration 1. Add ChromaDB client to the Otterwiki environment 2. Implement index sync (hook-based or periodic, per Phase 1 findings) 3. Add semantic search endpoint 4. Add `/reindex` endpoint for bulk population 5. Test semantic search ### Phase 4: MCP server 1. Scaffold FastMCP server with SSE transport 2. Implement all eight tools as HTTP wrappers 3. Test locally with MCP inspector 4. Configure for docker-compose deployment ### Phase 5: Docker compose and deployment 1. Finalize docker-compose.yaml 2. Reverse proxy configuration (Caddy or Nginx) 3. TLS setup 4. Test end-to-end: Claude.ai → MCP → API → Otterwiki → Git --- ## Success Criteria 1. From Claude.ai, I can `read_note("Actors/Iran")` and get the page content with frontmatter and links 2. From Claude.ai, I can `write_note("Events/2026-03-09 Day 10", content)` and it appears in Otterwiki's web UI within seconds 3. From Claude.ai, I can `semantic_search("interceptor depletion strategy")` and get relevant notes even if they don't contain those exact words 4. From the Otterwiki web UI, I can edit a note and the changes are reflected in subsequent MCP tool calls 5. All changes are Git-committed with meaningful messages and full version history 6. The Chroma index stays in sync with the wiki content regardless of whether edits come from the API or the web UI