Vectr
Vectr provides semantic codebase search and persistent working memory for AI code editors, allowing you to find code by concept and retain findings across sessions — locally with no API key required.
Search & Code Navigation
vectr_search— Search your codebase using natural language or concept descriptions (e.g., "JWT validation") via hybrid semantic + BM25 search; returns full function/class bodies with file paths and line numbersvectr_locate— Find where a known symbol is defined using multiple fallback strategies (exact match → suffix → fuzzy, etc.)vectr_trace— Traverse the call graph for a symbol (callers, callees, or both) to understand dependencies before modifying codevectr_fetch— Re-fetch a previously retrieved code chunk by its exact ID without re-running a searchvectr_map— Get a structural overview of a codebase (directory tree, languages, frameworks) without reading filesvectr_map_save— Save a synthesized plain-English codebase summary as a permanent "passport" for instant future retrievalvectr_ingest_traces— Import runtime call trace events into the symbol graph to reveal dynamic dispatch patterns that static analysis misses
Persistent Working Memory
vectr_remember— Store structured notes (findings, tasks, gotchas, directives) with tags, priority, and kind; notes survive/compactand new sessionsvectr_recall— Semantically retrieve stored notes using natural language queries, with filtering by kind/tags/priority/agevectr_evict_hint— Identify code chunks safe to drop from context (each is re-fetchable verbatim) to manage context window pressurevectr_snapshot— Seal all current notes as a named checkpoint for easy resumption latervectr_snapshot_list— List all saved session snapshots for the workspacevectr_forget— Delete a single stale note by ID or clear all notes for the workspacevectr_promote— Raise an auto-captured note's trust level fromautotoagentafter confirming it still holds
Index & Health Monitoring
vectr_status— Check index health (file count, chunk count, embedding model) and note count; useful at session start to decide whether recalling prior notes is worthwhile
Integrates with GitHub Copilot in VS Code to enable semantic codebase search and persistent working memory for AI-assisted coding.
Vectr
Delivery, not storage. A local daemon that gives an AI code editor semantic search over your codebase, plus working memory that shows up on its own when it is relevant.
Version 1.12.0 · Last updated 2026-09-03 · CHANGELOG
In 30 seconds
Your AI code editor pays for the same knowledge twice. It re-reads files it read yesterday, re-greps symbols it already found, and loses the exact signature it discovered at turn 5 the moment the conversation is compacted. Vectr is one local process that removes both halves of that bill:
Retrieval that understands code. AST-aware chunks, a symbol and call graph, and hybrid semantic plus BM25 ranking. Describe a behaviour and get the whole function back, in one call instead of a grep loop and three blind file reads.
Working memory that gets delivered. Save a finding once. It comes back verbatim in under 50ms, and on editors that expose session hooks it arrives automatically at the moment it applies, without the agent choosing to ask for it.
Local, keyless, zero config. One
pip install, onevectr start. The embedding model runs on your machine. Nothing is transmitted anywhere, and there is no API key.
pip install vectr && cd /path/to/your/project && vectr startThat is the whole setup. Vectr writes the MCP config for your editor, then indexes in the background.
Related MCP server: aivectormemory
How vectr differs from other AI agent memory tools
There is a healthy category of general-purpose memory layers for AI agents, and the good ones solve a real problem well. Vectr is most often compared to them, so here is the difference stated plainly. The left column describes the design these tools tend to share, not any one product; individual tools vary, so check the one you are considering.
A typical memory layer | Vectr | |
How memory reaches the model | The agent or developer calls an explicit store-and-retrieve API, | Every note carries trigger conditions over |
Domain | Generic conversational memory, aimed broadly at assistants and applications rather than at source code. Notes are strings about a user or a session. | Code native. A symbol graph, AST chunking, semantic code search, and working memory fused in one daemon, so a note can be anchored to a real symbol or path rather than a string. |
Inference | Commonly runs note extraction through an LLM and embeds through a provider API, so an API key and a provider account are part of the setup. | Zero internal LLM calls, by design. A local embedding model, no provider account, no API key. |
Deployment | Usually a library or a self-hosted server, frequently with a managed cloud tier alongside it. | One local daemon bound to |
Why the first row is the one that matters. Storage is not the bottleneck. Retrieval that an agent must volunteer to call is, because it largely does not call it. In our controlled evaluation the agent performed 0 memory operations across 114 turns even when the store was pre-seeded with knowledge directly relevant to its task. Deterministic injection delivered in every injection-equipped run, with zero false-alarm fires across the audit-logged trigger evaluations. Under repeated compaction, ten facts held only in the conversation were absent from 106 of 108 forced compactions, while the same ten facts injected from a harness-owned store arrived intact across 138 of 138 compact-resumes.
Full method and results: Delivery, Not Storage: Cue-Anchored Working Memory as a Harness Property for Coding Agents (arXiv:2607.20972).
What it costs and what it saves
Measured, not hypothetical. Recalling 3 stored notes with vectr_recall costs 360 tokens in one tool call. Re-deriving the same three facts with grep plus Read costs about 2,060 tokens across six tool calls on the same 182-file Python repo. That is roughly 5.7x fewer tokens and 6x fewer tool calls, in under 50ms (chars/4 tokenization, full breakdown in Measured costs, honestly).
Notes are persisted to disk, not held in the conversation, so they survive /compact and a fresh session equally. The session boundary does not matter: saving at turn 5 and recalling at turn 15 is the same mechanism as recalling three days later.
Where it pays off: unfamiliar or large codebases, work you resume, and long sessions with many turns. Where it does not: a one-off grep on code you already know cold. Reach for grep instead, and see When vectr can hurt.
Benchmarks
Public results live in benchmarks/. The corpora below are witnesses chosen because they are large and unfamiliar to the model, nothing more.
A six-task sprint on a large unfamiliar C codebase simulates a week of feature work. One research session stores findings with vectr_remember. Six isolated implementation sessions each open cold and call vectr_recall.
Implementation sessions only, 6 tasks combined:
Metric | Vanilla | Vectr | Delta |
Cost | $2.50 | $1.97 | -21% |
Wall time | 17.6 min | 13.5 min | -24% |
Turns | 123 | 94 | -24% |
Read + Bash calls | 102 | 62 | -39% |
Per-task re-discovery (Read and Bash calls before the first write):
Task | Vanilla | Vectr | Delta |
| 16 | 6 | -62% |
| 13 | 3 | -77% |
| 23 | 9 | -61% |
| 6 | 6 | 0% |
| 13 | 2 | -85% |
| 21 | 16 | -24% |
The 0% row is real and kept on purpose: that task was one the model could already navigate from training knowledge alone.
Research versus implementation, stated honestly. The research phase is paid once and costs more with vectr (+94%), because storing rich code stubs and signatures produces output tokens. The implementation phases repeat every task and cost less, because recall replaces re-discovery. The overhead breaks even after roughly 8 tasks of note reuse.
Phase | Vanilla | Vectr | Why |
Research (1 session, paid once) | $1.36 | $2.63 | Storing notes costs output tokens |
Impl (6 sessions, repeating) | $2.50 | $1.97 | Notes replace re-discovery |
Total sprint | $3.86 | $4.60 | Inverts to a net gain after about 8 tasks |
An earlier run on a 5,856-file Java corpus measured -58% implementation cost, -72% implementation tool calls, and -39% wall time.
Measured costs, honestly
Per-call token cost (median, 182-file Python repo, chars/4 tokenization):
Tool | Median tokens | Range |
| ~2,320 | 1,437 to 3,091 (n=8) |
| ~192 | |
| ~720 | |
| ~180 |
The trade-off, stated plainly: for a single pointed lookup on a small, already-familiar repo, grep is cheaper. Vectr's median cost across 5 single-fact tasks was 60% more tokens, and it is slower too, since a vectr_search round trip takes 1.7 to 3.6 seconds against about 28ms for grep. Vectr does not win on per-call cost. It wins on tool-call count (one round trip instead of several), on answer completeness (a whole symbol back, not a partial file read), and on everything in working memory, where the 5.7x recall refund compounds with every task you resume.
Fine print: the automatic eviction and reminder banners riding along on tool responses cost tokens too. An always-on re-fetch footer runs about 27 tokens, a light nudge about 89 tokens, and the escalated action-required banner (which fires only after both the chunk and token thresholds are crossed without a save) scales from about 480 to 535 tokens before it plateaus.
Quick start
Local (recommended)
python3.14 -m venv ~/.vectr-env
source ~/.vectr-env/bin/activate # Windows: ~/.vectr-env/Scripts/activate
pip install vectr
cd /path/to/your/project
vectr startRequires Python 3.14+. To install:
macOS:
brew install python@3.14Ubuntu/Debian:
sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt install python3.14 python3.14-venvWindows: python.org/downloads
vectr start returns immediately. Indexing runs in the background, so run vectr status to check progress. On first run the embedding model downloads once (about 290 MB). Restart your AI code editor once to pick up the new MCP config.
Docker (CI and servers)
git clone https://github.com/swapnanil/vectr
cd vectr
docker-compose up apiExposes port 8765. Docker does not auto-write IDE config files, so use the local install for IDE integration.
Connect to your AI code editor
vectr start writes the MCP config for your editor automatically. Restart your editor once.
Editor | Config | Status |
Claude Code | Auto: | Verified |
Cursor | Auto: | Experimental |
VS Code / GitHub Copilot | Auto: | Experimental |
Windsurf | Manual, see below | Experimental |
Cline | Manual, see below | Experimental |
Continue | Manual, see below | Experimental |
Codex CLI | Auto: | Experimental |
"Verified" means the full integration (config, guidance, and hooks) has been exercised end to end. "Experimental" means the MCP config is written and works, but the integration has not been run through the same verification pass.
Automatic delivery of memory depends on the host exposing session hooks. Where it does not, every tool below still works on demand over MCP.
Claude Code, .claude/settings.json:
{ "mcpServers": { "vectr": { "type": "http", "url": "http://localhost:8765/mcp" } } }Cursor, .cursor/mcp.json:
{ "mcpServers": { "vectr": { "url": "http://localhost:8765/mcp" } } }VS Code / GitHub Copilot (1.99+), .vscode/mcp.json:
{ "servers": { "vectr": { "type": "http", "url": "http://localhost:8765/mcp" } } }Windsurf, ~/.codeium/windsurf/mcp_settings.json:
{ "mcpServers": { "vectr": { "serverUrl": "http://localhost:8765/mcp" } } }Continue.dev, .continue/config.json:
{ "mcpServers": [{ "name": "vectr", "transport": { "type": "http", "url": "http://localhost:8765/mcp" } }] }Codex CLI, project-scoped .codex/config.toml (written automatically by vectr start):
[mcp_servers.vectr]
url = "http://localhost:8765/mcp"On its first run in the workspace, Codex shows a one-time interactive prompt to trust the project before it loads a project-scoped config. If you also ran vectr init --hooks, Codex shows a second one-time prompt to trust the hook commands. Both persist after you accept them once, and a config writer cannot clear them for you without weakening your security posture.
Stdio transport
The editors above connect over HTTP (vectr start plus POST /mcp). For MCP clients and hosting platforms that spawn the server as a subprocess and speak MCP over its stdin and stdout instead of opening an HTTP connection, run:
vectr mcp-stdio [WORKSPACE]No port, no daemon: a single foreground process framed as newline-delimited JSON-RPC 2.0 (one JSON object per line on each of stdin and stdout, no Content-Length headers). initialize and tools/list answer immediately, while the embedding model load and initial indexing happen on a background thread. A tools/call made before that finishes gets a graceful "still starting up" response instead of hanging. Stdout carries protocol JSON only, and all logging goes to stderr. The process exits cleanly on stdin EOF. --memory-only and --search-only behave the same as on vectr start.
How it works
AST-aware chunking. tree-sitter parses each file and splits at function, class, and method boundaries. No chunk breaks mid-logic.
Code embeddings.
ibm-granite/granite-embedding-english-r2(local, CPU-fast, overridable) maps natural-language queries to code symbols, so "JWT validation" findsverify_jwt_token. BM25 handles exact symbol names.Hybrid search. Vector similarity and BM25 combined, weighted by codebase characteristics. Large and unfamiliar leans semantic, small and well-documented leans BM25.
Symbol graph. Call edges, import chains, and HTTP routes (Flask, FastAPI, Express, Spring) are extracted and stored.
vectr_locateresolves a name through a 6-strategy cascade: exact match, suffix, same-module, import-chain, substring, then fuzzy. A fuzzy answer is always rendered as explicitly inexact rather than presented as a confident match, because a confident wrong symbol is worse than a not-found.Working memory.
vectr_rememberstores structured notes to SQLite and ChromaDB.vectr_recallruns semantic search over notes rather than a SQL LIKE, so multi-word queries still find the relevant context.Trigger evaluation. Each note's conditions are checked deterministically by the daemon at lifecycle moments, with a per-session fire ledger and injection budgets, so a note resurfaces exactly when it applies and never twice in the same window.
MCP protocol. 23 tools served over HTTP. Any MCP-compatible AI code editor connects without plugins.
23 MCP tools
vectr start writes a guidance file into your workspace with this table, so your AI code editor knows which tool to reach for without being prompted.
Search tools, which retrieve code from the index:
Situation | Tool |
You know a concept or behaviour, not a name |
|
You know a symbol name, not its file |
|
You need callers or callees of a symbol |
|
You need an architectural overview |
|
You want to save a synthesised map summary |
|
You have runtime call data to inject |
|
You need index health or note count |
|
Memory tools, which store and recall across sessions:
Situation | Tool |
Notes exist from a prior session |
|
One note must survive every recall regardless of the query |
|
You found something worth preserving |
|
A stored note turns out to be about a file you did not declare |
|
A stored note was anchored to the wrong file, or the anchored file's relevance is gone |
|
Starting a session, want to pick up where you left off |
|
Context is filling up |
|
A chunk shown earlier has left your context |
|
End of a long session, want a checkpoint |
|
Looking for a prior checkpoint |
|
Notes are stale after a large refactor |
|
An auto-captured note has been reviewed and still holds |
|
Automatically captured failure-to-success moments are waiting |
|
A stored note turned out to be wrong |
|
A note is simply out of date, not wrong |
|
A revoked note was right after all |
|
Triggers: the delivery layer
Those kind values carry sensible defaults. A note can also declare explicit per-note triggers on vectr_remember:
Axis | Fires when |
| A glob matches the file the current lifecycle moment targets |
| The targeted file defines or references that symbol, resolved exactly against the same symbol graph |
| Prompt similarity clears a fixed per-kind threshold |
| A lifecycle moment occurs: session-start, prompt-submit, pre-edit, pre-run, pre-commit, or post-compaction |
| The normalized verb of an about-to-run shell command matches a glob |
temporal |
|
Conditions AND within one entry and OR across entries. Every fire carries a one-line explanation of which trigger matched. Injection is budgeted (a hard per-turn cap spent jointly across surfaces), deduplicated by note id across every surface within a turn, and framed by provenance, so an unreviewed auto-captured note never renders with the authority of a standing rule from you. A note is never partially truncated: it arrives whole, as an index-tier line, or not at all.
Workspace-scoped notes double as a shared bus for multi-agent workflows. An orchestrator and its subagents all read and write the same note store, so a subagent can call vectr_remember(..., agent="coder-2") with its findings before finishing, and the orchestrator recalls them instead of re-reading the subagent's full transcript. The agent parameter is never inferred. It is explicit attribution, and it shows up as a tag in vectr_recall index output.
CLI reference
vectr start # index + start daemon for current dir
vectr start /project/api # positional workspace: a directory or .code-workspace file
vectr start --path /project/api # specific workspace (repeatable, multi-root)
vectr start --memory-only # working memory + hooks only, no code index, no watcher
vectr status # index health, chunk count, notes count
vectr status --all # all running instances
vectr stop /project/api # stop one instance (same positional as start)
vectr stop --path /project/api # stop one instance (equivalent --path form)
vectr stop --all # stop all instances
vectr index --path . # re-index without restarting daemon
vectr fetch src/auth.py:10-42 # re-fetch a chunk by exact id, verbatim
vectr resume # most recent task note, latest snapshot, open gotchas
vectr init --path . # write guidance file + MCP config without starting
vectr init --exclude vendor # exclude directories from indexing
vectr forget --path . # delete all working-memory notes
vectr memory export # render the working-memory store as a greppable, git-diffable markdown file (default MEMORY.md)
vectr memory export --path notes.md # write the export to a specific file
vectr memory export --disable # stop the auto-refresh that keeps the export file in sync
vectr memory edit # open the working-memory store in your $EDITOR, one block per note
vectr memory import # bring existing on-disk memory files into the working-memory store (see below)
vectr memory import --path NOTES.md # import a specific file instead of the default discovery list
vectr memory import --dry-run # preview what would be created (kind, title, source file, line range), change nothing
vectr cache prune # remove empty per-workspace cache dirs (live instances skipped)
vectr cache prune --dry-run # preview what would be removed, delete nothing
vectr proxy # experimental: localhost API proxy (see below)
vectr mcp-stdio # foreground stdio MCP transport, no port or daemon (see above)Importing existing memory
vectr memory export and vectr memory edit are the one-way-out and check-out/check-in paths. vectr memory import is the one-way-in: it brings existing on-disk memory into the working-memory store without re-entering it by hand. This is the on-ramp for a new user who already has memory written down.
Discovery. With no --path, the importer walks a closed list of default candidate files at the workspace root: MEMORY.md, CLAUDE.md, AGENTS.md, .cursorrules, and .github/copilot-instructions.md. Pass --path (repeatable) to point at any other file or directory; a directory is walked recursively for *.md and *.mdc files. Files that don't exist are reported but never raised on, since most workspaces will not have every default file.
Granularity. A source file is not one note and not one note per line. It is split at Markdown heading boundaries (^#{1,6}\s+), the same way a human skims a MEMORY.md. A heading-less file becomes one note. Headings inside fenced code blocks and HTML comment blocks are skipped, so a code example or the export-file header comment does not become a section boundary. A file previously exported by vectr memory export is detected by its <!-- Generated by ... --> header and parsed through the same block grammar vectr memory edit uses, recovering each original block as a note with its declared kind, priority, tags, and provenance carried forward.
Dry run. --dry-run prints exactly what would be created (kind, title, source file, source-line range, provenance) and changes nothing. Memory is the one resource the user cannot easily reconstruct, so dry run is the path of first resort:
vectr memory import --dry-run
# + [finding/auto] 'Project conventions' (MEMORY.md:12-34)
# + [operational/agent] 'Test runner' (CLAUDE.md:5-9)
# DRY RUN: would create 2 note(s), skip 0 already-imported, ...Idempotency. Every imported note carries a src:, src-sha:, and src-lines: tag triple. A re-run of vectr memory import against an unchanged source file recognizes those tags and creates zero new notes. Editing a section of the source file produces a new body whose hash does not match the stored one, so it becomes a new note, and the old one is left untouched, since silently deleting a prior import behind the user's back is exactly the silent loss the design defends against on the export side.
Provenance. Imported notes carry a source-file tag (imported and src:FILENAME, where FILENAME is the basename of the source file), so a recall that lists a note carrying imported is telling the user "this came from a file, not from this session's observations." The note's provenance defaults to auto, the class machine-captured facts with no reviewing judgment already carry, since a hand-written MEMORY.md is structurally indistinguishable from a generator's output to a parser. The exception is narrow and structural, not semantic: a note whose body contains imperative-rule markers (the closed keyword set MUST, MUST NOT, NEVER, ALWAYS, DO NOT, DON'T, REQUIRED, FORBIDDEN) is classified as kind="directive" and upgraded to provenance="agent". The reason is the auto + directive write-time guard in remember(): an unreviewed standing rule is a contradiction in terms, and an imported rule is exactly that pair. Promoting to agent is the smallest possible deviation from auto, exactly enough to satisfy the guard, and the user promotes further with vectr_promote after review.
Excluding paths
Create .vectrignore in your project root (same syntax as .gitignore):
vendor/
node_modules/
*.pb.go
dist/Or pass --exclude at init time:
vectr init --exclude vendor --exclude distExclusions apply to both the initial index walk and the live file watcher, so adding a directory to .vectrignore stops a running instance from re-indexing it. The next index also prunes any chunks already stored for now-excluded or deleted files, so you do not have to rebuild from scratch. If you ever need a clean rebuild, for example after changing the embedding model, force one:
vectr index --path . --force # ignore the incremental cache, re-embed everythingSupported languages
Language | Chunking | Symbol graph |
Python | AST (functions, classes) | Yes |
JavaScript | AST (functions, classes, arrow fns) | Yes |
TypeScript | AST | Yes |
Go | AST | Yes |
Rust | AST | Yes |
Java | AST | Yes |
C | AST | Yes |
C++ | AST | Yes |
Zig | AST | Yes |
All others | 200-line windows, 50-line overlap | No |
HTTP routes (Flask and FastAPI decorators, Express app.get(), Spring @GetMapping) are extracted as symbols and searchable via vectr_locate("GET /api/users").
Cost
Cost | |
Embedding model | $0.00, a one-time download of about 290 MB, cached at |
Re-index (10k files, first run) | About 10 min on CPU, under 5 sec on subsequent runs (mtime cache) |
Incremental re-index per changed file | About 0.5 sec |
vectr_search / vectr_recall | $0.00, local inference only |
Security
The default is unchanged and stays the headline: local, no API key, zero config, for a solo developer on a personal machine. Out of the box, the daemon binds to 127.0.0.1 only, CORS is restricted to localhost origins, each workspace gets its own isolated DB directory, port, and process (owner-only 0700 on POSIX), and the index and notes persist locally in ~/.cache/vectr/.
Everything below is opt in. Enabling nothing changes nothing.
Authentication. Set VECTR_API_KEY and every request to /v1/* and /mcp must carry it (X-Api-Key: <key> or Authorization: Bearer <key>, constant-time comparison, with /v1/health left open for liveness probes). Generate a key with vectr key. When the key is set at start time, the editor MCP configs vectr writes include the header automatically. Those files (.mcp.json, .cursor/mcp.json, .vscode/mcp.json) then hold the key in plaintext, so treat them as secrets and keep them out of shared or public version control.
Encryption at rest. Set VECTR_ENCRYPT_KEY, or store a passphrase in the OS keychain (service vectr, username encrypt-key, requires pip install vectr[encryption]), and note content, note titles, and snapshot payloads are encrypted with Fernet and a PBKDF2-derived key. Honest boundary: the code index is not encrypted, because the search engine needs readable chunk text and vectors. Protect it with OS full-disk encryption. Note tags and metadata stay plaintext, and note embedding vectors (a lossy projection of note text) are kept for semantic recall unless you set VECTR_ENCRYPT_DISABLE_NOTE_VECTORS=1.
Retention and audit. Notes are kept until you delete them. Set VECTR_NOTES_TTL_DAYS to auto-purge older notes at startup. vectr_forget(all=true) and vectr forget --all delete notes, snapshots, and note vectors, so everything means everything. Set VECTR_AUDIT_LOG=<path> for a rotating local log of index, search, remember, and recall events (off by default, and it records query text, which is its purpose, and is never transmitted). Full policy: docs/data-handling.md.
Team mode (shared instance). One central daemon can serve a team on one repo. Run VECTR_API_KEY=<key> vectr start --host 0.0.0.0 on the server (a non-loopback bind refuses to start without a key), then vectr connect --url http://<host>:<port> --api-key <key> --label <you> on each client to point the editor at it. Working memory is shared: a note one agent stores, every connected agent can recall, and --label attributes notes and audit lines. Note IDs are allocated by the central store in the order writes arrive, so with concurrent clients your notes are not a contiguous block. They interleave with other clients'. The Stored note #N line the write returns is the canonical reference to that specific note, so do not assume the next ID is also yours. Plain limits: one shared key means every holder is an equal, trusted peer (no roles, no per-user permissions), the server operator can read everything, search results reference the server's checkout, which may differ from your local tree, and vectr speaks plain HTTP, so put TLS at a reverse proxy or tunnel if the network is not trusted.
Proactive context (experimental, localhost only)
Pull-based recall means the agent has to ask. Proactive context extends deterministic delivery to hosts without session hooks, by sitting on the wire instead. It is experimental and localhost only. Since 1.6.0 proactive delivery is on by default on local instances (proactive.enabled; turn off with VECTR_PROACTIVE=0). The wire channel below is governed by launch consent instead of that switch: starting the proxy and pointing your agent at it is the opt-in, and stopping it or unsetting the base URL is the opt-out.
The vectr proxy command runs a small local proxy between your agent and the model API:
vectr proxy # starts a localhost proxy (default :8785)
export ANTHROPIC_BASE_URL=http://127.0.0.1:8785 # point your agent at itWhat it does and does not do, plainly:
Transparent by default. It forwards every request to the real API, and streaming responses and tool calls pass through byte for byte. Your API key is forwarded untouched and never stored or logged.
Deterministic injection. With the workspace daemon running, it appends matched working-memory notes to the request after the last prompt-cache breakpoint, so your prompt cache is never invalidated. Triggering is a similarity threshold plus exact structural matches, never keyword guessing, with a strict per-request budget so a hint only lands when it is worth the tokens.
Fail open. If the intelligence layer is slow or errors, your request goes through unchanged. To bypass the proxy entirely, unset the base URL:
unset ANTHROPIC_BASE_URL. The proxy is on the request path, so if it is down, unset the variable to talk to the API directly.Solo and localhost only. It reads your conversation to compute context, so it refuses any non-loopback bind and is mutually exclusive with team mode.
Caveats on a non-first-party base URL, per the host's own documentation: MCP tool search is disabled unless
ENABLE_TOOL_SEARCH=trueand the proxy forwards tool-reference blocks, and Remote Control is disabled on a non-api.anthropic.combase URL.
Org-wide caching (team mode). With a central shared instance, vectr can cache its own expensive artifacts, namely semantic search and recall results, keyed by exact identity and the current index state, so a re-index or note change invalidates them automatically and every connected developer benefits. It is off by default (proactive.cache), and vectr status reports its hit rate and estimated tokens saved so the value is measured rather than asserted. Vectr does not cache LLM responses across similar requests, only byte-identical ones, locally, and opt in, because a wrong cache hit would silently corrupt a conversation.
When vectr can hurt
Stale notes after codebase churn. Notes store file paths at write time. After a large refactor, vectr_recall flags changed referenced files with [STALE]. Re-verify before acting, delete the stale note with vectr_forget(note_id=N), or clear everything with vectr_forget(all=true).
Over-retrieval on a well-known API. If the model already knows a framework deeply from training, vectr's research overhead may exceed the savings. The benchmark above shows exactly that on debug_descriptor_priority, a task where training knowledge alone was enough to navigate.
Stale architectural overview. A passport saved by vectr_map_save describes the architecture at index time. After renamed modules or restructured entry points it can be confidently wrong, so vectr_map surfaces a staleness warning and you should re-run vectr_map_save.
Broad recall costs tokens. vectr_recall() with no query returns everything. Pass a targeted query, and check vectr_status() first if you are unsure whether recall is warranted at all.
Built with
Python 3.14 · FastAPI · sentence-transformers · tree-sitter · ChromaDB · BM25 · Docker
Author
Swapnanil Saha · swapnanilsaha.com
Available Tools
23 toolsvectr_anchorAIdempotent
Attach file paths to an EXISTING note so future changes to those files can flag the note as possibly-stale (UPG-ANCHOR-ATTACH). The single-call replacement for re-storing with anchors=[...] and supersedes=<note_id> when you notice afterwards that a note is about a specific process/config file. Anchors are staleness probes, never a claim that the note is wrong: on the next staleness check, a changed anchor means the process MAY have changed. Idempotent — paths already attached are reported back, not duplicated.
Inputs: anchors (required), note_id (required).
vectr_distillAIdempotent
Review pending arcs — discovered failure->success moments captured automatically from your own tool calls (e.g. a command that failed twice, then succeeded after an edit) — and turn the ones worth keeping into working-memory notes. Call with no arguments to render pending arcs for review, oldest and most-confident first, token-bounded. For each arc worth keeping, call vectr_remember(..., distilled_from=[arc_id]) to persist the lesson as a note (see vectr_remember's own parameters for kind/priority/triggers). For arcs not worth keeping, call vectr_distill(dismiss=[arc_id, ...], reason='...'). Distiller rules: (1) recall-first dedupe — vectr_recall(query=) before writing; if an existing note already covers it, dismiss the arc (reason: covered by note #N) — if the arc proves an existing note wrong or outdated, use contradicts=/supersedes= on that note instead of writing a duplicate. (2) Generalize — store the lesson (what class of command fails, why, what fixed it), not the transcript; keep concrete commands/paths only when they ARE the lesson. (3) Kind mapping — an env/process/build fact -> kind='operational' (add triggers=[{'event': 'prompt-submit', 'semantic': True}, {'command': ''}] when the lesson is tied to a command family); tied to specific files -> kind='gotcha' with those files as anchors; a standing user rule -> kind='directive' (rare from arcs). (4) Low-confidence arcs are dismissed unless the same lesson recurs across >= 2 arcs. (5) Priority defaults to medium; high only when acting on the stale belief is costly (e.g. false-pass verification traps). (6) Batch cap — distill at most ~5 notes per sitting; leave the rest pending.
Inputs: reason, dismiss.
vectr_evict_hintARead-onlyIdempotent
Lists the code chunks retrieved by THIS session that are safe to drop from context — each is re-fetchable verbatim in one deterministic call, and the response includes the exact vectr_fetch(ids=[...]) re-fetch keys. Use at the exploration → implementation transition, or when context pressure builds. This is the reverse signal in the vectr protocol: the AI saves findings (vectr_remember), vectr signals what it can restore instantly (vectr_evict_hint). NOT needed on short sessions — most useful after many vectr_search/vectr_locate calls.
Inputs: no named parameters.
vectr_fetchARead-onlyIdempotent
Deterministic re-fetch of a code chunk by its exact id — no embedding, no rerank, just the chunk. Every vectr_search/vectr_locate/vectr_trace result carries its chunk's id (the file:start-end shown in the result header). Use this to restore a chunk that was cleared from your context (by tool-result eviction, context compaction, or a context-editing tombstone) instead of re-running vectr_search or re-reading the whole file. NOT for finding NEW content — use vectr_search for that.
Inputs: ids (required).
vectr_forgetADestructiveIdempotent
Delete working-memory notes. Pass note_id to delete ONE note — the usual case, when a note is stale or superseded (ids are the [#N] shown by vectr_recall). Pass all=true to irreversibly clear EVERY note for this workspace (e.g. after a large refactor). Calling with no arguments deletes nothing. Snapshots are preserved — only active notes are removed.
Inputs: all, note_id.
vectr_ingest_tracesA
Import runtime trace events into the symbol graph to enrich static call analysis. Use when you have runtime profiling data (Python sys.settrace output, JSON trace logs) that reveals dynamic dispatch patterns the static analyser cannot see: decorators, getattr, dependency injection, monkey-patching, etc. Pass a list of trace events: [{caller, callee, caller_file?, caller_line?}, ...]. Dynamic edges are stored with edge_type='dynamic' and appear in vectr_trace results marked "(dynamic)" so you can tell them apart from statically-discovered calls. A caller/callee name that matches no indexed symbol is still ingested (it may be external or runtime-only) but is reported back as a warning — check for typos. NOT needed if static analysis (vectr_trace) already shows the call relationships.
Inputs: events (required).
vectr_locateARead-onlyIdempotent
Use when you know the SYMBOL NAME but not which file it's in. Returns file path + line number + kind for every matching definition. NOT when you're searching by concept or behaviour — use vectr_search instead. NOT when you want call relationships — use vectr_trace instead. Example: vectr_locate(name='EvaluateSegments') → 'targeting/evaluator.go:45'
Inputs: name (required), limit, caller_file.
vectr_mapARead-onlyIdempotent
Use at the start of a session on an UNFAMILIAR codebase to get a structural overview without reading any files. If a passport has been saved: returns a compact (~300 token) plain-English summary instantly. If not yet saved: returns raw structural metadata (dir tree, languages, frameworks) and instructs you to call vectr_map_save with your synthesised summary. NOT needed if you already know the codebase structure. NOT a substitute for vectr_recall — call vectr_status first to check for prior notes.
Inputs: no named parameters.
vectr_map_saveAIdempotent
Save your synthesised codebase summary as the permanent passport. Call this ONLY after vectr_map returned raw metadata — i.e. on your first visit to a codebase. NOT when vectr_map already returned a saved summary (passport already exists). Write a concise plain-English summary: what the codebase does, tech stack, key modules, entry points, domain terms. Aim for ~200-350 tokens. Does NOT overwrite an existing passport by default — if one is already saved, the call is a no-op and returns the existing summary; pass overwrite=true to replace it.
Inputs: summary (required), overwrite.
vectr_pinAIdempotent
Pin a note so it is injected on EVERY future vectr_recall(query=...) call, regardless of the query — never relevance-scored, never dropped for being off-topic. Use for a note that must never be missed by an unlucky query (the same standing-rule role kind='directive' already plays, but for a note you don't want to reclassify as a directive). Pass pinned=false to unpin. Bounded: only a small configured number of directive+pinned notes ever occupy this unconditional tier, so pin sparingly. Equivalent to passing pin=true directly to vectr_remember at write time.
Inputs: note_id (required), pinned.
vectr_promoteAIdempotent
Raise an auto-captured note's trust class to 'agent' — e.g. after this session has reviewed an auto-captured note and confirmed it still holds. This tool only takes that one step (auto -> agent); it never promotes a note to 'human', because deciding that a person has endorsed something is not the agent's call to make. Human endorsement happens on a user-side surface instead (a CLI/UI a person operates), not through this tool.
Inputs: to (required), note_id (required).
vectr_recallARead-onlyIdempotent
Retrieve notes stored earlier in this session or in prior sessions. TWO-TIER RECALL (UPG-RECALL-HIERARCHY): By default returns a crisp one-line index per note (id + kind/priority + title + age) — token-bounded, safe to call broadly. To read a note body: pass note_id=N (expand one note, full body) or detail='full' (all bodies). Use when vectr_status() confirmed notes_count > 0 — notes may have been stored this session or in a previous one; either way they are immediately useful. Pass a targeted query to retrieve only the notes relevant to your current task — do NOT call with no query unless you need everything.
Inputs: boot, kind, tags, limit, +6 more.
vectr_reinstateAIdempotent
Reverse a prior vectr_revoke (or a contradicts= write) — the note returns to active state and its original content, not the deterrent block, is shown again.
Inputs: note_id (required), reason.
vectr_rememberA
Save a working note and recall it on demand in <50ms — whether later this session, through context compaction, or in a future session. Use the moment you discover something non-obvious: a key file path, a call pattern, a gotcha, a partial stub, task progress. Store the actual code or finding — vectr returns it in <50ms; re-reading the file costs tokens and turns. Do NOT store obvious or easily re-derivable facts (e.g. 'the main file is main.py'). For a body over ~2KB, especially code-heavy with quotes/escapes, write it to a file and pass content_file instead of content — long escape-dense strings can be corrupted mid-stream as a tool-call argument. Retrieve with vectr_recall(query='what you need') — any time, same session or later.
Inputs: pin, kind, tags, agent, +12 more.
vectr_resumeARead-onlyIdempotent
One-call 'pick up where you left off': the most recent current-task note, the latest saved snapshot, and any open gotchas with their file anchors. Call at the start of a session or after a gap to reorient without re-reading files or re-deriving state vectr_recall would otherwise need several calls to assemble.
Inputs: no named parameters.
vectr_revokeAIdempotent
Flag a stored note as WRONG without deleting it — use when you've confirmed a prior finding no longer holds (contradicted by newer evidence, or you got it wrong the first time). Unlike vectr_forget, the note is not erased: it stays visible on future vectr_recall/session-start as a deterrent — 'previously believed..., revoked..., do not re-derive this without verification' — instead of its original content, so nothing silently repeats the mistake. Reversible with vectr_reinstate. Prefer passing contradicts=<note_id> directly to vectr_remember when you're recording the correction anyway; use this tool when you need to revoke without writing a replacement note.
Inputs: reason (required), note_id (required).
vectr_searchARead-onlyIdempotent
Use when you know WHAT you're looking for but not WHERE it is or WHAT it's called. Hybrid semantic + BM25 search — finds code by concept, behaviour, or description. Returns function/class bodies with file paths and line numbers. NOT when you already know the symbol name — use vectr_locate instead. NOT when you want call relationships — use vectr_trace instead.
Inputs: query (required), language, n_results.
vectr_snapshotA
Seal all current vectr_remember() notes as a named checkpoint. Use when you've stored multiple notes and want to mark a milestone you can return to (e.g. 'auth-refactor-wip', 'segment-targeting-done'). The next time you work on this, vectr_recall will return these notes. NOT required if you only stored 1-2 notes — vectr_recall retrieves all notes regardless.
Inputs: label (required), session_id.
vectr_snapshot_listARead-onlyIdempotent
List all saved session snapshots for this workspace, newest first. Use at session start to find an existing checkpoint if vectr_recall returned nothing or if you want to resume a specific named session.
Inputs: no named parameters.
vectr_statusARead-onlyIdempotent
Returns index health (files, chunks, embed model) AND notes_count (number of notes stored — earlier in this session or in prior sessions). Call once at the start of any session to decide whether vectr_recall is worth calling: if notes_count > 0, call vectr_recall(query=...) to retrieve relevant notes. If notes_count == 0, skip recall entirely. If your session already shows auto-injected Working Notes (vectr hooks), those ARE the recall output — do not re-call vectr_recall for them. Also useful when vectr_search returns nothing and you suspect indexing is still running.
Inputs: no named parameters.
vectr_supersedeAIdempotent
Retire an ALREADY-STORED note as superseded, when you discover only AFTER writing its replacement that supersedes= was not passed on that write. The retired note stops appearing in default recall/session-start and renders with a factual '[superseded ...]' badge — it is NOT marked wrong and never shows the revoked deterrent (it was accurate when written; the world moved on). Use vectr_revoke instead when the note is actually FALSE. Pass superseded_by=<note_id> to link the replacement if one exists; omit it to retire with no successor. Reversible with vectr_reinstate.
Inputs: note_id (required), reason, superseded_by.
vectr_traceARead-onlyIdempotent
Use when you know the SYMBOL NAME and need to understand its callers or callees before modifying it. Traverses the call graph in both directions. NOT when you don't know the symbol name yet — use vectr_search or vectr_locate first. NOT when you just want the definition location — use vectr_locate instead. Example: vectr_trace(name='EvaluateSegments') → 'Called by: RequestBid() in bidder/auction.go'
Inputs: name (required), limit, direction, include_builtins.
vectr_unanchorAIdempotent
Remove file paths from an EXISTING note's anchor set (UPG-ANCHOR-DETACH). The inverse of vectr_anchor: use it when you discover a note was anchored to the wrong file, or when the anchored file's relevance has gone away. A removed anchor simply stops being a candidate in the next staleness check — never a claim that the note is wrong. Idempotent — paths the note was never anchored to are reported back, not treated as failures. Path comparison is exact-string on the spelling the note was anchored with, matching vectr_anchor's contract.
Inputs: anchors (required), note_id (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 23 tools
The code navigation tools are strongly differentiated with explicit NOT-when guidance, and the search/locate/trace/fetch split is very clear. The main ambiguity risk sits in the note lifecycle cluster—forget, revoke, supersede, and reinstate have subtle boundaries that require reading the descriptions carefully.
All tools share the vectr_ prefix and snake_case convention, making the family instantly recognizable. The style is not perfectly uniform—most are simple verbs (recall, locate, remember), but a few are noun-led or compound forms (map_save, snapshot_list, evict_hint).
23 tools sits in the heavy range and would feel dense at first impression. Several note-lifecycle operations like pin, anchor, unanchor, promote, revoke, reinstate, and supersede are narrow enough that they could plausibly be consolidated, even though each has a distinct purpose.
The set covers its domain thoroughly: codebase discovery via map, search, locate, trace, fetch, and dynamic trace ingestion, plus a full persistent-note lifecycle with create, recall, delete, supersede, revoke, snapshot, pin, and anchoring. There are no obvious dead ends for the stated purpose, and edge cases like staleness detection are explicitly handled.
Maintenance
Related MCP Connectors
Self-hosted AI workspace: semantic RAG document search, long-term memory, and AI chat via MCP.
Shared memory for coding agents. Stop re-explaining your codebase every session.
Private persistent memory for Claude, ChatGPT & Gemini via MCP - semantic search, zero-code setup.
Persistent memory for AI agents. Semantic search, memory graph, W3C DID identity.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server with local vector search for your codebase. Smart indexing, semantic search, Git history — all offline.7107 PyPI50MIT
- AlicenseBqualityDmaintenanceGives AI coding assistants cross-session persistent memory with semantic search and task management, fully local.8Apache 2.0
- AlicenseCqualityBmaintenanceGives AI coding assistants persistent project memory and semantic code search, running fully locally with no API keys required.56MIT
- AlicenseAqualityDmaintenancePersistent memory for AI coding agents. Builds semantic memory from git history and codebase, searchable via MCP tools.44 npm2MIT