Local repository intelligence for coding agents
Project description
RepoCtx
Give your coding agent the ground truth for the task at hand.
A local MCP server that, for each task, hands your agent the docs, contracts, invariants, in-scope files, related tests, and import-graph neighbors that actually matter — so the model anchors on what's real instead of guessing.
Works with Cursor, Claude Desktop, Claude Code, Codex, and any MCP-speaking client.
AI agents working in this repo or advising users on RepoCtx: start with AGENTS.md.
The problem
A few prompts into a task, agents drift. They edit unrelated files, add "small improvements" that pull them away from the goal, and break invariants nobody told them about. Not because the model is bad — it just doesn't know which docs to trust, what's load-bearing, or where the task ends.
That part doesn't have to be guessed every turn. It can be prepared once and handed to the agent up front.
What RepoCtx does
For a task like "add retry jitter to webhook delivery", RepoCtx returns a ground-truth bundle:
| Layer | What you get |
|---|---|
| Authoritative docs | AGENTS.md, README.md, architecture notes, ADRs |
| Contracts & invariants | contracts/**, plus inline INVARIANT: / DO NOT: / IMPORTANT: markers |
| In-scope source files | The files the task actually touches (heuristics + embeddings) |
| Related tests | Tests likely to cover what you're editing |
| Graph neighbors | Nearby modules from the local import graph |
| Edit scope | allowed_paths, related_paths, protected_paths |
| Validation plan | Tests and commands to run before finalizing |
| Risk notes | Protected-path touches, constraint violations, possible drift |
The bundle is compact, token-budgeted, Markdown or JSON.
Install
pip install "repoctx-mcp[embeddings]"
Requires Python 3.11+. The embeddings extra downloads a ~1.2 GB local model on first use (Qwen3-Embedding-0.6B). For a lighter install without semantic retrieval:
pip install repoctx-mcp
Naming note: the package is
repoctx-mcp, the CLI command isrepoctx, the Python module is alsorepoctx.
Then, from inside the repo you want to use it on:
repoctx install
This single command:
- wires RepoCtx into Claude Code, Cursor, and Codex (whichever configs exist locally or globally)
- scaffolds a starter authority layout (
contracts/,docs/architecture/) - builds the embedding index if
[embeddings]is installed
Flags: --no-index skips the embedding build, --with-index requires it (errors if extras are missing), --no-scaffold skips the contracts/docs scaffold.
You can also install for one client at a time: repoctx install-claude-code, repoctx install-cursor, repoctx install-codex. All installers are idempotent — existing config and other MCP servers are preserved.
Editor setup
repoctx install writes these for you, but if you'd rather configure manually:
Cursor
Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per-project):
{
"mcpServers": {
"repoctx": {
"command": "python3",
"args": ["-m", "repoctx.mcp_server"]
}
}
}
Restart Cursor. RepoCtx auto-resolves to the nearest git root from Cursor's workspace context.
Claude Desktop
Open Settings → Developer → Edit Config (or edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"repoctx": {
"command": "python3",
"args": ["-m", "repoctx.mcp_server"]
}
}
}
Restart Claude Desktop. Because Claude Desktop launches MCP servers via launchd (cwd /, no workspace env vars), the first tool call of a session needs to supply repo_root once. The session memo carries it for every call after that.
Codex
Add to ~/.codex/config.toml or .codex/config.toml:
[mcp_servers.repoctx]
command = "python3"
args = ["-m", "repoctx.mcp_server"]
Or from the CLI:
codex mcp add repoctx -- python3 -m repoctx.mcp_server
Claude Code
repoctx install writes .mcp.json and adds an AGENTS.md / CLAUDE.md nudge so the agent knows to call bundle at task start. It also wires up three hooks (see Claude Code hook integration below).
What to ask your agent
Just describe the task normally:
- "Add retry jitter to webhook delivery."
- "Refactor the auth middleware to support OAuth."
- "Find the files involved in syncing local env with Vercel."
- "Show me the tests related to the billing webhook flow."
The agent calls RepoCtx as a tool and uses the bundle to scope its work.
Example output
For "add retry jitter to webhook delivery":
## Summary
Identified 2 docs, 2 files, 1 test, and 1 graph neighbor relevant to
'add retry jitter to webhook delivery'.
## Relevant Docs
- AGENTS.md — matches: retry, webhook
- docs/WEBHOOKS.md — matches: retry, webhook
> Webhook delivery retries should use exponential backoff with jitter.
## Relevant Files
- src/webhook/retry_policy.py — matches: retry
> def compute_retry_delay(): ...
## Related Tests
- tests/test_retry_policy.py — stem match + imports retry_policy.py
## Graph Neighbors
- src/webhook/delivery.py — imported by retry_policy.py
Pass --format json for structured output.
MCP tools
The full protocol — call these from any MCP client. Target usage is ≤ 5 calls per task.
| Tool | CLI | When to call |
|---|---|---|
bundle(task) |
repoctx bundle "task" |
Task start — primary call |
authority(task) |
repoctx authority "task" |
Just need authority + constraints |
scope(task) |
repoctx scope "task" |
Deciding what to edit |
validate_plan(task, changed_files) |
repoctx validate-plan "task" --changed a.py b.py |
Before finalizing |
risk_report(task, changed_files) |
repoctx risk-report "task" --changed a.py b.py |
Before finalizing |
refresh(task, changed_files, current_scope) |
repoctx refresh "task" --changed a.py ... |
Scope expanded mid-task |
semantic_search(query, top_k, kind) |
repoctx semantic-search "query" --top 10 |
Raw similarity lookup, no bundling |
Every bundle also carries self-recall rules — when_to_recall_repoctx, before_finalize_checklist, uncertainty_rule — so the agent knows when to call back without you reminding it.
(A legacy get_task_context(task) entry point is still supported — see the appendix.)
Authority conventions
RepoCtx looks for the following (all optional, all lightweight):
| Layer | Where | What |
|---|---|---|
| Level 1 — Contracts | contracts/** |
YAML front-matter (applies_to, severity, validated_by) + ## Invariants / ## Do not sections |
| Level 2 — Agent docs | AGENTS.md, AGENT.md, CLAUDE.md |
Agent-facing instructions |
| Level 2 — Architecture | docs/architecture/**, docs/adr/** |
Architecture notes, ADRs |
| Examples | examples/** |
Validating examples |
| Contract tests | tests/contracts/** |
Tests that enforce contracts |
| Inline markers | Any file | # INVARIANT:, # CONTRACT:, # DO NOT:, # IMPORTANT:, # See contract: <path> |
Bootstrap with repoctx init-authority to scaffold contracts/ and docs/architecture/ with starter templates.
repoctx propose-authority scans your repo and suggests candidates — files that look load-bearing based on imports, references, and patterns — so you don't have to declare them by hand.
Design doc: docs/plans/2026-04-23-repoctx-v2-design.md.
Claude Code hook integration
repoctx install (and repoctx install-claude-code) adds three hooks to .claude/settings.json that nudge the agent to actually use RepoCtx:
| Hook | When | What it does |
|---|---|---|
UserPromptSubmit |
Task entry | For substantive prompts, reminds the agent to call mcp__repoctx__bundle before planning |
PostToolUse matching Edit|Write|MultiEdit |
After every edit | Keeps the embedding index live (repoctx update --from-claude-hook) |
Stop |
Task exit | If the turn made edits but didn't call validate_plan, reminds the agent to run it |
Both nudges read Claude Code's hook JSON from stdin and always exit 0 — they never block the user's flow. The entry nudge stays silent for short, keyword-free prompts. The exit nudge stays silent when there were no edits, or when validate_plan was already called this turn. Hook entries are detected by command prefix on re-install, so re-running repoctx install is safe.
The anchored <!-- repoctx-nudge --> block placed in CLAUDE.md / AGENTS.md ships with current wording (must-call + inline definition of "non-trivial"). Older anchored blocks from earlier installs are rewritten in place on the next install / refresh without touching surrounding doc content.
Tuning / opt-out:
REPOCTX_LEARN=1— appends "If you decide to skip this, briefly state the reason." to reminders so you can collect skip rationale during a tuning week. Off by default.--no-claude-md-nudgeorREPOCTX_NO_CLAUDE_MD_NUDGE=1— skip the anchored-block insertion.- To fully opt out: delete the entries from
.claude/settings.json. They won't be re-added unless you re-run the install command.
Embeddings (optional, on by default)
RepoCtx blends three signals — heuristic token overlap, doc priority + graph expansion, and local embeddings — to rank candidate files. The embedding layer uses Qwen3-Embedding-0.6B and improves recall when the task description shares no tokens with relevant filenames.
pip install "repoctx-mcp[embeddings]" # adds sentence-transformers, numpy, tree-sitter
cd /path/to/repo
repoctx install # builds index automatically
Add .repoctx/ to .gitignore.
Manual control:
repoctx index # build the index
repoctx update src/billing/foo.py # re-embed a single file
repoctx rebuild # rebuild from scratch
The indexer splits each file into overlapping chunks — symbol-aware for code (function/class/method boundaries), paragraph-aware for prose — and stores them with metadata (file:, kind:, module:, symbol:, lines:) under .repoctx/embeddings/.
Scoring:
final_score = heuristic_score + embedding_weight × max(0, cosine_similarity)
Default embedding_weight is 12.0. Files with cosine similarity above 0.3 bypass heuristic filters, so semantically relevant files surface even without keyword overlap. For multi-chunk files, the file's similarity is the max over its chunks.
Tunables (env vars):
| Var | Default | Notes |
|---|---|---|
REPOCTX_EMBEDDING_DEVICE |
auto |
cpu / cuda / mps / auto |
REPOCTX_EMBEDDING_BATCH_SIZE |
16 |
Clamped to 8 on MPS |
REPOCTX_EMBEDDING_MAX_SEQ_LENGTH |
256 |
|
REPOCTX_EMBEDDING_DTYPE |
auto |
fp16 on accelerators, fp32 on CPU |
Apple silicon (MPS): indexing handles GPU memory automatically (fp16,
max_seq_length=256, batch clamped to 8, cache eviction between super-batches). Catchable encode errors fall back to CPU transparently. The rare uncatchable Metal C++ assertion still requiresREPOCTX_EMBEDDING_DEVICE=cpu repoctx index.
Fallback: if embedding dependencies aren't installed or no index exists, RepoCtx silently falls back to pure heuristic retrieval. The MCP tool contract is unchanged.
Migrating an older index? See Legacy and migration notes in the appendix.
CLI reference
The CLI works standalone too — useful for terminal testing or non-MCP usage.
cd my-app
repoctx "your task" # default: query
repoctx "your task" --format json
repoctx query "your task" --debug-scores # show heuristic/embedding/final breakdown
| Flag | Description |
|---|---|
--repo PATH |
Repository root override |
--format markdown|json |
Output format |
--verbose |
Debug logging |
--debug-scores |
Print score breakdown |
--no-embeddings |
Disable embedding retrieval for this query |
Subcommands (repoctx COMMAND --help for each):
query(default),bundle,authority,scope,validate-plan,risk-report,refresh,detect-changes,semantic-searchindex,update,rebuildinstall,install-claude-code,install-cursor,install-codexinit-authority,propose-authorityexperiment,hook,stats
Configuration
Repo root resolution
RepoCtx resolves the repo root in this order:
- Per-call
repo_rootargument on the MCP tool — strongest override; memoized for the lifetime of the MCP server process. --repo /path/to/repoflag (CLI / server startup).REPOCTX_REPO_ROOTenv var.- Host workspace env vars (
CLAUDE_PROJECT_DIR,WORKSPACE_FOLDER_PATHS,VSCODE_CWD). Path.cwd()if it is not/.$PWDenv var.
Whatever candidate is chosen is then walked upward to the nearest .git entry (both .git directories and .git files are accepted, so linked worktrees and submodules work). Nested repositories resolve to the nearest repo, not the outermost parent.
If no git root is found, RepoCtx fails with a message listing your most recently resolved repos so the agent can pick one and pass it as repo_root. RepoCtx never auto-selects from the recency list.
Supported files
| Category | Extensions |
|---|---|
| Code | .py, .ts, .tsx, .js, .jsx |
| Config | .json, .yaml, .yml |
| Docs | .md, .mdc |
Import-graph expansion works for Python (import, from) and JavaScript/TypeScript (import, require).
Telemetry
RepoCtx writes local JSONL telemetry to ~/.repoctx/telemetry/ by default. Task text and repo identifiers are hashed before storage. Set REPOCTX_TELEMETRY_DIR to change the location.
FAQ
Do I need to run a server manually?
No. Cursor, Claude Desktop, Claude Code, and Codex all start the MCP server for you from the config. You'd only run python3 -m repoctx.mcp_server if you were debugging the server itself.
Do I need to write a skill? No. RepoCtx is an MCP server, not a skill. Once your client is configured, it becomes a tool the agent can call.
Do I need one config per repo? Either works. Use a global config if you want RepoCtx everywhere; use a project config if you want it tied to one repo and shared with teammates.
How does RepoCtx pick the repo automatically? See Repo root resolution.
Can I test from the terminal first?
Yes — repoctx "your task" from inside any repo.
Appendix
Controlled experiment mode
RepoCtx can run a guided control vs. treatment comparison with paired git worktrees, so you can measure whether RepoCtx actually changes the outcome on a real task.
repoctx experiment
The wizard:
- collects one shared prompt for both lanes
- creates two clean worktrees from the same base commit under
.worktrees/ - hands you off to the
controlworktree first - on re-run, records control costs, prepares the
treatmentworktree, writes.cursor/mcp.jsonthere so RepoCtx is enabled in that lane only - after the treatment run, prints the summary
Fast path if you already know the prompt:
repoctx experiment "refactor the auth middleware to support OAuth"
The wizard asks for cost before and cost after for each lane so RepoCtx can compute the delta.
Control-lane MCP suppression: during the control lane, RepoCtx stubs MCP tool results (empty context + short message) so agents don't get RepoCtx retrieval even when RepoCtx is registered in Cursor's global mcp.json. Timing is controlled by ~/.repoctx/config.json:
| Key | Default | Meaning |
|---|---|---|
experiment_mcp_suppress |
true |
Set false to disable arming entirely |
experiment_mcp_idle_ttl_seconds |
90 |
Auto-clear suppression after this many seconds of CLI inactivity |
experiment_mcp_extend_seconds |
600 |
Each repoctx CLI run extends the deadline by this many seconds |
State lives at ~/.repoctx/telemetry/experiment-mcp-suppress.json. Cleared when you record a lane, start the treatment handoff, or hit the idle deadline.
More detail: docs/experiment-mcp-suppression.md.
What the experiment measures:
- manual before/after cost checkpoints from your agent UI
- git-derived change statistics from each isolated worktree
- optional completion + verification status
Limitations:
- doesn't measure external agent time automatically
- doesn't score quality automatically
- cost accuracy depends on the numbers you enter
Legacy and migration notes
Legacy MCP entry point. The original v1 tool get_task_context(task) is still exposed and works as a basic context-pack call. It's superseded by bundle(task), which returns the same context plus authority records, constraints, edit scope, validation plan, and risk notes. New integrations should call bundle.
Embedding index schema. The on-disk index format changed in 1.0.0 (schema_version: 2). Indexes built before 1.0.0 raise IndexSchemaMismatch on load — delete .repoctx/embeddings/ and re-run repoctx index once after upgrading.
Nudge block format. The anchored <!-- repoctx-nudge --> block in CLAUDE.md / AGENTS.md evolved between releases. Older anchored blocks are rewritten in place on the next install / refresh without touching surrounding doc content — no manual cleanup needed.
Development
git clone https://github.com/gald33/repoctx.git
cd repoctx
python3 -m pip install -e ".[dev,embeddings]"
python3 -m pytest -q
Agent guidance for working in this repo: AGENTS.md.
License
MIT
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 repoctx_mcp-1.4.0.tar.gz.
File metadata
- Download URL: repoctx_mcp-1.4.0.tar.gz
- Upload date:
- Size: 191.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a26fb1450bf52b52b202ea58c9c3b3fd7f4b9c45fc4ec068793befa5041cbb0e
|
|
| MD5 |
06ccc51dd24add44aea4f2695578964b
|
|
| BLAKE2b-256 |
8074fd9026fd7b9fd6cfb00ba4253e6bf4d9eaff88696b5ce1dcfafb709a1917
|
Provenance
The following attestation bundles were made for repoctx_mcp-1.4.0.tar.gz:
Publisher:
publish-pypi.yml on gald33/repoctx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoctx_mcp-1.4.0.tar.gz -
Subject digest:
a26fb1450bf52b52b202ea58c9c3b3fd7f4b9c45fc4ec068793befa5041cbb0e - Sigstore transparency entry: 1538983672
- Sigstore integration time:
-
Permalink:
gald33/repoctx@252191758512e62bfad4dd37662c87beb05d18b6 -
Branch / Tag:
refs/tags/v1.4.0 - Owner: https://github.com/gald33
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@252191758512e62bfad4dd37662c87beb05d18b6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file repoctx_mcp-1.4.0-py3-none-any.whl.
File metadata
- Download URL: repoctx_mcp-1.4.0-py3-none-any.whl
- Upload date:
- Size: 157.9 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 |
cd8c3760a8c0923ed370b44326345406dfda798745029dc722739857b45e4513
|
|
| MD5 |
f0ee8f1e6d6e34236f755dae98481aa7
|
|
| BLAKE2b-256 |
9da27a2871efeb652d3ddb66da0af9eea96ee196a134af2ca4b693a2693e0908
|
Provenance
The following attestation bundles were made for repoctx_mcp-1.4.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on gald33/repoctx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoctx_mcp-1.4.0-py3-none-any.whl -
Subject digest:
cd8c3760a8c0923ed370b44326345406dfda798745029dc722739857b45e4513 - Sigstore transparency entry: 1538983792
- Sigstore integration time:
-
Permalink:
gald33/repoctx@252191758512e62bfad4dd37662c87beb05d18b6 -
Branch / Tag:
refs/tags/v1.4.0 - Owner: https://github.com/gald33
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@252191758512e62bfad4dd37662c87beb05d18b6 -
Trigger Event:
push
-
Statement type: