google-activity-assistant
# Google Activity Assistant
**Local-first** tools over **your** [Google Takeout](https://takeout.google.com/) activity — keyword search in SQLite, **MCP** tools for any compatible client, and an optional local web dashboard.
> Not a live Google API. You export Takeout once (or periodically), ingest locally, then query with Claude Desktop, Cursor, Grok, or `gaa search`.
[](LICENSE)
[](https://www.python.org/downloads/)
| Doc | Use when |
|-----|----------|
| **[docs/HOW_TO.md](docs/HOW_TO.md)** | Full walkthrough: what it can/can’t do, Takeout, CLI, MCP, FAQ |
| [docs/AUTOMATION.md](docs/AUTOMATION.md) | Drive + Data Portability OAuth loop |
| [docs/MCP_AND_TAKEOUT.md](docs/MCP_AND_TAKEOUT.md) | Short MCP + export reference |
| [SECURITY.md](SECURITY.md) | Privacy / what not to commit |
| [CONTRIBUTING.md](CONTRIBUTING.md) | Dev setup & PRs |
---
## Why
Mainstream chatbots don’t have your Search/YouTube history. This project turns a Takeout export into:
1. **Structured + full-text search** (SQLite + FTS5)
2. **MCP tools** so agents can answer with **citations from your real activity**
3. Optional **local dashboard** (timeline / journeys)
Privacy-first: processing stays on disk under `data/` (gitignored).
---
## Similar work & positioning
This is **not** “the only personal-data project” and **not** “the only MCP.” Adjacent work exists; the packaging here is specific.
| Category | Examples / reality | Overlap with this repo |
|----------|--------------------|------------------------|
| Google Takeout itself | Official [takeout.google.com](https://takeout.google.com/) | Data source only |
| Offline Takeout parsers / privacy research | Scripts, blogs, personal-data tooling | Ingest inspiration |
| Personal RAG (“chat with my notes/PDFs”) | Common portfolio pattern | Same *idea* (local memory + LLM), different corpus |
| MCP for **browser** history | e.g. local browser-history MCP servers | Same *shape* (history → tools), not Google Takeout |
| Google **Workspace / product** MCPs | Gmail, Drive, Calendar, Looker, SecOps, etc. | Live product APIs — **not** My Activity dumps |
| Live “My Activity API” for third parties | **Does not exist** as a simple public stream | Why we use Takeout + local DB |
**What this project emphasizes**
- Takeout-first **Search / YouTube history** (JSON **and** HTML history files)
- **SQLite + FTS5** as system of record (portable, offline)
- **MCP tools** so any compatible host can query *your* export with citations
- Explicit honesty: **no live My Activity sync**, multi-GB media exports out of scope for v0.1
**How to describe it (interviews / README pitch)**
> Prefer: *“Most Google MCPs hit Drive/Gmail; this wires **Takeout My Activity / YouTube history** into a local index and MCP tools.”*
> Avoid: *“Nobody has ever built personal history + AI.”*
---
## Features (v0.1)
| Feature | Status |
|---------|--------|
| Ingest Takeout **JSON** (My Activity / YT history) | ✅ |
| Ingest Takeout **HTML** watch/search history | ✅ |
| Selective zip extract (skips huge media) | ✅ |
| SQLite + FTS5 keyword search | ✅ |
| **Hybrid search** (FTS + embeddings, RRF) | ✅ v0.2 |
| Embeddings: hashing default; **FastEmbed** optional | ✅ |
| CLI: `ingest`, `status`, `search`, `reindex` | ✅ |
| MCP: search (modes), stats, `import_takeout`, `reindex_embeddings` | ✅ |
| **Insights** (top channels/searches, by hour/weekday/month) | ✅ v0.3 |
| **Period summary**, **on this day**, **journeys**, **similar**, **random** | ✅ v0.3 |
| Local CSV/JSON export | ✅ v0.3 |
| Local FastAPI + React dashboard (`serve-ui`) | ✅ |
| Synthetic fixtures for demos/tests | ✅ |
| **Drive auto-import** of Takeout zips | ✅ v0.4 |
| **Data Portability API** export → download → ingest | ✅ v0.4 |
---
## Quick start
### Requirements
- Python **3.12+**
- [uv](https://docs.astral.sh/uv/) (recommended)
```bash
git clone https://github.com/Daemon-Killer/google-activity-assistant.git
cd google-activity-assistant
uv sync
```
### Sample data (no Google account needed)
```bash
uv run gaa ingest-fixtures
uv run gaa reindex # embeddings for hybrid/semantic search
uv run gaa status
uv run gaa search "React Server" # hybrid by default
uv run gaa search "React Server" --mode keyword
uv run gaa insights
uv run gaa on-this-day
uv run gaa journeys
uv run gaa random
uv run gaa export ./data/export.csv
```
Optional better semantic model (ONNX, no PyTorch):
```bash
uv sync --extra semantic
uv run gaa reindex # rebuilds missing rows with FastEmbed when available
```
### Your Takeout
1. Open [takeout.google.com](https://takeout.google.com/)
2. Prefer **My Activity** (Search + YouTube) and/or **YouTube → History**, format **JSON** if available (HTML also works)
3. Avoid exporting full Photos/video libraries if you only want activity search
4. Ingest:
```bash
uv run gaa ingest /path/to/takeout.zip
# or extracted folder
uv run gaa ingest /path/to/Takeout
uv run gaa status
```
Real exports and `data/activity.db` are **gitignored**. Never commit them.
### Automate re-import (Drive / Portability)
Manual zip ingest is the fallback. For a real loop, use OAuth once, then:
```bash
# docs/AUTOMATION.md — Cloud OAuth client + .env
uv run gaa google login --preset all
uv run gaa google refresh # new Takeout files on Drive → ingest
uv run gaa google export-start -r youtube # official Portability job
uv run gaa google export-status JOB_ID
uv run gaa google export-download JOB_ID
```
Full setup: **[docs/AUTOMATION.md](docs/AUTOMATION.md)**
---
## MCP (Claude / Cursor / Grok / …)
Start the stdio server:
```bash
uv run gaa serve-mcp
```
### Grok Build (`~/.grok/config.toml`)
```toml
[mcp_servers.google_activity]
command = "uv"
args = [
"run",
"--directory",
"/absolute/path/to/google-activity-assistant",
"gaa",
"serve-mcp",
]
enabled = true
startup_timeout_sec = 60
```
### Claude Desktop
`claude_desktop_config.json`:
```json
{
"mcpServers": {
"google-activity": {
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/google-activity-assistant",
"gaa",
"serve-mcp"
]
}
}
}
```
### Tools
| Tool | Purpose |
|------|---------|
| `search_activity` | Search with `mode`: `keyword` \| `semantic` \| `hybrid` (default) |
| `get_activity_stats` | Counts / date range / embedding coverage |
| `get_db_info` | Local paths only |
| `import_takeout` | Ingest a **local** Takeout zip/folder path |
| `reindex_embeddings` | Build vectors for hybrid/semantic search |
| `get_insights` | Top channels/searches, hour/weekday heat |
| `summarize_period` | Counts + samples for a date range |
| `on_this_day` | Same calendar day across years |
| `find_journeys` | Search→watch pairs (local heuristic) |
| `similar_activity` | Embedding neighbors for a hit id |
| `random_memories` | Random rows |
| `export_activity` | Write CSV/JSON to a local path |
| `google_login` / `google_auth_status` | Desktop OAuth |
| `list_drive_takeouts` / `refresh_from_drive_takeouts` | Auto-ingest new Drive Takeouts |
| `start_portability_export` / `get_portability_status` / `download_portability_job` | Official export API |
After ingest + reindex, ask: *“Search my YouTube for romantic Bollywood songs and cite titles + dates.”*
Or: *“Refresh from Drive, then get_activity_stats.”*
**Full walkthrough (export, FAQ, demo script):** [docs/HOW_TO.md](docs/HOW_TO.md) · **MCP config short ref:** [docs/MCP_AND_TAKEOUT.md](docs/MCP_AND_TAKEOUT.md)
---
## Local dashboard
```bash
# If web/dist is missing:
cd web && npm install && npm run build && cd ..
uv run gaa serve-ui
# → http://127.0.0.1:8788
```
API routes under `/api/*` (stats, activity list, journeys). Binds to localhost by default.
---
## Project layout
```
google-activity-assistant/
├── src/google_activity_assistant/
│ ├── cli.py # gaa commands
│ ├── mcp_server.py # FastMCP tools
│ ├── parse_takeout.py # JSON + HTML parsers
│ ├── ingest.py
│ ├── db.py # SQLite + FTS5
│ ├── search.py
│ ├── journeys.py # search→watch proximity
│ └── api.py # FastAPI dashboard API
├── web/ # React + Vite dashboard
├── fixtures/ # synthetic Takeout samples
├── docs/
│ ├── HOW_TO.md # full user guide
│ └── MCP_AND_TAKEOUT.md
├── tests/
└── data/ # gitignored — your DB + exports
```
---
## Development
```bash
uv sync --extra dev
uv run pytest
```
See [CONTRIBUTING.md](CONTRIBUTING.md).
---
## Privacy & license
- [SECURITY.md](SECURITY.md) — local-first, what not to commit
- [LICENSE](LICENSE) — **MIT**
Takeout is Google’s official data export. This project is unaffiliated with Google.
---
## Roadmap
1. Better multi-product My Activity coverage (Chrome, Maps activity)
2. Background poller for Portability jobs (still ~daily, not live)
3. Packaging / one-command installers
PRs welcome.
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: search_activity performs full-text search, get_activity_stats provides aggregate counts and date range, and get_db_info returns local database metadata. There is no overlap or ambiguity between them.
All tool names follow a consistent verb_noun snake_case pattern: search_activity, get_activity_stats, get_db_info. The naming is predictable and uniform.
With 3 tools, the set is at the lower end of the typical range but each tool serves a necessary role for the server's purpose: searching, understanding the dataset, and checking database state. It feels slightly thin but not unreasonable.
The domain is searching Google activity data. The set covers the core need (search), provides statistical overview (stats), and exposes database status (db_info). There are minor gaps such as no explicit ability to list all products or fetch a single record by ID, but these are workable via the existing tools.