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.1
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.1.tar.gz | 1.3 MB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| ruvector-0.1.1-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| ruvector-0.1.1-cp39-abi3-manylinux_2_28_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| ruvector-0.1.1-cp39-abi3-manylinux_2_28_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| ruvector-0.1.1-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| ruvector-0.1.1-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.1.tar.gz
| Download URL | ruvector-0.1.1.tar.gz |
|---|---|
| Size | 1.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
12e34b8486ac4e12037321b7c23dbe41b161a211019853018cb7376d6da61afb
|
|
BLAKE2b-256 checksum How to use checksums |
d1e7521a64ab6f207fb04817dd1d127d5c9d8a2538d9f11bb50a9be603592bef
|
| 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.1-cp39-abi3-win_amd64.whl
| Download URL | ruvector-0.1.1-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
25eb55d61cada68c95c690b160924d1c863cd6900cc82bc3ccc487404b0daaa4
|
|
BLAKE2b-256 checksum How to use checksums |
9c13af8f96b73306fde0d9d3f3bc8caa7e0f3c91dd711449cb94697b3fd7b0a8
|
| 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.1-cp39-abi3-manylinux_2_28_x86_64.whl
| Download URL | ruvector-0.1.1-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 |
9a1d767a8f2d19e4a536b9cf50e57f0e13831998860d34af3fd35b972766a6b1
|
|
BLAKE2b-256 checksum How to use checksums |
55530eacb752ae3874c957ee686876d7a5c6e9c90e57170e8801a532325c3344
|
| 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.1-cp39-abi3-manylinux_2_28_aarch64.whl
| Download URL | ruvector-0.1.1-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 |
47e993699ae3171dd7940f890091156a488f5803c6ee5dc3400d1bbef82a8ccb
|
|
BLAKE2b-256 checksum How to use checksums |
169215728c9b29525b28f3a6ba5e4aff46d43b31300986eed635ba448bb7bfe6
|
| 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.1-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | ruvector-0.1.1-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 |
a0a27ca94f954519377b1f2e5161015cee6480750034e5502b690ba5890c224d
|
|
BLAKE2b-256 checksum How to use checksums |
3093cf838318ac5f4e6ddcd3ac7e09c2b3d81980e695180c51deaf42468b95e1
|
| 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.1-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | ruvector-0.1.1-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 |
09b0215b0c225aa51d4ecdc6cc1d06e6acffcf447e161fa622080e79c5f88b04
|
|
BLAKE2b-256 checksum How to use checksums |
4d9fe0b2640b17b8d75cf2e2a5d238603f01c4616ce0ad15602725d418d7b9d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|