Skip to content
Michael Ferreyros

Case study

PKA: Personal Knowledge Assistant

A wiki that compiles itself, a task board, and a capture inbox, all served by one MCP server I wrote in Python. My day job is recommending this kind of architecture. This is proof I run it myself.

The problem

Notes in one app, tasks in another, bookmarks in a third, and the AI assistant can see none of them. Every new surface (terminal, desktop, phone, web) adds another silo. The fix isn't another app. It's one protocol layer everything speaks.

What I built

PKA is one brain across five surfaces: Claude Code in the terminal, Claude Desktop, Claude on my phone, a Next.js web UI, and agents running unattended on Railway. Every surface reads and writes the same Postgres tables. That's the design rule: if I need a capability, it becomes an MCP tool first. Then every Claude surface has it for free. One honest exception: the phone doesn't speak MCP; the Action Button posts straight into the same inbox table. Same tables, shorter path.

The server registers 38 tools: search the wiki, ask it questions, ingest a YouTube video, capture a thought, triage the inbox, move a task across the board.

How it works

Capture. YouTube videos go in by URL: yt-dlp pulls the transcript, Whisper is the fallback when there isn't one. RSS and Atom feeds get polled, classified, and topic-tagged. Tweets and GitHub repos ingest too. On my phone, the Action Button opens Wisprflow dictation; I speak a thought and it posts straight into the inbox table. Capture has to be one gesture or it doesn't happen.

Compile. An LLM compiler turns raw transcripts into structured wiki pages, organized under five life-area domains (work, school, business, personal, projects). The compiler sees the full page index before it writes. If new material overlaps an existing page, it merges: a slug collision triggers an LLM merge, never a silent overwrite. Every update is trigger-captured into a revision history, and a batch consolidation pass dedupes the wiki over time. Pages that get merged away leave slug aliases behind, so old links keep working.

Serve. Supabase Postgres is the single source of truth. Search is hybrid: Postgres full-text plus Voyage embeddings, fused with reciprocal rank fusion, and it degrades gracefully to full-text-only if the embedding key is absent. A markdown mirror syncs into an Obsidian vault for graph browsing; the vault is secondary and fully regenerable from the database. The web UI is Next.js 16 on Vercel (live at my.ferreyros.me, a subdomain of this site) behind Google OAuth with row-level security on. A queue worker on Railway handles long-running ingestion so nothing blocks.

Triage. Inbox items get auto-classified: task, journal entry, reminder, note, among others. The task board is a plain Kanban (todo, in progress, done) with priorities, and the same tools that move cards in the web UI move them from the terminal.

The rule that makes it work

The wiki isn't decoration. My working sessions query it before designing anything: a few hundred compiled pages (design principles, architecture patterns, and agent-orchestration notes among them) drawn from what I've actually read and watched. The assistant cites the pages it draws from.

One instruction matters most: when the wiki has nothing, say so. A knowledge system that can't admit an empty result isn't a knowledge system.

Why this matters

When I tell a research institute that MCP is the right integration layer (a skill plus a connector is a plugin; one protocol beats ten point-to-point integrations), I'm not repeating a vendor deck. I run this architecture daily, on my own data, across five surfaces. The repo carries a pytest suite that CI enforces on every push. Advice you haven't shipped is opinion.

Stack

  • Server: Python MCP server (stdio), 38 tools, one unification layer
  • Data: Supabase Postgres: knowledge, revisions, sources, tasks, inbox
  • Search: Postgres FTS + Voyage embeddings, RRF hybrid
  • Ingestion: yt-dlp + Whisper fallback, RSS/Atom poller, queue worker on Railway
  • Mirror: Obsidian vault, regenerable markdown
  • Web: Next.js 16 + React 19 + Tailwind 4 on Vercel, RLS on