quick-search
A FastMCP server for bounded, keyword-driven repository context search, enabling efficient codebase exploration with snippet extraction and focused code block retrieval.
README
quick-search
quick-search is a FastMCP server for bounded, keyword-driven repository context search. It is designed for broad codebase exploration when a client needs likely-relevant files and small, targeted snippets instead of reading whole files.
The server exposes two MCP tools:
search_repo_contextsearch_focused_context
It is intended for agent workflows such as:
- finding the most relevant implementation files for a feature or bug
- surfacing symbol definitions before reading full files
- keeping context windows small during repository search
- falling back to normal search only when bounded retrieval is not enough
Recommended agent workflow:
- Call
search_repo_contextwithoutput_mode="compact"andinclude_diagnostics=false. - Keep the returned
query_id. - If the result identifies promising files but you need more ranking or query metadata, call the tool again with
query_id=<prior id>andoutput_mode="full". - If the relevant context is spread across a few files, call
search_focused_contextwith that samequery_idto retrieve full enclosing definitions instead of snippets. - If you already know the specific keyword you want and want enclosing definitions directly, call
search_focused_contexton its own withkeywords. - If you already know both the keyword and the exact files, call
search_focused_contextwithkeywordsandfile_pathsto skip broad ranking entirely. - If the result is empty or surprising, call the broad tool again with
query_id=<prior id>andinclude_diagnostics=true. - Once the search is narrowed, switch to Serena or normal file reads.
What It Does
For each request, quick-search:
- discovers candidate files under the target directory
- skips obvious noise such as generated files, binaries, vendored code, and very large files
- counts keyword hits per file
- boosts likely source files and symbol-definition matches
- ranks files with an explainable score
- returns bounded snippet windows around strong matches
- optionally returns full enclosing multi-file code blocks in a second focused step
The result is compact structured JSON with:
- summary counts
- ranked files
- snippet excerpts
- focused code blocks when requested
- short usage guidance for the calling agent
Registering The MCP
You might need to install uv for this to work. Then download this repository. And either edit the config.toml yourself or use the one line commands below. Running it with uv is highly recommended.
Codex
Direct Python launch:
With uv:
codex mcp add quick-search -- \
uv run --directory path/to/repository python main.py
codex mcp add quick-search -- \
path/to/repository/.venv/bin/python \
path/to/repository/main.py
Useful checks:
codex mcp list
codex mcp get quick-search
Claude Code
Local scope:
claude mcp add quick-search --scope local -- \
uv run --directory path/to/repository python main.py
Project scope:
claude mcp add quick-search --scope project -- \
uv run --directory path/to/repository pyhton main.py
Equivalent .mcp.json entry:
{
"mcpServers": {
"quick-search": {
"command": "uv",
"args" = ["run", "--directory", "/home/leibersp/code/quick-mcp", "python","main.py"],
"env": {}
}
}
}
Configuration
You can configure default retrieval budgets at MCP registration time with environment variables:
QUICK_SEARCH_MAX_FILES=20
QUICK_SEARCH_MAX_SNIPPETS=30
QUICK_SEARCH_MAX_TOTAL_LINES=500
QUICK_SEARCH_LOG_LEVEL=INFO
Behavior:
QUICK_SEARCH_MAX_FILESsets the defaultmax_filesQUICK_SEARCH_MAX_SNIPPETSsets the defaultmax_snippetsQUICK_SEARCH_MAX_TOTAL_LINESsets the defaultmax_total_linesQUICK_SEARCH_LOG_LEVELcontrols Python logging verbosity
Per-call tool arguments still override the retrieval defaults.
Example Codex registration with configured defaults:
codex mcp add quick-search \
--env QUICK_SEARCH_MAX_FILES=20 \
--env QUICK_SEARCH_MAX_SNIPPETS=30 \
--env QUICK_SEARCH_MAX_TOTAL_LINES=500 \
--env QUICK_SEARCH_LOG_LEVEL=INFO \
-- \
path/to/repository/.venv/bin/python \
path/to/repository/main.py
Retrieval Behavior
The implementation follows the broad-context retrieval plan in broad-context-mcp-plan.md.
Current behavior includes:
cwdsearch by default, with optional explicitdirectory- optional
subpathrestriction to a subtree or single file withindirectory - optional include/exclude glob filters on candidate file paths
match_modewithsubstring,word, andidentifierbehavior- bounded keyword expansion from the provided
keywords output_modewith compact-by-default responses- optional
include_diagnosticsfor backend and exclusion summaries rg --filesdiscovery when available, with Python fallbackripgrepkeyword collection when available, with Python fallback- source-file preference over docs/config when scores are similar
- definition-hit preference for common source languages using regex heuristics
- overlap-aware snippet window merging
- hard budgets on returned files, snippets, and total lines
The server intentionally does not do embeddings, AST parsing, or semantic reranking in v1.
Defaults
Default retrieval limits:
max_files = 12max_snippets = 8lines_before = 24lines_after = 40max_total_lines = 1200
Internal implementation defaults also include:
max_snippets_per_file = 3max_file_size_bytes = 1_000_000
Tool Contract
search_repo_context
Input
search_repo_context accepts:
{
"keywords": ["snow", "albedo", "melt"],
"query_id": null,
"directory": "/path/to/repo",
"subpath": "src/model",
"paths_include_glob": "src/**/*.py",
"paths_exclude_glob": "**/tests/*",
"match_mode": "substring",
"output_mode": "compact",
"include_diagnostics": false,
"max_files": 12,
"max_snippets": 8,
"lines_before": 24,
"lines_after": 40,
"prefer_source_files": true,
"max_total_lines": 1200
}
Parameter notes:
- provide at least one non-empty
keyword - prefer specific phrases or identifiers over very general terms such as
snowwhen possible query_idmay be used instead of rerunning the same search when you only want a differentoutput_modeor diagnostics view- provided
keywordsare preserved first; additional generalized terms may be added up to a bounded limit - ranking gives much more weight to matches on the original input keywords than to expansion-only matches
directoryshould be an absolute path for reliable agent behaviorsubpathis optional and must be relative todirectorysubpathmay point to either a directory or a single filepaths_include_globandpaths_exclude_globfilter candidate file paths relative todirectory- if both glob filters are provided, the exclude glob wins
match_modedefaults tosubstringworduses word-boundary matchingidentifiermatches identifier tokens such assnow_modelandsnowModeloutput_modedefaults tocompact- use
output_mode="full"when you need richer ranking, snippet, and query metadata include_diagnosticsdefaults tofalse- relative paths such as
.are not portable across MCP clients and only work when the client exposes roots - if you are calling this tool from an agent, do not rely on
.meaning the agent's current directory; pass an absolutedirectory - if
directoryis omitted, the server falls back to its own process working directory unless exactly one client root is exposed max_files,max_snippets, andmax_total_linesmust be>= 1lines_beforeandlines_aftermust be>= 0- per-call values override configured environment defaults
Output
Typical output shape:
{
"query_id": "9e0a4f3f8e6b2c1d",
"searched_directory": "/path/to/repo",
"summary": {
"files_considered": 1832,
"files_ranked": 47,
"files_returned": 12,
"snippets_returned": 8,
"budget_truncated": true
},
"query": {
"keywords": ["snow melt balance"],
"resolved_keywords": ["snow melt balance", "snow", "melt", "balance", "snow melt", "melt balance"]
},
"ranked_files": [
{
"path": "src/model/snow_energy_balance.jl",
"category": "source",
"keyword_hits": 18,
"distinct_keywords_matched": 3,
"definition_hits": 2,
"score": 0.94,
"recommended_read": true,
"reason": "high hit count, multiple keywords, source file, definition hits"
}
],
"snippets": [
{
"path": "src/model/snow_energy_balance.jl",
"line_start": 120,
"line_end": 140,
"matched_keywords": ["snow", "melt"],
"snippet": "..."
}
],
"usage_guidance": "Prefer source files over docs when scores are similar. Prefer files with multiple distinct keywords and clustered hits. Use the snippets to decide which files deserve deeper reading. Only do further searches across the repository if this context is insufficient."
}
Ranking Signals
Ranking is explainable rather than opaque. Signals include:
- total keyword hits
- number of distinct keywords matched
- source-file bonus
- keyword density
- likely definition hits
- penalties for tests, generated files, vendor code, and similar noise
The ranked file output also exposes several of these internal signals directly so an agent can decide the next Serena call without recomputing them.
In compact mode this stays lean:
categorykeyword_hitsdistinct_keywords_matcheddefinition_hits
In full mode additional fields are included:
line_countis_source_fileis_test_filedefinition_hitskeyword_density
Match Modes
quick-search supports three matching modes:
substring- current behavior
- matches query text anywhere in the line
word- requires word boundaries
- avoids matching
snowinsidesnowpackorsnow_model
identifier- matches identifier-like tokens in code
- matches
snowinsidesnow_modelandsnowModel - does not match
snowinsidesnowfall
Keyword Expansion
quick-search works from explicit keywords only.
It broadens those keywords in a bounded way so searches are not limited to exact literal matches:
- keeps the original keyword first
- extracts useful identifier-like tokens
- preserves some short adjacent phrases
- caps the final resolved keyword set
Expansion terms are meant as recall helpers, not primary ranking signals. Files that match the original input keywords should rank ahead of files that mostly match only generalized terms.
The original and resolved keywords are returned in the query object so the
behavior is transparent to the caller.
In compact mode, query contains:
keywordsresolved_keywords
In full mode, the same keys are returned.
Output Modes
quick-search now defaults to output_mode="compact" to reduce MCP response size.
Use compact when:
- you only need the top candidate files and snippets
- the tool is feeding another retrieval step
- context budget matters
Use full when:
- you are tuning retrieval quality
- you want ranking metadata for debugging or evaluation
- you want to inspect how your input keywords were generalized
- you plan to immediately inspect only a small number of returned files
Diagnostics
Set include_diagnostics=true to include a compact diagnostics block with:
- discovery backend
- matching backend
- excluded file counts by reason
This is off by default so normal agent calls stay small.
Compact-First Pattern
The intended call pattern for agents is:
- Run a compact query first.
- Keep the returned
query_id. - If needed, call the tool again with that
query_idand:output_mode="full"for richer metadatainclude_diagnostics=truefor debugging
This gives you a cheap first pass without committing to the larger payload every time or recomputing the search.
Cached Expansion
Every successful search response includes a query_id.
That id refers to an in-process cached full result:
- the first call can use
output_mode="compact" - a later call can reuse
query_idwithoutput_mode="full" - a later call can reuse
query_idwithinclude_diagnostics=true
This avoids recomputing the same search just to retrieve richer metadata.
This means a source file with a relevant def, class, function, struct, or similar declaration can outrank a doc file with many incidental mentions.
search_focused_context
Use this only after search_repo_context when the relevant code is spread across
multiple files and line snippets are no longer enough.
Input:
{
"query_id": "9e0a4f3f8e6b2c1d",
"keywords": null,
"directory": null,
"file_paths": null,
"max_files": 3,
"max_blocks": 6,
"max_blocks_per_file": 2,
"max_total_lines": 400
}
Parameter notes:
- provide either
query_idor at least one non-emptykeyword - use
query_idwhen this is the second stage aftersearch_repo_context - use
keywordsplusdirectorywhen you want focused retrieval directly without a prior broad-search call - use
keywordsplusfile_pathswhen you want to skip broad ranking and search only specific files - omit
file_pathsto let the tool pick the strongest source files from the broad search - use
file_pathswhen you want to constrain the focused pass to specific files from the broad result or standalone focused search - this tool is intended for multi-file code context, not broad discovery
- it returns full enclosing definitions when possible and avoids returning whole files
Typical output shape:
{
"query_id": "9e0a4f3f8e6b2c1d",
"searched_directory": "/path/to/repo",
"query": {
"keywords": ["snow melt balance"],
"resolved_keywords": ["snow melt balance", "snow", "melt", "balance"]
},
"summary": {
"candidate_files_considered": 3,
"files_with_context": 2,
"blocks_returned": 4,
"budget_truncated": false
},
"candidate_files": ["src/snow.py", "src/energy.py"],
"blocks": [
{
"path": "src/snow.py",
"block_type": "function",
"signature": "def snow_melt_balance():",
"line_start": 12,
"line_end": 24,
"matched_keywords": ["balance", "melt", "snow"],
"match_lines": [12, 13, 18],
"score": 1.0,
"content": "def snow_melt_balance():\n ..."
}
]
}
This second-stage tool is best when:
- the broad search already found the right files
- the relevant logic is distributed across a small number of files
- you need complete functions or classes, not line windows
- you want to avoid falling back immediately to unrestricted pattern search
Direct-file mode is best when:
- you already know the exact files to inspect
- file ranking and broad discovery are unnecessary overhead
- you still want enclosing functions or classes rather than whole files
Bounded Results
The result is intentionally not exhaustive.
quick-search returns the top ranked files and bounded snippets only. It does not return every file that was eligible for ranking, and it does not return filtered files that were skipped during discovery.
If you need broader coverage, raise the budgets and only do further searches across the repository if this context is insufficient.
Scoped Search
You can narrow the ranking pass before matching and snippet extraction:
- use
subpathto search only inside a subtree - use
subpathto search a single known file - use
paths_include_globto keep only specific candidate file patterns - use
paths_exclude_globto remove low-value areas such as tests or fixtures
Example:
{
"keywords": ["snow", "melt"],
"directory": "/repo",
"subpath": "src/physics",
"paths_include_glob": "src/physics/*.py",
"paths_exclude_glob": "src/physics/test_*"
}
Directory Resolution
For agents, the safe contract is simple: always pass an absolute repository path.
Relative paths are not reliable because MCP servers do not automatically know the caller's current working directory. Some clients expose workspace roots and let quick-search resolve . against those roots, but others do not. In those clients, a relative path will fail rather than silently searching the wrong directory.
Use relative paths only if you control the client and know it exposes roots. Otherwise, pass an absolute directory.
Running The Server
With uv
From the repository root:
UV_CACHE_DIR=/tmp/uv-cache uv run quick-search
If your local uv setup is stable, this is the simplest command.
With the Local Virtual Environment
If you want a more explicit launch command:
path/to/repository/.venv/bin/python path/to/repository/main.py
This is often the safer registration target for MCP clients because it avoids extra resolver and cache behavior at process startup.
Transport
The server defaults to stdio transport:
MCP_TRANSPORT=stdio
That is the correct default for Codex and Claude Code local MCP registration.
Logging
quick-search emits meaningful runtime logs to stderr. This is important because stdout is reserved for MCP protocol traffic.
Each search logs:
- target root directory
- query keywords
- files considered, ranked, and returned
- snippets returned
- whether budgets truncated the result
- top-ranked files
- per-file snippet coverage as
shown_lines=<returned>/<total> (<percent>%)
Example log lines:
2026-04-07 13:45:45,123 INFO repo_context_search: repo search root=/repo keywords=snow, melt files_considered=3 files_ranked=2 files_returned=2 snippets_returned=1 budget_truncated=False
2026-04-07 13:45:45,124 INFO repo_context_search: rank=1 path=src/model.py score=1.000 hits=5 distinct_keywords=2 definition_hits=1 snippets=1 shown_lines=12/240 (5.0%)
If you want quieter output, set:
QUICK_SEARCH_LOG_LEVEL=WARNING
Development
Project Layout
- main.py: process entrypoint and logging setup
- quick_search.py: FastMCP server and tool registration
- repo_context_search.py: retrieval, ranking, and snippet extraction engine
- tests/test_repo_context_search.py: unit tests
- broad-context-mcp-plan.md: implementation plan
Install / Sync
If needed:
uv sync
Run Tests
.venv/bin/python -m unittest -v tests.test_repo_context_search
or:
.venv/bin/python -m unittest discover -s tests
Compile Check
.venv/bin/python -m py_compile quick_search.py repo_context_search.py main.py
Verified Behaviors
The test suite currently covers:
- default search in
cwd - source-file preference
- definition-hit preference
- subtree restriction
- include/exclude glob filtering
wordandidentifiermatching modes- keyword expansion from explicit keywords
- compact and full output modes
- focused multi-file context extraction with full Python blocks
- optional diagnostics output
- overlapping snippet merge
- total line budget enforcement
- binary, generated, and large-file exclusion
- no-match behavior
- docs-only matches
- environment-driven default budgets
- logging of ranked files and snippet coverage
Limitations
Current limitations are intentional:
- exact keyword matching only
- no semantic search or embeddings
- no AST parsing
- definition detection is heuristic, not language-complete
- retrieval is optimized for bounded context, not exhaustive code intelligence
If the returned context is insufficient, the intended client behavior is to only do further searches across the repository when this context is insufficient.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.