Every call you've ever recorded becomes a structured, queryable AI brain — auto-classified, searchable, wired into Claude Code. Feed this one page to Claude Code and skip the 6 production bugs it took me to get here.
You're in 30–50 calls a month. Strategy, clients, board, sales. 48 hours later the gold is gone — and your AI assistant knows none of it.
Calls rot in Fathom. Nobody rewatches them. Your AI gives generic advice because it has zero context about what actually happened in your business last week.
You open Claude Code and ask "Top 3 themes in my client calls this month?" or "Who haven't I spoken to in 60 days?" — and it answers from real transcripts. Automatically. Nothing to do after each call.
"Read this document as my full briefing. Build the Meeting Intelligence Stack end-to-end. Don't deviate from the architecture. Before you start, ask me for: my Supabase URL + service_role key, my exact Fathom name, my clients, my team, my Obsidian vault path, and meeting types to flag. Then generate the schema, the Fathom sync, the bucketing trigger, the iCloud-safe Obsidian export, and a CLAUDE.md. Smoke-test 10 meetings before the full backfill."
__PLACEHOLDER__s) plus a 3-section bucketing template and a SETUP-FOR-CLAUDE.md. Your Claude personalizes instead of rewriting.
Every call recorded
│
Fathom (recorder) ──── MCP · OAuth ────┐
▼
Supabase (Postgres)
├─ meetings + auto-classify trigger
└─ transcript_segments
│
┌───────────────────────────────┼───────────────────────────────┐
▼ ▼ ▼
Obsidian vault Claude Code Scheduled agent
second brain · CLAUDE.md = full same creds in memory
linked notes business context → weekly analysesAlready recording your calls — fathom.video. First sync opens a browser for OAuth via mcp-remote.
Settings → API → grab the project URL + service_role key. God-mode key: env var only, never commit, never share (bug 6).
Max plan. Node's built-in fetch + npx for mcp-remote@latest.
Vault path (iCloud-synced is fine — see bug 1). Your name exactly as in Fathom — the classifier keys off it.
meeting-intelligence/ ├── fathom-sync.mjs # Fathom MCP → Supabase (meetings + transcripts). One-shot backfill, then daily. ├── export-to-obsidian.mjs # Supabase → Obsidian markdown (notes + company/bucket/person hubs). iCloud-safe. ├── supabase/ │ └── bucketing.sql # The auto-classification trigger (the brain). Run once in SQL editor. ├── CLAUDE.md # Context: creds + bucketing reference + business context + "always query first" └── .env # SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY (gitignored)
The reference path. With the bundle, your Claude walks it for you (see Download & install ↓) — but every step is followable by hand too.
The reference walkthrough of what gets built, in order — your part is the three steps at the end. Download & install ↓
Two tables: meetings (id, title, recorded_at, recorded_by, invitees[], url, bucket, company, owner_present) and transcript_segments (meeting_id, speaker, timestamp_seconds, content, deep_link_url).
A Postgres function + trigger sets bucket / company on every INSERT/UPDATE from recorder, invitees and title. Three sections, first-match-wins — order is everything (bugs 3 + 4). A second function computes company independently with the same pattern. Backfill all existing rows at the end by firing the trigger: UPDATE meetings SET recorded_by = recorded_by;
The sync script speaks JSON-RPC to Fathom via mcp-remote, paginates the meeting list, upserts metadata in batches of 100, then pulls transcripts one at a time (bug 2). You probably have 2–5 years of recordings doing nothing — this turns them into an asset overnight.
One note per meeting (frontmatter + speaker-grouped transcript) plus auto-built hubs: per company, per bucket, per person. Writes to /tmp first, then atomic folder-swap (bug 1).
A CLAUDE.md with creds, the bucketing reference, who's who — and the standing instruction "always query Supabase before answering questions about meetings or people."
Cron both scripts daily. For team members: filtered view + read-only key behind RLS — content manager sees client calls, never board or finance. Revoke = rotate the key.
✓ Done when the team queries without seeing god-mode dataWriting the export directly into an iCloud-synced vault produces sync-conflict duplicates (_index 2.md everywhere).
await writeFile( join(VAULT, bucket, file), md); // iCloud races the delete+write
// build all in /tmp, then swap
const TMP = join(tmpdir(), `exp-${Date.now()}`);
for (const f of folders) {
await rm(join(VAULT,f), {recursive:true});
await cp(join(TMP,f), join(VAULT,f),
{recursive:true});
}Fathom's MCP rate-limits hard on bulk transcript pulls — parallel requests = 429 storm and half your data missing.
await Promise.all( meetings.map(m => getTranscript(m.id)));
for (const m of meetings) {
await getTranscript(m.id);
await sleep(3000); // + exp backoff
} // 4s 8s 16s 32s on 429If "you're not involved → skip" runs before "team member recorded it", every meeting you weren't personally in gets dropped to NULL and vanishes.
1. external guard → NULL 2. team recorder → department 3. you're involved → client
1. team recorder → department 2. external guard → NULL 3. you're involved → client
Specific rules must precede generic ones, or meetings land in the wrong bucket.
when title ilike '%board%' then 'board' -- catches squad leads!
when title ilike '%squad lead%' then 'leads' -- FIRST when title ilike '%board%' then 'board' -- after
Re-syncing a meeting that already has segments doubles them silently.
await insert(segments); // every re-run doubles the data
if (await hasTranscript(m.id)) continue;
await del(`?meeting_id=eq.${m.id}`);
await insert(segments);
// meetings: Prefer: merge-duplicatesIt bypasses RLS — god-mode. .env only (gitignored), read from process.env. Team access = scoped anon key behind a filtered view: content manager sees client calls, never board, finance, or invitee emails.
Fathom returns timestamped markdown with deep links [MM:SS](url) Speaker: text — parse that first, and keep a plain Speaker: text fallback for recordings without timecodes. In people-hub extraction: skip bare emails (florian@fino.de → skip) and skip yourself, or you pollute _people/ with junk nodes.
Open Claude Code and ask: "What are the top 3 themes in my client calls this month — and which meeting titles are you basing that on?"
If it cites real titles and dates → live. Second check: click any person in Obsidian's _people/ → every conversation you've ever had with them.
| Where | What to change |
|---|---|
bucketing.sql §a | Your team members → their departments |
bucketing.sql §c | Your clients (name + email domain) + title keywords |
fathom-sync.mjs | Your exact Fathom display name |
export-to-obsidian.mjs | DEFAULT_VAULT → your vault path |
.env | SUPABASE_URL · SUPABASE_SERVICE_ROLE_KEY |
| Layer | Tool | Cost |
|---|---|---|
| Recording | Fathom | $19–39/mo |
| Database | Supabase | free–$25/mo |
| AI | Claude Code (Max) | $20/mo |
| Second brain · Automation | Obsidian · cron | free |
Total: ~$40–80/month — to turn every meeting into a queryable AI brain.
crontab -l)_index.md in the vaultDownload the bundleThe production sync + export scripts, a bucketing template, and the setup brief.
Unzip & open in Claude CodeOpen the unzipped folder in Claude Code (or Cowork).
Say the magic sentence"Read SETUP-FOR-CLAUDE.md and install." — it asks for your Supabase keys, Fathom name, clients & team, then wires everything.
Download the bundle Free · 4 files · scripts + bucketing template + setup brief