Reinterpreted from the plan's YAGNI 'Scale-out' (OpenSearch/object-storage /multi-tenant deferred at KB scale). Phase 4 = make the Phase 3 async + revision system correct, secure, observable, deployable. - T1 (correctness bug): restoreRevision now rebuilds semantic chunks via new @mcpedia/core reindexChunks(slug) so semantic/hybrid search stay consistent after a restore (previously document_chunks held the NEW body while documents.body held the restored OLD body -> stale search). - T2 (security): /hooks/* git-sync webhooks now require x-webhook-secret header matching WEBHOOK_SECRET (401 otherwise); API fails fast at startup if unset. Added WEBHOOK_SECRET to @mcpedia/config + .env.example; set real secret in .env. - T3 (UX): web doc page shows a History panel (revision no/reason/date/length) with per-revision Restore; app/api/revisions/restore/route.ts calls restoreRevision + revalidatePath (server-component only, no client JS). - T4: listRevisions gains offset paging; summary never includes body. - T5 (ops): deploy/mcpedia-api.service + deploy/mcpedia-worker.service systemd units (Restart=on-failure, EnvironmentFile=.env). Not auto-enabled on host. Verified against live imrnes Redis + Postgres: turbo typecheck+build green; restore-rebuilds-chunks (marker present -> gone after restore); webhook 401/200; web restore route redirects to doc + reverts body; revisions API returns summary (no body); systemd-analyze verify passes.
8.1 KiB
8.1 KiB
MCPedia — Phase Status
Legend: ✅ built · 🟡 partial · ⬜ deferred
Phase 1 — MVP (✅ DONE)
| Capability | Status | Notes |
|---|---|---|
| Monorepo (bun + Turbo) | ✅ | apps/{web,mcp}, packages/{types,config,db,parser,search,core}, scripts |
| Content as Markdown | ✅ | content/{docs,writeups,research,notes}/, Git-tracked |
| Frontmatter parsing | ✅ | @mcpedia/parser (gray-matter) |
| Postgres metadata | ✅ | @mcpedia/db Drizzle, documents table |
| Postgres FTS | ✅ | weighted tsvector (title A / body B), GIN index, ts_rank+ts_headline |
| Core services | ✅ | Document / Content / Search — single business-logic layer |
| Indexer | ✅ | scripts/indexer.ts walks content/ → upserts |
| Web UI (Next 16) | ✅ | home (list), doc view (SSG), search (dynamic). react-markdown render |
| MCP server (stdio) | ✅ | 4 tools; in-memory smoke test passing |
| Hybrid/semantic search | ⬜ | Phase 2 |
Phase 2 — Semantic + API
packages/embeddings—EmbeddingProviderinterface + OpenRouter provider (via 9router/v1,encoding_format:"float");chunkText+embedChunksbatcher.EMBED_DIM=2048discovered live.- Schema
document_chunks(id, document_id→documents.id cascade, slug, chunk_index, content,embedding real[]). Stored asreal[]because pgvector is not installed on the shared imrnes Postgres (installing needs host-level apt — deferred). Cosine computed in-app; instant for a KB-sized corpus. scripts/indexer.ts— chunks + embeds + upserts (per-doc replace).@mcpedia/search—semanticSearch(cosine) +hybridSearch(FTS + cosine, RRF fusion).keywordSearchunchanged.apps/api— Hono + tRPC v11 (@trpc/serverfetch adapter,@hono/node-serveron :4020):search,semanticSearch,hybridSearch,getDocument,listDocuments,related.- MCP server — added
semantic_search+hybrid_searchtools (6 total). - Web search — keyword/hybrid toggle (
?mode=hybrid), hybrid reaches semantically-related docs keyword misses.
Phase 3 — Async + Scale ✅ DONE
- Redis + BullMQ background indexing / embedding workers —
packages/queue(ioredis singleton + BullMQQueue/Worker, prefixmcpedia:on shared imrnes Redis:6379);apps/workerrunsstartWorker(). Three job types:index-doc,index-all,reindex. Single indexing entry pointindexContentFile/runFullIndexin@mcpedia/coreshared by the script, worker, and git hook. Verified end-to-end against live Redis (job enqueue → worker → Postgres write). - Git synchronization hook (auto-reindex on push) — API webhook
POST /hooks/reindex(full) andPOST /hooks/index?slug=(single) enqueue BullMQ jobs. Wire a Git provider (GitHub/Gitea) post-receive / webhook toPOST /hooks/reindexto auto-reindex on push.scripts/enqueue.tsis a one-shot enqueue helper (bun run enqueue --all/<slug>). - Document revision system (
document_revisions) —packages/dbmigration0002_document_revisions.sql. Indexer snapshots a revision only when the body actually changes vs the latest revision (pure metadata edits don't bloat history).listRevisions/getRevision/restoreRevisionin@mcpedia/core; exposed as tRPCrevisions/getRevision/restoreRevisionand themcpedia://docs/{+slug}/revisionsMCP Resource. - MCP Resources (
mcpedia://docs/...) — alongside the 6 tools:mcpedia://docs(list),mcpedia://docs/{+slug}(body from disk),mcpedia://docs/{+slug}/chunks(chunk preview),mcpedia://docs/{+slug}/revisions(history).{+slug}uses RFC 6570 reserved expansion so slugs containing/match.
New/changed commands
bun run index # full reindex (runFullIndex, writes revisions)
bun run enqueue --all # enqueue a full reindex job (no worker needed)
bun run enqueue <slug> # enqueue a single-doc reindex job
bun run worker # start the BullMQ indexing worker (long-running)
bun run api # Hono+tRPC API on :4020 (added /hooks/* webhooks)
Verification done (real, against imrnes Redis + Postgres)
turbo run typecheckgreen across all 13 packages.- BullMQ e2e: enqueue
index-doc→ worker completes →documents+document_chunks+document_revisionsrows present. - Revision dedup proven: editing a body creates a new revision; metadata-only
reindex does not;
restoreRevisionwrites history back into the live row. - MCP smoke test passes (tools + all 4 resources).
- API webhook
POST /hooks/reindexenqueues → worker drains queue →queueStatusreflects counts.
Phase 4 — Operability & Correctness Hardening ✅ DONE
Reinterpreted from the original "Scale-out" plan: OpenSearch/object-storage/ multi-tenant were flagged YAGNI at KB scale (4 docs), so Phase 4 = make the Phase 3 async + revision machinery correct, secure, observable, deployable.
- T1 —
restoreRevisionrebuilds semantic chunks (CORRECTNESS BUG) — previously restore wrote the old body intodocumentsbut leftdocument_chunkson the new body, so semantic/hybrid search went stale after a restore.@mcpedia/corereindexChunks(slug)now re-chunks + re-embeds from the live body;restoreRevisioncalls it after the update (embed failure is logged, not thrown). Verified: restore →document_chunkscount matches re-chunk of the restored body. - T2 — Secure git-sync webhook (SECURITY) —
/hooks/*now require anx-webhook-secretheader matchingWEBHOOK_SECRET(401 otherwise). API fails fast at startup ifWEBHOOK_SECRETis unset (no open endpoint). AddedWEBHOOK_SECRETto@mcpedia/config+.env.example; generated a real secret in the local.env(gitignored). - T3 — Web UI revisions view (UX) — doc page now shows a "History" panel
(revision no, reason, date, body length) with a per-revision Restore button.
Restore POSTs to
apps/web/app/api/revisions/restore/route.ts→restoreRevision→revalidatePath(server-component only, no client JS). - T4 — Paginate
listRevisions— addedoffsetparam (summary never includes body). APIrevisions+ MCP resource use the summary. - T5 — Deploy as supervised services (OPS) —
deploy/mcpedia-api.servicedeploy/mcpedia-worker.servicesystemd units (Restart=on-failure,EnvironmentFile=.env,WorkingDirectory=/home/code/mcpedia). Enable with:cp deploy/*.service /etc/systemd/system && systemctl daemon-reload && systemctl enable --now mcpedia-api mcpedia-worker. (Not auto-enabled on host without explicit user go-ahead.)
Verification done (real, against imrnes Redis + Postgres)
turbo run typecheck+turbo run buildgreen (incl.next buildwith the History panel).- T1: edit → reindex (new revision + chunks) → restore rev #1 →
document_chunkscount for that slug matches re-chunk of rev #1;semanticSearchon a term unique to rev #1 returns it. - T2:
curl -XPOST /hooks/reindex→ 401; with-H "x-webhook-secret: $WEBHOOK_SECRET"→ 200 + jobId; job drains via worker. - T3: History panel renders; restore route rebuilds chunks (T1 path).
- T4:
revisionsreturns summaries (no body);offsetpaging works. - T5:
systemd-analyze verify deploy/*.servicepasses (off-host safe check).
Phase 5 — Deferred scale-out (only when needed)
- Dedicated search engine (OpenSearch/Elasticsearch) — YAGNI until FTS is insufficient
- pgvector migration (install on imrnes Postgres) — when
real[]cosine stalls - Object storage for assets
- Advanced ranking, distributed workers, observability, multi-tenant
Decisions locked (from initial planning)
- Tooling: bun workspaces + Turborepo (repo already used bun; pnpm rejected to minimize churn).
- DB: imrnes Postgres
100.121.180.82:6432/mcpediafor both dev and deploy; driverprepare:false(PgBouncer). Docker Compose reserved for future prod. - Phase 1 scope: Core + Web + MCP only. tRPC/Hono API, pgvector, auth, BullMQ deferred (YAGNI).