Files
mcpedia/.hermes/plans/phase3-plan.md
T
asepharyana 8f2229d447 feat(mcpedia): Phase 3 — async indexing (BullMQ), git-sync webhook, revisions, MCP Resources
- packages/queue: ioredis singleton + BullMQ Queue/Worker (prefix mcpedia:
  on shared imrnes Redis :6379); apps/worker runs startWorker()
- @mcpedia/core: indexContentFile/runFullIndex (single indexing entry point
  shared by script/worker/hook) + revision.service (list/get/restore)
- document_revisions table (migration 0002) — snapshots only on body change
- apps/api: POST /hooks/reindex + /hooks/index webhooks; tRPC revisions,
  getRevision, restoreRevision, jobStatus, queueStatus
- apps/mcp: register MCP Resources mcpedia://docs{/,+slug/chunks/revisions}
  ({+slug} RFC6570 reserved expansion for slugs containing /)
- apps/mcp zod pinned to ^4 to match MCP SDK 1.30 compiled types
  (resolves registerTool TS2589/ShapeOutput skew)
- scripts/enqueue.ts one-shot job enqueue helper; indexer refactored to runFullIndex
- PHASES.md/README/.env.example/docs updated
2026-08-19 20:18:28 +07:00

5.2 KiB

MCPedia — Phase 3 "Async + Scale" Implementation Plan

Status: Phase 1 (MVP) + Phase 2 (Semantic+API) DONE. Phase 3 adds async background work, git-driven reindex, document revision history, and MCP Resources. All logic stays in @mcpedia/core; new packages/queue wires BullMQ; apps/worker runs the worker process; the existing API gets a git-sync webhook + job-status procedures; the MCP server gains Resources.

Scope (4 features from PHASES.md)

  1. Redis + BullMQ background indexing/embedding workers
  2. Git synchronization hook (auto-reindex on push via webhook)
  3. Document revision system (document_revisions)
  4. MCP Resources (mcpedia://docs/...) alongside existing tools

Architecture decisions (locked)

  • Redis: shared imrnes Redis 100.121.180.82:6379, no auth (verified +PONG). REDIS_URL env (default redis://100.121.180.82:6379), optional REDIS_PASSWORD. BullMQ key prefix mcpedia: to avoid collisions on the shared instance.
  • Queue lib: bullmq@6.1.2 + ioredis@6.0.0 (BullMQ peer dep). Pass an ioredis instance; BullMQ duplicates it for blocking commands.
  • Single source of truth preserved: per-doc indexing logic moves into @mcpedia/core as indexContentFile(relPath, reason?). The script, the worker, and the git hook ALL call this. Revisions are snapshotted inside it.
  • Revisions: created only when body actually changes vs the latest revision (avoids bloat on every sync). Stored in document_revisions.

Files touched

packages/config

  • src/index.ts: add REDIS_URL, REDIS_PASSWORD, QUEUE_PREFIX.

packages/db

  • src/schema.ts: add documentRevisions table (id, documentId→documents.id cascade, slug, revisionNo int, title, body, meta jsonb, reason text, createdAt). Index (document_id, revision_no DESC), (slug).
  • drizzle/0002_document_revisions.sql: migration (applied via psql).
  • drizzle/meta/0002_snapshot.json + _journal.json entry (keeps drizzle-kit consistent even though we apply manually).

packages/core (new)

  • src/index.service.ts:
    • indexContentFile(relPath: string, reason = "index") — parse → upsert documents → indexChunks → snapshot revision (if changed).
    • runFullIndex(reason?) — walk content, index each, return counts.
  • src/revision.service.ts:
    • createRevision(...), listRevisions(slug, limit), getRevision(id), latestRevisionBody(slug), restoreRevision(id).
  • src/index.ts: export both.

packages/queue (NEW)

  • package.json (@mcpedia/queue): deps bullmq, ioredis, @mcpedia/core, @mcpedia/db, @mcpedia/config.
  • src/client.ts: ioredis instance factory from config.
  • src/queue.ts:
    • INDEX_QUEUE = "mcpedia-index".
    • enqueueIndexDoc(slug, absPath, reason), enqueueFullIndex(reason).
    • getQueue() lazy singleton.
  • src/worker.ts: startWorker() — BullMQ Worker with 3 job types: index-doc (single), index-all (full), reindex (full, reason=git-push). Graceful shutdown on SIGINT/SIGTERM. Job progress + error handling.

apps/worker (NEW)

  • package.json (@mcpedia/worker): script start: bun src/index.ts.
  • src/index.ts: startWorker() + heartbeat log.

apps/api

  • src/index.ts: add POST /hooks/reindex (full) and POST /hooks/index?slug= (single) webhook routes → enqueue jobs. Mount AFTER /trpc.
  • src/router.ts: add jobStatus (id→state/prev/failedData), queueStatus (waiting/active/completed/failed counts), revisions (slug→list), restoreRevision (id→new slug/doc).
  • package.json: add @mcpedia/queue dep, hooks reused.

apps/mcp

  • src/index.ts: register Resources:
    • mcpedia://docs (list all metas)
    • mcpedia://docs/{slug} (full body from disk)
    • mcpedia://docs/{slug}/chunks (chunk previews)
    • mcpedia://docs/{slug}/revisions (revision list)
  • src/smoke.test.ts: add listResources + read mcpedia://docs assertion.

scripts

  • scripts/indexer.ts: refactor main() to call runFullIndex().

Root

  • package.json: add "worker": "bun --cwd apps/worker run start", "reindex": "bun run scripts/worker.ts"? No — worker runs the listener; triggering reindex = bun run api webhook or enqueueFullIndex helper. Add "enqueue-index": "bun run scripts/enqueue.ts" (one-shot enqueue).
  • .env.example: add REDIS_URL, REDIS_PASSWORD, QUEUE_PREFIX.

Docs

  • PHASES.md: mark Phase 3 items DONE with notes.
  • README.md: document worker, webhook, revisions, MCP resources.

Verification (real, not claimed)

  1. bun install picks up new deps.
  2. bunx turbo run build + typecheck green across workspace.
  3. Real BullMQ e2e against imrnes Redis: script that enqueues an index-doc job, starts a Worker, asserts the job completes and the doc row
    • chunks + a revision row appear in Postgres. Verifies Redis+ioredis+bullmq
    • db + core all wired correctly.
  4. bun --cwd apps/mcp run smoke passes (incl. new resources).
  5. bun run index (runFullIndex) green; verify documents, document_chunks, document_revisions row counts via psql.
  6. API webhook: curl -XPOST localhost:4020/hooks/reindex enqueues; worker processes; curl localhost:4020/trpc/queueStatus reflects counts.
  7. MCP resource read returns real content.