context-server
Semantic search over a folder of markdown, served as an MCP server for coding agents.
Index once into a SQLite DB (embeddings + BM25). Point Claude Code, Cursor, or any MCP client at serve, and the agent can search that corpus instead of guessing from memory.
One Rust binary. ONNX Runtime is linked in via ort / fastembed — no separate libonnxruntime to ship. SQLite is bundled.
Quick start
pip install context-server
# or: uvx context-server@latest …
context-server index --input ./docs --db context.db
context-server search --db context.db "how do we handle backports"
context-server serve --db context.db
Wheels: Linux x86_64/aarch64 (manylinux_2_39 / glibc 2.39+, e.g. Ubuntu 24.04+) and macOS Apple Silicon.
The first embedding run downloads BGE-small-en-v1.5 into
$XDG_CACHE_HOME/context-server/fastembed/ (or ~/.cache/...; once, tens of MB).
Override with FASTEMBED_CACHE_DIR or HF_HOME.
Optional: tell the agent when to use this corpus
context-server index --input ./docs --db context.db \
--instructions-file ./mcp-instructions.txt
# or: --instructions 'Use semantic_search for questions about …'
That text is stored in the DB and exposed as MCP ServerInfo.instructions when you serve.
Claude Code
claude mcp add --transport stdio --scope user context-server \
-- uvx --refresh context-server@latest \
serve --db /absolute/path/to/context.db
--refresh + @latest rechecks PyPI on each start. If Claude rarely surfaces the tools, set "alwaysLoad": true on the server entry in your Claude MCP config.
Cursor
~/.cursor/mcp.json (or project .cursor/mcp.json):
{
"mcpServers": {
"context-server": {
"command": "uvx",
"args": [
"--refresh",
"context-server@latest",
"serve",
"--db",
"/absolute/path/to/context.db"
]
}
}
}
Reload MCP after editing. Re-index when content changes, then restart the MCP session so serve reloads the DB.
What it indexes
Only .md / .markdown. Chunks on # / ## / ###, keeps the heading path on each chunk, and splits long sections with overlap.
Convert structured sources (YAML, etc.) to prose before indexing. Fenced YAML searches poorly; a short paragraph that keeps names, roles, and relationships together works much better.
Try the sample set:
cargo build --release
./target/release/context-server index --input examples/sample-docs --dry-run
./target/release/context-server index --input examples/sample-docs --db /tmp/sample.db
./target/release/context-server search --db /tmp/sample.db "password reset"
Search
Default mode is hybrid: dense cosine (BGE-small-en-v1.5) plus BM25, fused with reciprocal rank fusion. Dense catches paraphrase; BM25 catches exact tokens (usernames, acronyms, IDs).
context-server search --db context.db --mode hybrid "query" # default
context-server search --db context.db --mode dense "query"
context-server search --db context.db --mode lexical "query"
# Scope to a subtree / heading / metadata tag
context-server search --db context.db --path-prefix teams/ "who owns storage"
context-server search --db context.db --heading Backport "z-stream"
context-server get --db context.db --path teams/storage.md --chunk 0
MCP tools
| Tool | Role |
|---|---|
semantic_search |
Ranked passages + scores; optional path_prefix / heading / tag filters |
list_documents |
Indexed chunks; optional path_prefix |
get_document |
Full chunk by citation (source_path + chunk_index), or all chunks for a path |
Search hits cite chunks as source_path#chunk_index. Call get_document to pull the full text for quoting.
Remote database (GCS)
serve and search accept a gs:// URI. The object is cached under $XDG_CACHE_HOME/context-server/dbs/ (or ~/.cache/...). index still writes a local path only.
context-server serve --db 'gs://my-bucket/latest/context.db'
# Project-qualified form also works (gs:// required; stripped for the Storage API)
context-server serve --db \
'gs://projects/my-gcp-project/buckets/my-bucket/objects/latest/context.db'
Uses Application Default Credentials. If a sibling {object}.sha256 exists (sha256sum format), a matching local cache is reused; otherwise the DB is re-fetched and verified.
CLI
context-server index --input <path> [--db FILE] [--dry-run] [--batch N]
[--full] [--sync]
[--instructions TEXT | --instructions-file FILE]
context-server serve --db <local path | gs://…>
context-server search --db <local path | gs://…> [--limit N] [--mode hybrid|dense|lexical]
[--path-prefix P] [--heading H] [--tag T] <query>
context-server get --db <local path | gs://…> --path FILE [--chunk N]
context-server embed <query> # smoke-test query embedding (BGE instruction)
index is upsert-only by default. Use --sync only when the database should
exactly mirror the current input: it deletes indexed paths missing from that
input, and an empty input removes every indexed document. The former --update
behavior is now the default; replace previous prune-by-default commands with an
explicit --sync.
Build from source
cargo build --release
cargo test
Rust 1.88+, Linux x86_64 is the primary target. You need a C++ stdlib for the linker (libstdc++) and whatever OpenSSL/native-tls needs on your platform.
On Fedora/RHEL, if the linker wants -lstdc++ but only libstdc++.so.6 exists:
mkdir -p .linker && ln -sfn /usr/lib64/libstdc++.so.6 .linker/libstdc++.so
export RUSTFLAGS="-L native=$(pwd)/.linker"
Linux wheels (same image CI uses — Ubuntu 24.04 / glibc 2.39):
./scripts/build-wheel.sh
VERSION=2026.716.1 ./scripts/build-wheel.sh # optional override
Releasing
CalVer YYYY.MMDD.N (e.g. 2026.716.1) so versions work for both Cargo and PyPI. Run the Release workflow on main (Actions UI or CLI); it picks the next version, builds wheels, publishes to PyPI, then creates the matching git tag and GitHub Release (with wheels attached).
gh workflow run release.yml --repo context-server/context-server
Design notes
Under the hood: fastembed BGE-small-en-v1.5 (384-d, L2-normalized; query instruction applied at search time), rusqlite with float32 blobs, rmcp over stdio. index is incremental by file: unchanged files (same post-chunk content hash) are skipped, so the embedding model is not loaded. Indexing safely upserts by default. Pass --sync to also remove database paths missing from --input, or --full to re-embed everything collected. A model or chunker migration requires a complete-corpus --sync run.
More detail and roadmap: PLAN.md.
Supported scale
The primary target is up to 10,000 chunks; 50,000 chunks is the regularly
benchmarked upper range for the exact in-memory implementation. Re-evaluate
storage/index architecture around 100,000 chunks or 500 MiB resident memory.
Run scripts/benchmark-scale.py --source-db context.db to reproduce structural
latency and database-size measurements.
License
MIT — see LICENSE.
Metadata
Release files for context-server 2026.908.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 | Provenance | |
|---|---|---|---|---|
| context_server-2026.908.1.tar.gz | 295.1 kB | Without provenance | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| context_server-2026.908.1-py3-none-manylinux_2_39_x86_64.whl | Python 3 | none | Linux glibc 2.39+ x86-64 | Details |
| context_server-2026.908.1-py3-none-manylinux_2_39_aarch64.whl | Python 3 | none | Linux glibc 2.39+ ARM64 | Details |
| context_server-2026.908.1-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
Total release size: 47.7 MB
Release files / context_server-2026.908.1.tar.gz
| Download URL | context_server-2026.908.1.tar.gz |
|---|---|
| Size | 295.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4fa4488fa6f33a00abd71640340df049b618433ca198678a08585e0af307e005
|
|
BLAKE2b-256 checksum How to use checksums |
fd370ef20d8eeda9f3fdff3585ae11ed92d6779eb6decbe54fdf5a39353deeb3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / context_server-2026.908.1-py3-none-manylinux_2_39_x86_64.whl
| Download URL | context_server-2026.908.1-py3-none-manylinux_2_39_x86_64.whl |
|---|---|
| Size | 16.4 MB |
| Tags | Linux glibc 2.39+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
3aaa976a49c7e3228ed46c3905f7e814d73f729849bf08d7f92199360c0c4e89
|
|
BLAKE2b-256 checksum How to use checksums |
58a5a8eac5b66e356a0597e4b811b9c1979485a3ce0ab347728867cf0adc27d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / context_server-2026.908.1-py3-none-manylinux_2_39_aarch64.whl
| Download URL | context_server-2026.908.1-py3-none-manylinux_2_39_aarch64.whl |
|---|---|
| Size | 16.7 MB |
| Tags | Linux glibc 2.39+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
0f5aeb3e3c015d1c138f7606a361adcdee490724be138990921e412c9263b0b6
|
|
BLAKE2b-256 checksum How to use checksums |
7de6d5452ab7522e70e68b5ef53c455f0bfbccfe7cd8fc3665105dedc8967d2a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / context_server-2026.908.1-py3-none-macosx_11_0_arm64.whl
| Download URL | context_server-2026.908.1-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 14.2 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
df1288adba88fb8048619f035e68954267f1b23a20a69d212ed0dfefeee86cfe
|
|
BLAKE2b-256 checksum How to use checksums |
180c7cfd4dc5844efcc7f879cc54272a610e33a4e1fd1607f93306f2039d216f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for context-server 2026.908.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.