ruvector — Python SDK, CLI, and MCP server
Ultra-low-latency vector similarity search, backed by a Rust RaBitQ 1-bit
quantization core (ruvector_rabitq::RabitqPlusIndex) with native NumPy
interop — zero-copy reads, GIL released on every heavy call. Ships three
things from one pip install ruvector:
ruvector— the Python library (Collection,RabitqIndex).ruvectorconsole script — a CLI for scripting/ops (create/insert-batch/search/delete/export/import/info/benchmark/serve), installed with the base package.ruvector serve— an MCP server (stdio or streamable-HTTP) exposing the same surface as tools, plus a ChatGPT Apps SDKui://widget for visual search exploration, install with themcpextra.
This crate is the Python half of the ruvector workspace; the underlying
algorithm lives in crates/ruvector-rabitq/ and is unchanged by this
binding. Scope, benchmarks, and the milestone roadmap (M1 here → M2 ruLake/
HNSW → M3 embeddings → M4 A2A client) are in
docs/adr/ADR-352-ruvector-python-sdk-cli-mcp.md
and docs/sdk/.
Install
pip install ruvector # library + the `ruvector` console script (numpy, click, rich)
pip install ruvector[mcp] # + `ruvector serve` (MCP server)
pip install ruvector[all] # everything
For local development from a checkout:
cd crates/ruvector-py
maturin develop --release # builds the Rust cdylib in-place, links as ruvector._native
pip install -e '.[all]' # pulls click/rich/mcp for the cli/mcp extras
pytest tests/
The --release flag matters: a debug build is dramatically slower on the
search loop and will fail the latency acceptance tests.
30-second example (library)
import numpy as np
from ruvector import Collection
vectors = np.random.default_rng(42).standard_normal((100_000, 128)).astype(np.float32)
coll = Collection.from_vectors(vectors, rerank_factor=20)
hits = coll.search(vectors[0], k=10)
for h in hits:
print(h.id, h.score) # ascending squared-L2 distance
coll.save("my.rbpx") # writes my.rbpx + my.rbpx.meta.json
coll2 = Collection.load("my.rbpx")
Collection adds ids, per-vector metadata, soft delete, and filtering on
top of the lower-level RabitqIndex (which is still available directly
for the ~50 lines/row case where you don't need any of that):
coll = Collection.create(dim=128)
coll.insert(vectors[0], metadata={"category": "news"})
coll.insert_batch(vectors[1:100], metadatas=[{"category": "sports"}] * 99)
hits = coll.search(vectors[0], k=5, filter={"category": "news"}) # client-side filter
coll.delete(hits[0].id) # soft delete (tombstone)
coll.vacuum() # physically rebuild without tombstoned rows
Filtering is applied client-side (the Rust scan has no filter pushdown in
M1.5) — documented in Collection.search's docstring, not hidden.
CLI
ruvector create --path my.rbpx --dim 128
ruvector insert-batch --path my.rbpx --vectors vecs.npy --metadata meta.json
ruvector search --path my.rbpx --query q.npy -k 10 --json
ruvector delete --path my.rbpx --id 42 --vacuum
ruvector export --path my.rbpx --out snapshot
ruvector import --path new.rbpx --vectors vecs.npy
ruvector info --path my.rbpx
ruvector benchmark -n 100000 --dim 128 # in-process latency/QPS, no external comparator
ruvector serve # MCP server over stdio
ruvector serve --http --port 8420 # MCP server over streamable-HTTP (loopback)
ruvector serve --read-only # no create/insert/delete tools
serve --http on a non-loopback --host refuses to start unless
RUVECTOR_MCP_TOKEN is set to a non-empty secret (bearer auth); an empty or
whitespace-only value counts as unset.
ruvector --help starts in ~20ms — every subcommand lazily imports numpy/
click/rich inside its own function body (see ruvector/cli.py's module
docstring), so nothing pays for them until it actually runs.
MCP server
ruvector serve exposes: vector_create_collection, vector_insert,
vector_insert_batch, vector_search, vector_delete, vector_stats,
vector_list_collections, and vector_explore — a widget-backed tool that
renders results in an HTML table (ui://ruvector/explore.html), following
the ChatGPT Apps SDK convention (_meta["openai/outputTemplate"]).
Tool arguments name a collection by name, never by filesystem path —
names are restricted to [A-Za-z0-9_-] and resolved under
RUVECTOR_MCP_DATA_DIR (default ~/.ruvector/collections), so there is no
directory-traversal surface regardless of what a remote MCP client sends.
See ruvector/mcp_server.py's module docstring for the full security
model.
# point an MCP client at: ruvector serve (stdio)
# or: ruvector serve --http --port 8420
API summary (library)
| Call | Returns | Notes |
|---|---|---|
Collection.create(dim, *, rerank_factor=20, seed=42) |
Collection |
empty; lazily builds on first insert |
Collection.from_vectors(vectors, *, ids=None, metadatas=None, ...) |
Collection |
bulk build, parallel rotate+pack |
coll.insert(vector, *, metadata=None) / coll.insert_batch(vectors, *, metadatas=None) |
int / list[int] |
true incremental add, no rebuild |
coll.search(query, k, *, filter=None, rerank_factor=None) |
list[SearchHit] |
filter: exact-match dict or predicate callable |
coll.delete(id) / coll.vacuum() |
None / int |
soft delete / physical rebuild |
coll.save(path) / Collection.load(path) |
— | .rbpx + .rbpx.meta.json sidecar |
coll.stats() |
CollectionStats |
count, dim, rerank_factor, memory_bytes, tombstoned |
RabitqIndex.build(vectors, *, ids=None, rerank_factor=20, seed=42) |
RabitqIndex |
lower-level index, no metadata/filter/delete |
idx.search(query, k, *, rerank_factor=None) |
list[(int, float)] |
(id, score²) ascending |
idx.save(path) / RabitqIndex.load(path) |
— | .rbpx v1 format |
ruvector.RuVectorError / ruvector.CollectionError |
exceptions | base / Collection-level |
Non-contiguous or wrong-dtype inputs raise TypeError at the boundary
rather than silently copying — predictable beats fast. Ids must fit in
u32 (the index's storage width); a larger id raises CollectionError
(library) or ValueError (RabitqIndex directly) rather than silently
truncating.
Benchmark
Real numbers (this host, random-Gaussian data — adversarial for ANN in
general, see the caveat in ADR-352) are in
docs/adr/ADR-352-ruvector-python-sdk-cli-mcp.md.
ruvector benchmark runs an in-process build+search latency check with no
external comparator.
Links
- ADR-352 — scope, benchmark, security, publishing status
- SDK plan and milestones — binding strategy + M1-M4 roadmap
ruvector-rabitq— the Rust crate this wraps
License
MIT, matching the rest of the ruvector workspace (see LICENSE).
Metadata
Release files for ruvector 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ruvector-0.1.0.tar.gz | 1.3 MB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| ruvector-0.1.0-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| ruvector-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| ruvector-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| ruvector-0.1.0-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| ruvector-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 13.4 MB
Release files / ruvector-0.1.0.tar.gz
| Download URL | ruvector-0.1.0.tar.gz |
|---|---|
| Size | 1.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
795c46a2b141e238d787dc0c0598d868f13ecdfde8c591eed9f10ab848167064
|
|
BLAKE2b-256 checksum How to use checksums |
41743933ba266549326b35bf46730c51a6a2ff6d592bfa77f9c8c6cd27cb6287
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ruvector-0.1.0-cp39-abi3-win_amd64.whl
| Download URL | ruvector-0.1.0-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
9b5a760ccdc762f1c85792be4f62a30c6160ef6df652b064e0f9f030a3064513
|
|
BLAKE2b-256 checksum How to use checksums |
14f69c21fbdb7a45c3c77789f6b673585c65ec1eb7b8f46bf51f63cb5337cdd0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ruvector-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl
| Download URL | ruvector-0.1.0-cp39-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
296e7418c02c9b4ef41edbbeb7aad0b38fb1df1dacc0b4a181b222db8ce4672a
|
|
BLAKE2b-256 checksum How to use checksums |
09ea2e6b86e025ecf5359a80d3ef142f105c16fd43a0834e1e84d56de272ab5c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ruvector-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl
| Download URL | ruvector-0.1.0-cp39-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 2.3 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
f8a4cf4189abd0d22b587184d50275c0f1ffdf65b8419bc8a5d57f133ebc6e52
|
|
BLAKE2b-256 checksum How to use checksums |
340fe988a6bb2a60f0d44388d6185ceabaa8006427df696bbc656c34d1690492
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ruvector-0.1.0-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | ruvector-0.1.0-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
b226b9f1a0a756c679524038fb1ededd978e9158211d64565c195aeee3fa6aac
|
|
BLAKE2b-256 checksum How to use checksums |
f1f5eb2e1dbfbb98ad10c0db25612a941b1c2bd61f905df37f053a4c8b418677
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ruvector-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | ruvector-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.5 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
63dd71d939cc04a9e559ff3fae349d1c427afcea1b7e7da06b18eab62fc2df98
|
|
BLAKE2b-256 checksum How to use checksums |
1f96494f050359da66b804def56180d46ec2bb1dd58715aba6bcfd8d8ae8dda5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|