Personal semantic memory for Claude — built on top of your client calls.
TLBrain indexes meeting transcripts from TL;DV, stores everything in Google Drive, and lets Claude search through your conversations by meaning — without you having to paste anything manually.
graph LR
A[TL;DV] -->|transcripts| B[Google Drive]
B -->|incremental sync| C[TLBrain]
C -->|semantic retrieval| D[Claude]
Works with claude.ai (web) · Claude Desktop (Chat · Cowork · Code) · Claude Mobile (iOS · Android) Google Drive + Qdrant + Gemini · Single-user · Near-zero infrastructure cost · Deploy with one command
- Who it's for
- Example queries
- How it works in practice
- Quick Start
- Upgrading from v1 to v2
- Why TLBrain
- Why not just use Claude Projects?
- Why MCP?
- Cost Breakdown
- Limitations
- Technical Architecture
Consultants, sales reps, account managers, founders — anyone with a high volume of client calls and no time to re-read transcripts.
"Find what we agreed on with Acme in March regarding money"
"What objections did the client raise during the last call?"
"Show the conversation where we discussed extending the contract"
Claude retrieves the most relevant transcript fragments automatically. Saves tokens — only the relevant parts end up in context, not the full transcript. If you need more detail, ask Claude to read the full transcript from that call.
Transcripts are automatically organized into folders in Google Drive. The hierarchy is arbitrary — any depth. If a transcript landed in the wrong folder — ask Claude to move it, or drag the file manually. Either way, TLBrain picks up the change automatically.
Transcripts that couldn't be assigned to any folder land in a special _unassigned folder. Claude will notify you and suggest reviewing — just move them to the right folder and they'll be re-synced automatically.
Noticed errors in a transcript? Fix them directly in Google Drive — TLBrain will re-sync within 15 minutes. Or ask Claude to trigger a sync immediately.
If Claude couldn't find the right transcript — share the Google Drive link and tell Claude what the document is about. TLBrain will remember that hint and use it in future searches.
Already using TL;DV? You can import your entire existing history. Best to do it in small batches — TLBrain may not recognize the client on the very first transcript, but as you correct it, it learns the pattern. Usually only the first transcript per folder needs a fix.
You can ask Claude for a full list of folders and their sync status at any time.
Prerequisites: Google Cloud project, TL;DV account, Qdrant Cloud (free tier), Gemini API key.
Despite the one-command deployment, all third-party services (Google Cloud, Firebase, Qdrant, Google AI Studio) must be registered and configured manually — the steps below walk you through each one.
Stuck at any step? Share this section of the README and a screenshot of where you are with Claude — it will give you a hint right away.
cp .env.example .envOpen https://console.cloud.google.com/, create a new project. Recommended name: tlbrain-prod. Copy your Project ID (e.g. tlbrain-prod-496610).
Add to .env:
PROJECT_ID=your-project-idhttps://cloud.google.com/sdk/docs/install
gcloud auth login
gcloud config set project YOUR_PROJECT_IDOpen https://console.firebase.google.com/, on the "Create a project" page enter the project name (tlbrain-prod), then click the "Add Firebase to Google Cloud project" link at the bottom. In the dialog that opens, select your GCP project (tlbrain-prod). On the "Configure Google Analytics" step, disable Google Analytics — it is not needed for TLBrain.
Once the project is ready, go to Firestore in the left menu and click Create database with these settings:
- Edition: Standard
- Mode: Production
- Region: europe-west1
- Database ID: (default)
Open https://cloud.qdrant.io, create a free cluster:
- Name: any (e.g.
tlbrain) - Provider: Google Cloud Platform
- Region: Frankfurt
- Tier: Free (1 node, 4 GiB disk, 1 GiB RAM)
Add to .env:
QDRANT_URL=https://YOUR-CLUSTER-URL:6333
QDRANT_API_KEY=your-qdrant-api-keyCreate a dedicated empty folder in Google Drive — this will be the root for all TLBrain transcripts. Don't use an existing folder with other files in it.
Subfolders can be created via MCP (create_folder) or manually in Google Drive — TLBrain picks them up automatically.
Add to .env:
ROOT_FOLDER_URL=https://drive.google.com/drive/folders/YOUR_FOLDER_ID- Open APIs & Services → Audience, select External, click Next → Create
- Open APIs & Services → Credentials
- Create Credentials → OAuth client ID, type: Web application, name:
TLBrain MCP - On the same form, scroll down to Authorized redirect URIs and add:
https://claude.ai/api/mcp/auth_callbackhttp://localhost:8085
- Open APIs & Services → Audience → Publish App → confirm
Without publishing, refresh tokens expire every 7 days (Testing mode limitation).
Add to .env:
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
ALLOWED_EMAIL=your-email@gmail.com # leave empty to skip OAuth when connecting Claude — simpler setup, but anyone with the MCP URL gets full access to your transcriptsOpen https://aistudio.google.com/apikey, create an API key.
Important: Switch your Google AI Studio account from Free tier to Tier 1 (pay-as-you-go). Free tier rate limits are too low to process transcripts and index them into the vector database. All other services in TLBrain remain on free tier.
Add to .env:
GEMINI_API_KEY=your-gemini-api-keyVERSION=2.0.0 # or latest for the most recent build
# Google Cloud
REGION=europe-west1
# Retrieval tuning
RETRIEVAL_TOP_K=15
RETRIEVAL_SCORE_THRESHOLD=0.6
# Sync scheduler — controls update latency after Drive changes
# "*/5 * * * *" = every 5 min | "*/15 * * * *" = every 15 min | "0 4 * * *" = daily
SYNC_CHECKER_SCHEDULE="*/15 * * * *"
# Max instances (controls cost — Cloud Run scaling)
VECTOR_SYNC_MAX_INSTANCES=2
MCP_MAX_INSTANCES=1
TLDV_IMPORT_MAX_INSTANCES=2
# Cloud Tasks
VECTOR_SYNC_QUEUE=tlbrain-vector-sync-queue
# Infrastructure names (Cloud Run / Cloud Function)
MCP_SERVICE_NAME=tlbrain-mcp
VECTOR_SYNC_SERVICE_NAME=tlbrain-vector-sync
SYNC_CHECKER_NAME=tlbrain-sync-checker
# TL;DV connector
TLDV_IMPORT_SERVICE_NAME=tlbrain-tldv-import
TLDV_IMPORT_QUEUE=tlbrain-tldv-import-queue
TLDV_WEBHOOK_FUNCTION_NAME=tlbrain-tldv-webhook
TLDV_RECONCILIATION_FUNCTION_NAME=tlbrain-tldv-reconciliation
TLDV_RECONCILIATION_SCHEDULE="0 3 * * *"Before deploying, make sure your
.envhas no comment lines (starting with#) — remove any that are there.
bash infra/deploy/deploy.shA browser window will open for Google authorization:
- If you see a "Google hasn't verified this app" warning — click Advanced → Go to TLBrain MCP (unsafe)
- On the permissions screen, make sure all checkboxes are selected, then click Continue
Deploys the MCP server, Sync service, Cloud Tasks queue, and Sync Checker. The script will ask Continue deploy? (y/n) — enter y to proceed.
When finished, the script prints the MCP URL and TL;DV webhook URL — copy them, you'll need them in the next steps.
The deploy takes ~7 minutes to complete.
In TL;DV click your avatar in the bottom-left corner → Settings → Integrations → Webhooks → Add, paste the webhook URL from the deploy output → event: TranscriptReady.
- Open any Claude client → Customize → MCP Servers → Add
- URL:
https://YOUR-MCP-URL.run.app/mcp - Claude will detect OAuth automatically and prompt you to sign in with Google
- Sign in with the same email as
ALLOWED_EMAIL
After each redeploy, remove the MCP server and add it again — the session is tied to the Cloud Run instance.
If you're already using TLBrain, do not just redeploy — the v2 code is incompatible with v1 data. A simple redeploy will break folder search: transcripts will lose their folder association and become unreachable by path.
v2 changes the core data model: folders are now tracked by Drive folder ID instead of a flat client_name string. This enables arbitrary nesting depth and makes folder renames and moves work automatically. To convert your existing data, run the one-time migration before redeploying.
The migration runs in four steps:
- Build
folders/— scans the Drive hierarchy underROOT_FOLDERand populates the newfolders/Firestore collection. - Update
transcript_index— replacesclient_namewithparent_id(Drive folder ID) in every transcript record. - Update Qdrant — replaces
client_namewithparent_idin vector payloads (no re-embedding needed). - Delete
clients/— removes the old Firestore collection.
pip install google-cloud-firestore google-api-python-client google-auth qdrant-client
gcloud auth application-default login
python -m migrations.v1_to_v2.migrate --dry-run # preview changes without writing
python -m migrations.v1_to_v2.migrate # apply
python -m migrations.v1_to_v2.verify # post-migration checkAfter the migration completes successfully, redeploy all services.
| Feature | TLBrain | Typical RAG |
|---|---|---|
| Single-user optimized | ✅ | ❌ |
| Near-zero infrastructure cost | ✅ | |
| Conversation-aware retrieval | ✅ | ❌ |
| MCP-native (works with Claude) | ✅ | ❌ |
| Utterances stored without dense embeddings | ✅ | ❌ |
| Incremental sync (no full reindex) | ✅ | |
| Hybrid search (semantic + BM25) | ✅ |
Cheap semantic memory. Embeddings are generated only for summaries and facts — not for every utterance. Utterances are stored with BM25 sparse vectors and retrieved by range. This drastically reduces cost and vector storage size.
Works with Google Drive. Transcripts are stored as native Google Docs — no proprietary formats. Folders mirror your real hierarchy — unlimited nesting depth. If something goes wrong, the data is always directly accessible.
MCP-native. Claude connects like a standard MCP server. No plugins, no custom integrations — just the protocol.
Incremental sync. One file changed — only that file gets reindexed. SHA-256 hash of file content tracks edits; folder moves are detected separately via Drive Changes API.
Conversation-aware retrieval. Retrieval works with utterance windows, not arbitrary chunks. Summaries cover overlapping ranges; facts are anchored to specific dialogue segments.
Deterministic pipeline. temperature=0, prompt versioning, idempotent operations. Same file → same result.
No vendor lock-in. Google Drive, Qdrant Cloud, Gemini — all on free/pay-per-use. Qdrant Cloud free tier is enough for most single-user scenarios.
| Claude Projects | TLBrain | |
|---|---|---|
| Adding transcripts | Manual upload | Auto-sync from TL;DV |
| Context limit | Hits ceiling fast | Semantic retrieval — only relevant fragments |
| Structured memory | None | Facts, summaries, decisions extracted per call |
| Cost at scale | Grows with context size | Fixed ~$0.20 per transcript indexed |
| Search | Keyword / full-text | Hybrid semantic + BM25 |
| Client organization | Manual | Auto-detected, correctable |
Claude Projects is great for small, curated knowledge bases. TLBrain is built for ongoing workflows where new calls come in every week and you need to search across months of history without thinking about it.
TLBrain works as a standard remote MCP server. That means Claude can use it directly — no browser extensions, no plugins, no custom UI.
Works with any Claude client that supports remote MCP:
- claude.ai — web
- Claude Desktop — Chat, Cowork, Code (macOS, Windows)
- Claude Mobile — iOS, Android
Connect once, use everywhere. Configuration syncs automatically across clients.
TLBrain is designed so you don't pay for what a single-user scenario doesn't need.
Note on initial sync costs: If you're importing a large backlog of transcripts from TL;DV, expect noticeable Gemini charges during the first sync. Gemini (Tier 1) is billed per token — transcript processing and embedding generation are the main costs. Other services run on free tier, but heavy initial load may push them over their monthly limits and trigger billing. After the first month, free tier limits reset and the system returns to near-zero cost.
Qdrant Cloud (~$0/month):
- 1 GiB RAM, 4 GiB disk — free forever
- Utterances stored with BM25 sparse vectors (not dense) → 4× less space
- Embeddings only for summaries and facts → ~10–20% of utterance volume
- Enough for most single-user scenarios
Gemini (~$0.20 per transcript):
- You only pay for Gemini when indexing new transcripts
- ~$0.20 per transcript — depends on conversation length, number of utterances, and content volume
gemini-embedding-2,output_dimensionality=768→ 4× cheaper than 3072- One Gemini request per window (summary + facts together)
- If a file hasn't changed, it's skipped — Gemini is never called again
Google Cloud (~$0/month):
- Cloud Run: free tier covers all single-user traffic
- Cloud Functions, Cloud Tasks, Cloud Scheduler: included in free tier
- Firestore: free tier covers 50k reads and 20k writes per day — more than enough for single-user at 15 calls/week
As long as you stay within the free tier limits, you only pay for syncing new transcripts — roughly $0.20 per call. Everything else is free. You can go on a six-month vacation, come back, and nothing will be lost and nothing will have cost you a penny while you were away.
- Single-user only — the architecture does not support multi-tenant
- TL;DV as the primary source — other providers require writing a connector
- Polling sync — changes are picked up with a delay up to
SYNC_CHECKER_SCHEDULE(default: 15 min) - Native Google Docs only — other file formats are ignored
- No cross-transcript aggregation yet — a single query returns fragments from the most relevant transcript, not a summary across multiple calls. Workaround: ask Claude to search by client + date range, then ask it to read the full transcript from the results if needed
MCP Service (Cloud Run)
- Remote MCP endpoint for Claude clients
- Retrieval pipeline (semantic + BM25 + pin)
- Google OAuth 2.0 (single-user via
ALLOWED_EMAIL)
Vector Sync Service (Cloud Run, scale to 0)
POST /sync/doc/{doc_id}— index a single document- Parsing, windowing, summary/facts generation, write to Qdrant
- Firestore transactions to prevent double-processing
Sync Checker (Cloud Function gen2)
- Triggered by
SYNC_CHECKER_SCHEDULE(default:*/15 * * * *) - Drive Changes API — only changed files
- Recovery of stale tasks
TL;DV Connector:
- Webhook Function — receives
TranscriptReady, creates a Cloud Tasks job - Reconciliation Function — daily check of the last 48h
- Import Service (Cloud Run) — downloads transcript, detects client, creates Google Doc
When Claude calls query, six stages run:
Stage 1 — Recall (three parallel searches):
- Semantic (dense): search over summaries + facts, top-15, score ≥ 0.6 →
covered_range - Keyword (BM25 sparse): search over utterances, top-10 → window
[i-2, i+2]around each hit - Pin (user_facts): dense search over
type=user_fact, top-10 — no score threshold; documents with matches are always included
Stage 2 — Range merge: merge overlapping ranges within the same document. [121–125] + [123–127] → [121–127].
Stage 3 — Fetch: retrieve utterances by merged ranges — no second search query.
Stage 4 — Dedup + sort: deduplicate by (doc_id, order_index), sort by order_index ASC.
Stage 5 — Segment build: group into dialogue segments per doc_id.
Stage 6 — Context output: build MCP tool result with segments and metadata.
During indexing, each document is processed through anchor-based windowing:
- Anchor = every Nth utterance (e.g. every 3rd)
- Window =
[i-2, i-1, i, i+1, i+2]around the anchor - Overlap between adjacent windows happens automatically
For each window — one Gemini request returning:
- Summary — brief description of the topic, decisions, and next steps
- Facts — list of structured facts (prices, objections, agreements)
Generation parameters: temperature=0, top_p=1. Prompts are versioned (prompt_version). Idempotency: summary_key = doc_id + center_index + version — if it already exists, it's skipped.
Utterances are always saved. A failure in summary/facts generation does not block indexing.
Sync Checker (Cloud Function, runs on schedule):
- Fetches changes via Drive Changes API (incremental, not full scan)
- For each changed file, computes
content_hash = sha256(file_content) - If hash matches → skip; if different → enqueue reindex
- Runs recovery: resets stale
syncinganddownloadingrecords
Vector Sync Service (Cloud Run):
- Receives a task from Cloud Tasks:
POST /sync/doc/{doc_id} - Atomically acquires the document via Firestore transaction (
imported → syncing) - Reads the Google Doc via Docs API, parses utterances and metadata
- Generates summaries, facts, BM25 sparse vectors
- Append new → delete old: uploads new chunks with new
versionfirst, then deletes old ones bydoc_id + old_version - Updates status:
syncing → synced
Status machine: queued → downloading → imported → syncing → synced / error
Claude → initialize
Claude → tools/list
Claude → tools/call(query, ...)
↓
MCP Server → retrieval pipeline
↓
MCP Server → returns segments as tool result
↓
Claude → generates response based on context
The MCP server returns only context. The final response is generated by Claude.
Authentication: Google OAuth 2.0 Authorization Code Flow. Claude detects OAuth via discovery endpoints automatically.
Qdrant — 4 object types:
| Type | Vectors | Purpose |
|---|---|---|
summary |
dense (768) | Semantic search, contains covered_range |
facts |
dense (768) | Semantic search over specific facts |
utterance |
BM25 sparse | Keyword search + range retrieval |
user_fact |
dense (768) | Manual facts, protected from deletion on reindex |
Firestore — 3 collections:
transcript_index/{doc_id}— sync status, hashes, metadatafolders/{folder_id}— folder registry (id, name, parent_id)config/vector_sync— service keys (Drive page token)
| Tool | Description |
|---|---|
query |
Hybrid search (semantic + BM25) over transcripts. Filters: folder_path, date_from, date_to. Searches recursively through nested folders. Documents with user_facts are always included. |
get_transcript |
Full transcript without semantic search. By doc_id or folder_path + date range. |
list_folders |
List the folder hierarchy. Pass folder_path to list subfolders of a specific folder. |
list_recent_transcripts |
Flat list of transcripts sorted by date desc. Filters: folder_path (recursive), date_from, date_to. Supports pagination. |
add_fact |
Manually attach a fact to a transcript. Stored as user_fact, idempotent. |
create_folder |
Create a folder: Drive subfolder + Firestore record. Pass folder_path to nest under an existing folder. |
rename_folder |
Rename a folder: updates Drive, Firestore. Pass folder_path to identify the folder. |
move_transcript |
Move a transcript to another folder. Updates Drive, resets for reindexing. |
import_all_transcripts |
Import all missed transcripts from TL;DV. Supports limit and since. |
sync_changes |
Trigger an immediate sync run without waiting for the schedule. Accepts optional folder_path or doc_id for targeted resync. |
sync_status |
Sync status counts by stage + number of unassigned transcripts. |
rename_transcript |
Rename a transcript: updates file name in Drive and title in the transcript list. |
tlbrain/
├── core/ # Shared code for both services
│ ├── config.py
│ ├── gemini/ # Embeddings + LLM
│ ├── google_drive/ # Drive client + Docs reader
│ ├── parsing/ # Parser + windowing
│ ├── qdrant/ # Client + schema + writer
│ ├── retrieval/ # Pipeline + search + segments
│ └── utils/
├── services/
│ ├── mcp/ # MCP server
│ ├── vector_sync/ # Sync service
│ ├── sync_checker/ # Cloud Function checker
│ └── connectors/
│ └── tldv/
│ ├── import_service/
│ ├── reconciliation/
│ └── webhook/
├── infra/
│ ├── docker/
│ └── deploy/
├── migrations/
│ └── v1_to_v2/ # One-time migration from v1 to v2
└── .env.example
DATE: YYYY-MM-DD
TIME: HH:MM
PROVIDER: tldv | fireflies | manual
---
Speaker Name :: Utterance text.
Speaker Name :: Utterance text.
MIT