Local-first, read-only MCP server for Claude Code. Indexes and searches full-stack projects (FastAPI + React + PostgreSQL) with SQLite-backed FTS, cross-reference graph, and file-watcher auto-reload — so Claude reads only what it needs instead of entire files.
Step 1 — Install
# From local source (development)
cd C:\path\to\faststack-mcp
pip install -e .
# From GitHub
pip install "git+https://github.com/Evaan-devlops/faststack-mcp.git"
# With optional extras
pip install -e ".[watch]" # file watcher (watchfiles)
pip install -e ".[postgres]" # PostgreSQL backend (psycopg2-binary)
pip install -e ".[all]" # bothAlready have watchfiles / psycopg2 globally? Nothing extra to install — they're picked up automatically.
Step 2 — Add to Claude Code
{
"mcpServers": {
"faststack": {
"command": ".venv\\Scripts\\python.exe",
"args": ["-m", "faststack_mcp"]
}
}
}Step 3 — Index your project
index_folder("/path/to/your/project") → project_id + counts
index_folder("/path/to/project", watch=True) → + auto-reindex on file changes
Step 4 — Explore
get_project_outline(project_id) → structure at a glance (~17 tokens)
search_symbols(project_id, "createUser") → find functions, routes, hooks, models
get_symbol(project_id, symbol_id) → exact code snippet only
find_references(project_id, "get_db") → all files that import / call a symbol
get_file_context(project_id, "db.py", 120) → 80 lines around line 120
get_file_outline(project_id, "main.py") → all symbols in one file
search_text(project_id, "HTTPException") → fallback full-text search
get_file_tree(project_id) → directory tree
list_projects() → all indexed projects
invalidate_cache(project_id) → force full re-index
get_token_usage() → cost + per-session averages
| Tool | Purpose | Key params |
|---|---|---|
index_folder |
Scan & cache a project | path, force, watch, include_env_files |
list_projects |
Browse cached projects | — |
search_symbols |
Primary nav — ranked FTS symbol search | query, kind, language, include_synthetic |
get_symbol |
Read exact code snippet by symbol_id | symbol_id |
find_references |
New — import graph + text scan for a symbol | symbol_name, limit |
get_file_context |
New — read N lines around a line number | file_path, around_line, radius=40 |
search_text |
Full-text search across all files | query, limit |
get_project_outline |
Grouped structure overview | sections |
get_file_tree |
Directory tree of indexed files | — |
get_file_outline |
All symbols in one file | file_path |
invalidate_cache |
Remove cached index | project_id |
get_token_usage |
Session cost + tool breakdown | last_n, session_id |
find_references(project_id, "get_db")
→ {
symbol_name: "get_db",
files_found: 3,
total_usages: 3,
references: [
{ file_path: "users.py", usages: [{ line: 5, context: "from db import get_db" }] },
{ file_path: "auth.py", usages: [{ line: 12, context: "db = get_db()" }] },
{ file_path: "orders.py", usages: [{ line: 8, context: "from db import get_db" }] }
]
}
Use before modifying a shared symbol to understand blast radius. Uses the import graph built
during index_folder (fast path) with a whole-word regex scan as fallback.
get_file_context(project_id, "src/db.py", around_line=120, radius=40)
→ lines 80–160, line 120 marked with >>>
Use when get_symbol is too narrow — e.g. understanding the code around a symbol boundary,
inspecting a migration function, or reading error-handling context.
Controlled by FASTSTACK_STORAGE environment variable.
| Value | Default | When to use |
|---|---|---|
sqlite |
Yes | Local dev, any project size. Single ~/.faststack-mcp/faststack.db with FTS5. |
postgres |
No | Shared team index, large projects, or when you want the index in your project DB. Requires DATABASE_URL. |
json |
No | Backward compat with pre-v0.2 caches. No FTS or reference graph. |
No configuration needed. Features:
- FTS5 virtual table on symbol name + qualified_name + signature
- Combined FTS5 + LIKE fallback (handles
get_db,create_userunderscore names) refstable forfind_referenceslookups- WAL mode — concurrent reads while indexing
export FASTSTACK_STORAGE=postgres
export DATABASE_URL=postgresql://user:pass@localhost:5433/mydbFeatures:
GENERATED ALWAYS AStsvector column + GIN index (zero-maintenance FTS)ts_rankrelevance ordering + ILIKE supplement- Tables prefixed
faststack_*(no conflicts with your app tables) - Batch inserts via
execute_values(500 rows/page) faststack_referencestable forfind_references
psycopg2-binarymust be installed (pip install psycopg2-binaryorpip install faststack-mcp[postgres]).
# Start watcher alongside indexing
index_folder("/path/to/project", watch=True)- Uses
watchfiles(install:pip install watchfilesorpip install faststack-mcp[watch]) - 1.5 s debounce — rapid saves are batched before re-index fires
- Background daemon thread — does not block Claude
- Falls back silently if
watchfilesis not installed
Built automatically during index_folder. Tracks every import and from X import Y
statement across Python and TypeScript/JavaScript files.
The graph is stored in the index and queried by find_references. It enables:
- "What calls / imports this symbol?" in one tool call
- Impact analysis before refactoring shared utilities
- Understanding FastAPI dependency injection (
Depends(get_db))
Synthetic symbols (auto-generated model fields, config keys) are shown or hidden depending on context:
| Query kind | Default |
|---|---|
pydantic_model, model, type, interface |
Shown (fields are useful) |
config_key, config_file, tsconfig, json_key |
Shown |
| Everything else | Hidden |
Override explicitly: search_symbols(project_id, "User", include_synthetic=True)
get_token_usage(last_n=20)
→ {
totals: { input_tokens, output_tokens, estimated_cost_usd },
averages: { input_tokens_per_session, cost_usd_per_session },
tool_breakdown: { get_symbol: { calls: 18, est_saved: 136800 }, ... },
estimated_tokens_saved_by_mcp: 209000
}
Requires the Stop hook in ~/.claude/settings.json to log session data.
Source: .py .ts .tsx .js .jsx .sql
Config / meta: .json .jsonl .toml .yaml .yml .ini
Named files: package.json pyproject.toml tsconfig.json tsconfig.app.json
tsconfig.node.json vite.config.* alembic.ini eslint.config.js
tailwind.config.js tailwind.config.ts manifest.json chunks.jsonl
index.faiss bunfig.toml .env.example
Skipped dirs: .git node_modules .next dist build coverage .venv venv
__pycache__ .mypy_cache .pytest_cache .idea .vscode .claude
Sensitive files skipped: .env .env.* *.pem *.key *.p12 id_rsa id_ed25519
(.env* files opt-in via include_env_files=True)
| Language | Parser | Extracts |
|---|---|---|
| Python / FastAPI | ast |
functions, classes, routes, pydantic models, services, repos, decorators, Depends() |
| TypeScript / React | tree-sitter + regex fallback |
components, hooks, types, interfaces, backend routes/services/repos |
| SQL | regex | tables, views, indexes, functions, procedures, triggers |
| JSON / JSONL | json.loads |
root keys, RAG chunk fields, manifest metadata |
| Config | tomllib / json / yaml / ini |
package scripts/deps, tsconfig, vite, eslint, alembic |
| Env | regex (masked) | variable names — values stored as ***MASKED*** |
| Tailwind | heuristics | config keys, className usage |
- All file reads path-confined to the indexed project root
- Symlink escape checks on every lookup
- Binary file detection (null-byte scan)
- File size cap (default 2 MB)
- Sensitive file patterns skipped by default
- No write or shell operations on project files
- SQLite cache isolated at
~/.faststack-mcp/
# Local dev
pip install -e "C:\path\to\faststack-mcp"
# GitHub
pip install "git+https://github.com/Evaan-devlops/faststack-mcp.git"
# With extras
pip install -e ".[watch]" # watchfiles — file watcher
pip install -e ".[postgres]" # psycopg2-binary — PostgreSQL backend
pip install -e ".[all]" # both| Variable | Default | Description |
|---|---|---|
FASTSTACK_STORAGE |
sqlite |
Storage backend: sqlite, postgres, json |
DATABASE_URL |
— | PostgreSQL DSN (required when FASTSTACK_STORAGE=postgres) |
python -m venv .venv
.venv\Scripts\python -m pip install -e .
python -m py_compile src/faststack_mcp/server.py # syntax check
pytest tests/.env.exampleindexed by default with masked values (***MASKED***).envand.env.*skipped unlessinclude_env_files=True- Raw values never persisted