- 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
5.2 KiB
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)
- Redis + BullMQ background indexing/embedding workers
- Git synchronization hook (auto-reindex on push via webhook)
- Document revision system (
document_revisions) - MCP Resources (
mcpedia://docs/...) alongside existing tools
Architecture decisions (locked)
- Redis: shared imrnes Redis
100.121.180.82:6379, no auth (verified+PONG).REDIS_URLenv (defaultredis://100.121.180.82:6379), optionalREDIS_PASSWORD. BullMQ key prefixmcpedia: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/coreasindexContentFile(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: addREDIS_URL,REDIS_PASSWORD,QUEUE_PREFIX.
packages/db
src/schema.ts: adddocumentRevisionstable (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.jsonentry (keeps drizzle-kit consistent even though we apply manually).
packages/core (new)
src/index.service.ts:indexContentFile(relPath: string, reason = "index")— parse → upsertdocuments→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): scriptstart: bun src/index.ts.src/index.ts:startWorker()+ heartbeat log.
apps/api
src/index.ts: addPOST /hooks/reindex(full) andPOST /hooks/index?slug=(single) webhook routes → enqueue jobs. Mount AFTER /trpc.src/router.ts: addjobStatus(id→state/prev/failedData),queueStatus(waiting/active/completed/failed counts),revisions(slug→list),restoreRevision(id→new slug/doc).package.json: add@mcpedia/queuedep,hooksreused.
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: addlistResources+ readmcpedia://docsassertion.
scripts
scripts/indexer.ts: refactormain()to callrunFullIndex().
Root
package.json: add"worker": "bun --cwd apps/worker run start","reindex": "bun run scripts/worker.ts"? No —workerruns the listener; triggering reindex =bun run apiwebhook orenqueueFullIndexhelper. Add"enqueue-index": "bun run scripts/enqueue.ts"(one-shot enqueue)..env.example: addREDIS_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)
bun installpicks up new deps.bunx turbo run build+typecheckgreen across workspace.- Real BullMQ e2e against imrnes Redis: script that enqueues an
index-docjob, 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.
bun --cwd apps/mcp run smokepasses (incl. new resources).bun run index(runFullIndex) green; verifydocuments,document_chunks,document_revisionsrow counts via psql.- API webhook:
curl -XPOST localhost:4020/hooks/reindexenqueues; worker processes;curl localhost:4020/trpc/queueStatusreflects counts. - MCP resource read returns real content.