Status: Released in RepoNeedle 1.2.0 (PyPI, 2026-11-12). The earlier release 1.1.0 does not include the MCP server.
Transport: STDIO JSON-RPC 2.0 (HTTP/SSE transport is not implemented)
SDK Requirement: mcp>=1.28,<2 (Optional dependency group: mcp)
Validation: Verified with the MCP Python SDK (in-process tool calls) and a raw JSON-RPC STDIO handshake (initialize, tools/list, tools/call) in the test suite. Not yet validated in a live Claude Code, Cursor, Cline or Windsurf session; the client configuration examples below follow each client's documented MCP configuration format.
The Model Context Protocol (MCP) is an open specification enabling AI coding agents (such as Claude Code, Cursor, Cline, and Windsurf) to securely access local tools, file systems, and specialized retrieval engines through a standardized JSON-RPC 2.0 protocol.
- CPU-Only Execution: Runs locally on commodity CPUs without GPUs, API keys or network calls. On the ARB v2 benchmark, per-query latency after indexing was ~142 ms median and ~318 ms mean (repository indexing is a separate one-time cost at server startup).
-
Token Budget Invariant: Returned context satisfies
$$C_{\text{actual}} \le B_{\text{configured}}$$ as counted by RepoNeedle's internal regex token model. Counts from a specific LLM tokenizer may differ, so leave headroom in tight context windows. - Change-Impact Analysis: Ranks downstream files, modules and regression tests related to files an agent has edited.
-
Optional Dependency: MCP support is packaged as an optional extra (
mcp). Users who do not need MCP do not install the MCP SDK; the core engine keeps its standard dependencies (numpy, scipy, networkx, pydantic, pathspec, rich).
RepoNeedle core requires Python 3.10+. Install RepoNeedle 1.2.0 or later with the optional mcp extra:
pip install "reponeedle[mcp]>=1.2.0"Note
1.1.0 contains neither the MCP server nor the mcp extra. The >=1.2.0 pin also upgrades an existing 1.1.0 installation.
For local development, install from a clone:
git clone https://github.com/krishika08/RepoNeedle.git
cd RepoNeedle
pip install -e ".[mcp]"To verify that the MCP server executable is available:
reponeedle-mcp --helpThe RepoNeedle MCP server operates locally over the standard STDIO transport.
reponeedle-mcp --repo /absolute/path/to/repository --budget 4000python -m reponeedle.mcp --repo /absolute/path/to/repository --budget 4000| Option | Flag | Default | Description |
|---|---|---|---|
--repo |
-r |
. |
Absolute or relative path to the target repository root. |
--budget |
-b |
4000 |
Default token budget for retrieved context blocks. |
--transport |
stdio |
Transport protocol (stdio). |
|
--no-skeletons |
False |
Require full file bodies; disallow structural skeleton mode. | |
--max-file-size-bytes |
500000 |
Skip files strictly larger than this many bytes during the startup index. Must be a positive integer (zero/negative exits with code 1). Skipped paths are reported by reponeedle_inspect. |
If files are excluded at startup, the server writes a one-line summary to STDERR, e.g. [reponeedle-mcp] Indexing exclusions: 2 file(s) over 500000 bytes, 0 unreadable. Call reponeedle_inspect for the skipped paths.
Important
STDIO Cleanliness: The MCP server sends all initialization logs and diagnostics exclusively to STDERR. STDOUT carries strictly formatted JSON-RPC messages, ensuring zero protocol framing corruption.
To add RepoNeedle as an MCP tool server in Claude Code, run (the -- separates Claude Code's own options from the server command and its arguments):
claude mcp add reponeedle -- python -m reponeedle.mcp --repo /absolute/path/to/your/projectOr add an mcpServers entry to a Claude Code MCP configuration file (see the Claude Code documentation for the project- and user-scope file locations):
{
"mcpServers": {
"reponeedle": {
"command": "python",
"args": [
"-m",
"reponeedle.mcp",
"--repo",
"/absolute/path/to/your/project",
"--budget",
"4000"
]
}
}
}In Cursor:
- Open Cursor Settings
$\to$ Features$\to$ MCP. - Click + Add New MCP Server.
- Configure the server:
-
Name:
reponeedle -
Type:
command -
Command:
python -m reponeedle.mcp --repo /absolute/path/to/your/project
-
Name:
Or edit .cursor/mcp.json:
{
"mcpServers": {
"reponeedle": {
"command": "python",
"args": [
"-m",
"reponeedle.mcp",
"--repo",
"/absolute/path/to/your/project"
]
}
}
}RepoNeedle exposes three tools to connected agents:
Retrieves relevance-ranked source files, focus spans, and structural skeletons matching a natural language coding task, strictly packed within the requested token budget.
{
"query": "Fix authentication session expiration timeout",
"budget_tokens": 4000,
"ranker": "legacy",
"task_type": "auto",
"anchor_files": ["auth/session.py"],
"enable_experimental_abstention": false
}query(string, required): Coding task, issue description, or traceback.budget_tokens(integer, default4000): Maximum context tokens.ranker(string, default"legacy"): Ranking algorithm (legacy,bm25,arm101,cascade,change_impact).task_type(string, default"auto"): Workflow intent (auto,code2test,comment2context,trace2code,edit2ripple,selective).selectivedoes not enable abstention.anchor_files(array of strings, optional): Seed files used to condition retrieval.enable_experimental_abstention(boolean, defaultfalse): Opt in to the experimental, uncalibrated out-of-scope abstention heuristic. Whenfalse, the tool always returns ranked results (except for an empty repository) and does not detect out-of-scope queries. See Section 5.4.
{
"status": "success",
"query": "Fix authentication session expiration timeout",
"ranker": "legacy",
"task_type": "comment2context",
"abstained": false,
"explanation": "Retrieved 3 files (1420/4000 tokens). Ranker: legacy. Task: comment2context. Experimental abstention disabled.",
"budget_tokens": 4000,
"total_tokens": 1420,
"execution_time_ms": 12.4,
"confidence_score": 0.84,
"selected_files": [
{
"rank": 1,
"path": "auth/session.py",
"tier": "focus_span",
"token_count": 450,
"score": 0.8124,
"explanation": "High-density implementation span"
}
],
"rendered_context": "# File: auth/session.py\n```python\n...\n```\n\n",
"abstention": {
"enabled": false,
"status": "disabled",
"experimental": true,
"calibrated": false,
"threshold": 0.12,
"reason": "Experimental abstention disabled (default); out-of-scope queries are not detected and results are returned regardless of query scope."
}
}confidence_score is an uncalibrated heuristic signal, not a probability that the repository contains the answer.
Identifies downstream files, dependent modules, and regression test suites affected by modifications to specific files. Implements verified R6 champion algorithms:
-
Seed File Suppression (SFS): Downweights seed files (
$0.01\times$ ) so the agent is not re-fed files it already modified. -
Test Intent Priors (TIP): Dynamically boosts associated regression test suites (
$1.45\times$ ). - AST Dependency Traversal: Confirmed imports, function calls, and directory proximity.
{
"anchor_files": [
"auth/session.py",
"auth/token.py"
],
"query": "Assess authentication refactor impact",
"budget_tokens": 4000
}anchor_files(array of strings, required): One or more modified or seed file paths.query(string, optional): Context description or task intent.budget_tokens(integer, default4000): Maximum context tokens.
Enables the coding agent to inspect the indexed repository state without dumping full source trees.
Takes no arguments ({}).
{
"status": "ready",
"repository_root": "/absolute/path/to/project",
"indexed_file_count": 148,
"supported_file_types": [
".py", ".ts", ".js", ".tsx", ".jsx",
".go", ".rs", ".java", ".c", ".cpp",
".h", ".md", ".toml", ".yaml", ".yml", ".json"
],
"indexed_path_examples": [
"auth/session.py",
"auth/token.py",
"database/connection.py"
],
"sample_count": 3,
"active_rankers": ["arm101", "bm25", "cascade", "change_impact", "legacy"],
"default_ranker": "legacy",
"budget_config": {
"max_tokens": 4000,
"allow_skeletons": true,
"mode": "auto"
},
"index_initialization": {
"status": "initialized",
"init_time_ms": 145.2,
"message": "Repository indexed in-memory at server startup. Restart server to refresh index."
},
"indexing": {
"source": "directory",
"indexed_files": 148,
"max_file_size_bytes": 500000,
"skipped_large_files": 2,
"skipped_paths": ["generated/large_module.py", "vendor/huge_source.cpp"],
"skipped_unreadable_files": 0,
"unreadable_paths": [],
"skipped_symlink_outside_repository": 0,
"symlink_outside_repository_paths": [],
"skipped_directories": 0,
"skipped_directory_paths": [],
"skipped_directories_outside_repository": 0,
"max_files": 3000,
"max_files_reached": false
},
"abstention": {
"default_enabled": false,
"experimental": true,
"calibrated": false,
"threshold": 0.12,
"opt_in": "Pass enable_experimental_abstention=true to reponeedle_retrieve."
}
}The indexing object reports what the startup index excluded, so an agent can tell when a file it expects is missing:
skipped_paths/skipped_large_files: supported-extension files strictly larger thanmax_file_size_bytes(set with--max-file-size-bytes). A file exactly at the limit is indexed.unreadable_paths/skipped_unreadable_files: files that could not be read (including broken links and link loops).symlink_outside_repository_paths/skipped_symlink_outside_repository: in-repository links whose target resolves outside the repository root. Their targets are never read; only the link path is listed.skipped_directory_paths/skipped_directories: directories that were not traversed. Directory symlinks and Windows junctions are never followed (files under a link target inside the repository are indexed once, under their real path), and a physical directory is never entered twice, so link cycles cannot cause repeated traversal.skipped_directories_outside_repositorycounts the links that resolve outside the repository root (or cannot be resolved); their contents are never listed or read.max_files_reached:trueif indexing stopped at themax_filescap.
Paths are repository-relative with forward slashes and sorted. File contents are never included. Files excluded by .gitignore, built-in ignore patterns, or unsupported extensions are excluded by design and are not listed. Diagnostics describe the index built at server startup; restart the server to refresh them.
RepoNeedle includes a heuristic that can abstain (return no files) when a query appears to be out of scope for the repository. It is experimental and disabled by default:
- Why it is experimental: on the official Agent Retrieval Bench v2 selective split it correctly abstained on 0 of 82 negative samples (100% false returns). Natural issues and wrong-repository queries share enough vocabulary with real code that the overlap, peakiness and margin signals do not separate them.
- Why it is disabled by default: leaving an uncalibrated filter on suggests that out-of-scope queries are being caught when they are not. With it disabled, agents should treat every result as candidate context, not as proof that the repository contains the answer.
- How to enable it: pass
"enable_experimental_abstention": truetoreponeedle_retrieve. The returnedabstention.statusis then"evaluated", and an abstained response hasstatus: "abstained", emptyselected_filesand emptyrendered_context. task_type: "selective"does not enable it. The task label only changes routing weights.reponeedle_change_impactnever applies the heuristic.- An empty repository always yields
abstained: truewithabstention.status: "empty_repository", independent of the flag.
The RepoNeedle MCP server executes locally with read-only access to the configured repository. It enforces strict boundary controls:
- Explicit Root Configuration: The repository root is fixed at server launch (
--repo). Tool calls cannot specify or switch to arbitrary directories. - Path Traversal Protection: Tool path arguments (e.g.
anchor_files) whose path climbs above the repository root (a leading..path component) or that point outside it are rejected with descriptive validation errors. Names that merely start with..(e.g...cache/x.py) are legitimate and accepted. - Symlink Escape Prevention: Tool path arguments are resolved to their canonical location with
os.path.realpath()and rejected if that lies outside the repository root. Indexing applies the same rule: every file is resolved before it is opened, files whose canonical location is outside the repository (symlinks; junctions on Windows) are skipped without reading the target, and only the in-repository link path is recorded (symlink_outside_repository_paths, reasonsymlink_outside_repository). Links that resolve inside the repository are indexed normally. - Zero File Modification: The server operates strictly read-only. It contains no file write, edit, or delete capabilities.
- Credential Safety: Diagnostics, error messages, and inspection metadata never expose environment variables, API tokens, or secrets.
- Startup Ingestion: The server parses repository ASTs and builds inverted postings indices once upon startup.
- In-Memory Query Re-use: Subsequent tool calls execute against the pre-computed in-memory index, avoiding redundant disk scans. Query latency depends on repository size (small test repositories: a few milliseconds; ARB v2 real repositories: ~142 ms median).
- No Persistent Cache or File Watching: The index is not persisted to disk and the server does not watch the filesystem; both are not implemented.
- Refreshing the Index: When source files on disk change substantially, restart the MCP server process (or restart Claude Code / reload Cursor window) to rebuild the index.
If MCP support is no longer needed:
pip uninstall mcpRepoNeedle's core CLI (reponeedle) and Python API will continue functioning normally with zero missing dependency warnings.