Gemini embedder for mcp-fts5-starter — drop-in hybrid BM25 + dense retrieval via Google's Gemini Embeddings API.
Project description
mcp-fts5-starter-gemini
Gemini embedder for
mcp-fts5-starter— drop-in hybrid BM25 + dense retrieval via Google's Gemini Embeddings API.
What this is
The parent project ships with an Embedder Protocol and stops short of any actual provider — by design. This repo is the canonical example of that Protocol implemented against a real backend, so you can graduate from BM25-only to hybrid retrieval (BM25 + dense vectors fused with RRF) by changing one line:
from mcp_fts5_starter.search import SearchDB
from mcp_fts5_starter_gemini import GeminiEmbedder
db = SearchDB("data/index.db", embedder=GeminiEmbedder()) # GOOGLE_API_KEY in env
Use this as a working reference. To wire OpenAI, Ollama, sentence-transformers, or anything else, copy the ~150 lines in src/mcp_fts5_starter_gemini/embedder.py and swap the API call.
Quick demo (no API key needed)
The repo ships with five sample notes and a JSON cache of their pre-computed embeddings. The demo runs the same four paraphrased queries against two indexes — BM25 only and BM25 + Gemini — to show what hybrid retrieval actually buys you:
git clone https://github.com/zx22413/mcp-fts5-starter-gemini
cd mcp-fts5-starter-gemini
uv sync
uv run python scripts/demo.py
Real output:
Query: 'speeding up code with memoization'
BM25 only :
(no hits)
BM25 + Gemini :
[ +0.016] Caching strategies (notes/caching.md)
Query: 'managing many simultaneous database connections'
BM25 only :
(no hits)
BM25 + Gemini :
[ +0.016] Database connection pooling (notes/connection-pooling.md)
Query: 'throttling clients that send too many requests'
BM25 only :
(no hits)
BM25 + Gemini :
[ +0.016] API rate limits (notes/rate-limits.md)
...
Query: 'deferring slow work outside the request'
BM25 only :
(no hits)
BM25 + Gemini :
[ +0.016] Background job queues (notes/job-queues.md)
...
The queries are intentionally written without any of the keywords the documents actually use. BM25 has zero recall on all four. Gemini bridges the semantic gap and the right document surfaces every time.
Real usage
import os
from mcp_fts5_starter.search import SearchDB
from mcp_fts5_starter.ingest import sync
from mcp_fts5_starter_gemini import GeminiEmbedder
embedder = GeminiEmbedder(api_key=os.environ["GOOGLE_API_KEY"])
db = SearchDB("data/index.db", embedder=embedder)
sync(db, "data/my-notes") # embeds each doc once during ingest
results = db.search("how do I cache?") # query-time embedding + RRF fusion
The Embedder Protocol is just embed(texts: list[str]) -> list[list[float]] — GeminiEmbedder adds:
- API-key resolution (
GOOGLE_API_KEY→GEMINI_API_KEY→ constructor arg) - Batching to Gemini's 100-input cap
- Exponential-backoff retries on
429 Too Many Requests - A defensive raise when Gemini's response is missing the expected shape
Configuration
| Env var | Purpose |
|---|---|
GOOGLE_API_KEY |
Primary Gemini API key |
GEMINI_API_KEY |
Fallback — checked if the above is unset |
| Constructor arg | Default | Notes |
|---|---|---|
model |
"gemini-embedding-001" |
Pin to a specific model so dimensions are stable |
output_dim |
1536 |
Supported: 128 / 256 / 512 / 768 / 1536 / 3072 |
task_type |
None (symmetric SEMANTIC_SIMILARITY) |
Set "RETRIEVAL_DOCUMENT" / "RETRIEVAL_QUERY" for asymmetric retrieval |
batch_size |
100 |
Gemini's per-call cap |
Task type and the symmetry trade-off
The parent's Embedder Protocol takes a flat list[str] — there's no in-band signal for "is this a document or a query." GeminiEmbedder defaults to SEMANTIC_SIMILARITY, which is symmetric — cosine similarity stays meaningful in both directions.
Gemini's docs claim a small recall improvement from using RETRIEVAL_DOCUMENT for ingest and RETRIEVAL_QUERY for search. To get that, instantiate two embedders and wire them at different points in your stack. For most personal-corpus workloads, the symmetric default is good enough.
Refreshing the embedding cache
The committed data/sample/embeddings.json was generated by:
GEMINI_API_KEY=... uv run python scripts/precompute-embeddings.py
Re-run that script if you change the sample corpus, the demo queries, or the embedding model/dimension. The keys in the JSON are SHA-256 hashes of normalized text — ChangeLog records every refresh.
Status
🚧 Alpha — pre-PyPI. Install from source: pip install git+https://github.com/zx22413/mcp-fts5-starter-gemini. v0.1.0 ships to PyPI once the Trusted Publisher is configured.
Companion repos
| Repo | Role |
|---|---|
mcp-fts5-starter |
The MCP server template + FTS5 index this plugs into |
forget-rag |
Memory-decay layer for FTS5 — different problem, same backend |
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_fts5_starter_gemini-0.1.0.tar.gz.
File metadata
- Download URL: mcp_fts5_starter_gemini-0.1.0.tar.gz
- Upload date:
- Size: 148.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ec7f1d23b7461522c182f19c45137c64f31e40fe0d66e5e228ce3e0167e50f7
|
|
| MD5 |
2c6fa8ba143055235cc489d3b150bb3f
|
|
| BLAKE2b-256 |
3f29dbdb21e62c8f474bc03d8ec04ae78d02f4a615903c0f08f575210ddc2686
|
Provenance
The following attestation bundles were made for mcp_fts5_starter_gemini-0.1.0.tar.gz:
Publisher:
publish.yml on zx22413/mcp-fts5-starter-gemini
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_fts5_starter_gemini-0.1.0.tar.gz -
Subject digest:
1ec7f1d23b7461522c182f19c45137c64f31e40fe0d66e5e228ce3e0167e50f7 - Sigstore transparency entry: 1462595926
- Sigstore integration time:
-
Permalink:
zx22413/mcp-fts5-starter-gemini@c002d2a18a3285eb2f86e2f7dac09ff9fd8b87fe -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zx22413
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c002d2a18a3285eb2f86e2f7dac09ff9fd8b87fe -
Trigger Event:
release
-
Statement type:
File details
Details for the file mcp_fts5_starter_gemini-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mcp_fts5_starter_gemini-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5b263ecefe520a114fb6edb920b67644e51adf3d2518d8a87c9950d5e78cf8d
|
|
| MD5 |
22d08bdefd650084c8e42f2abbe67320
|
|
| BLAKE2b-256 |
0aed557b99cce6b665e0dea0642ad0092749c82b83d8ed317e14b9adb1bd5ed1
|
Provenance
The following attestation bundles were made for mcp_fts5_starter_gemini-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on zx22413/mcp-fts5-starter-gemini
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_fts5_starter_gemini-0.1.0-py3-none-any.whl -
Subject digest:
f5b263ecefe520a114fb6edb920b67644e51adf3d2518d8a87c9950d5e78cf8d - Sigstore transparency entry: 1462596303
- Sigstore integration time:
-
Permalink:
zx22413/mcp-fts5-starter-gemini@c002d2a18a3285eb2f86e2f7dac09ff9fd8b87fe -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zx22413
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c002d2a18a3285eb2f86e2f7dac09ff9fd8b87fe -
Trigger Event:
release
-
Statement type: