Metadata-Version: 2.3
Name: youty-mcp
Version: 1.0.0
Summary: Youty MCP server — exposes the Youty vault index (sqlite-vec + FTS5) to MCP-compatible AIs.
Keywords: mcp,youty,vault,sqlite-vec,search,rag,youtube
Author: Bent Eisheuer
Author-email: Bent Eisheuer <legetdev@gmail.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: MacOS X
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Database :: Database Engines/Servers
Classifier: Topic :: Text Processing :: Indexing
Requires-Dist: mcp>=1.2.0,<2.0.0
Requires-Dist: sqlite-vec>=0.1.6,<0.2.0
Requires-Dist: httpx>=0.27.0,<1.0.0
Requires-Dist: numpy>=1.26.0,<3.0.0
Requires-Dist: transformers>=4.56,<5
Requires-Dist: torch>=2.0,<3
Requires-Dist: sentence-transformers>=5.0,<6
Requires-Dist: sentencepiece>=0.2,<1
Requires-Dist: protobuf>=4,<7
Requires-Python: >=3.11
Project-URL: Homepage, https://github.com/legetdev/youty
Project-URL: Repository, https://github.com/legetdev/youty
Project-URL: Issues, https://github.com/legetdev/youty/issues
Project-URL: Documentation, https://github.com/legetdev/youty/blob/main/youty-mcp/README.md
Description-Content-Type: text/markdown

# youty-mcp

Local MCP server that exposes the Youty vault's vector index to any
MCP-compatible AI (Claude Desktop, Claude Code, Cursor).

## What it does

Six tools, hybrid dense + BM25 retrieval over your captured YouTube /
Instagram / TikTok videos, plus joint text → frame retrieval via
Google's SigLIP-Base-Patch16-224 (Apache-2.0). Queries land in ~300 ms
for text, ~32 ms warm for frames on Apple Silicon.

| Tool | Returns |
|---|---|
| `search(query, k=15, platform?, since_iso?)` | hybrid dense + BM25 + RRF over transcript chunks; top-k results with `frame` paths + `video_md_path` |
| `search_frames(query, k=10, platform?)` | SigLIP-Base joint text→image; top-k frame matches with parent video metadata |
| `get_transcript(video_id)` | full `video.md` + parsed frontmatter |
| `get_video(video_id)` | frontmatter + folder listing + frame paths |
| `list_videos(platform?, channel?, limit=100)` | newest-first listing |
| `find_similar(video_id, k=10)` | nearest videos by averaged body-chunk vectors |

## Install

```bash
cd youty-mcp
uv sync                       # creates .venv, installs deps
```

Dependencies: `mcp`, `sqlite-vec`, `httpx`, `numpy`, `transformers`,
`sentence-transformers`, `torch`, `sentencepiece`, `protobuf`. Python ≥ 3.11.
Text queries are embedded on-device with EmbeddingGemma, matching how the
index was built; frame queries use SigLIP via `transformers`. Frame *image*
embeddings come from the Mac app's bundled CoreML encoder, so this server
never needs `coremltools` itself.

## Text search: 100% on-device — no key, zero config

The server embeds each query on-device with **the same model the index was
built with**, read from `index_meta.current_text_model`, so query and document
vectors share one space. That model is Google's EmbeddingGemma, run locally via
`sentence-transformers` — no key, no provider option, no cloud call of any kind.
The weights download from HuggingFace on the first text `search` (one-time per
machine, ~1.2 GB, cached in `~/.cache/huggingface/`); every query after that is
fully offline.

## SigLIP weights (auto-downloaded for frame-text queries)

The **frame side** (image embedding) is handled by the Mac app and CLI
via a CoreML `.mlpackage` of SigLIP-Base bundled inside the app at
`Youty.app/Contents/Resources/SigLIP-Base-224_image.mlmodelc`. No
download required — it ships with the binary.

The **text side** (this server's query embedding) downloads
`google/siglip-base-patch16-224` (~370 MB) from HuggingFace on the
first `search_frames` call. Cached in the standard HuggingFace cache:

```
~/.cache/huggingface/hub/models--google--siglip-base-patch16-224/
```

One-time per machine. Hot-path embed is ~32 ms on Apple Silicon.

## Claude Desktop wiring

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```jsonc
{
  "mcpServers": {
    "youty": {
      "command": "uvx",
      "args": ["youty-mcp"]
    }
  }
}
```

Restart Claude Desktop. Then ask: _"What are best practices on creating
AI influencers, and what tools should I use? Use my Youty vault."_

## Claude Code wiring

```bash
claude mcp add youty -- uvx youty-mcp
```

## Tests

```bash
uv run pytest -q
uv run python tests/smoke_live.py    # one-shot live on-device search smoke
```

## Index location

Default: the Mac app's sandboxed index at
`~/Library/Containers/dev.leget.youty/Data/Library/Application Support/Youty/index.db`,
falling back to `~/Library/Application Support/Youty/index.db` if that isn't
present. Override either with `YOUTY_INDEX_DB=/abs/path`.

The Mac app writes here when it saves a video (background, non-blocking).
The MCP server reads here and promotes data to `sqlite-vec` and FTS5
virtual tables at startup.

The index is **rebuildable** from the vault's `video.md` files alone —
losing it is recoverable, never catastrophic. Use the Mac app's Settings
window → "Re-index entire vault", or run headless:

```bash
"/path/to/youty.app/Contents/MacOS/youty" --reindex "/path/to/vault"
"/path/to/youty.app/Contents/MacOS/youty" --index-frames "/path/to/vault"
```

## Troubleshooting

- **`search` returns 0 results** — the index is empty. Save a video from
  the Mac app (indexer enabled in Settings) or run `--reindex` on an
  existing vault. No key needed — text indexing is on-device by default.
- **`search_frames` is slow** on the first call — the SigLIP text
  encoder downloads ~370 MB of weights from HuggingFace into
  `~/.cache/huggingface/` (one-time per machine). On the Mac-app side
  the bundled SigLIP CoreML image encoder compiles to Neural Engine on
  first use (~1 s). Subsequent queries are ~32 ms.
- **Legacy bundles with 4-digit-second JPEG names** (`0717.jpg`) are
  silently skipped by frame indexing. The current contract is 8-digit
  milliseconds (`00718000.jpg`). Re-saving the video regenerates frames
  in the new format.
- **Vault location unknown** error from `get_transcript` — the indexer
  records the vault path; if you've changed it, run `--reindex` once
  against the new path so `index_meta.vault_root` updates.
