fw-context-mcp
Exposes tools for AI assistants to query a persistent SQLite+FTS5 index of C/C++ symbols parsed from real build commands, enabling sub-millisecond lookup, full-text search, and natural-language explanation without hallucination.
README
fw-context
Build-aware code intelligence for embedded firmware — a tool that lets AI
assistants understand your C/C++ codebase without hallucinating function
names, missing overloads, or guessing which #ifdef branch is active.
What it does
fw-context parses your actual build (compile_commands.json) with
libclang — the same parser your IDE uses — and
stores every C/C++ symbol in a full-text-searchable SQLite + FTS5 database
on disk. An MCP server then exposes this index as tools that AI assistants
(Claude Code, OpenCode) can call directly: sub-millisecond lookups, zero
hallucination, real C/C++ understanding.
Once indexed, your AI assistant can answer questions like:
"What does
modem_parser_oob_initdo and who calls it?""Find all functions related to BLE pairing failure handling."
"Show me the definition of
ZCfgDataManager— not the declaration, the actual implementation.""Is the index up to date, or did I forget to re-index after my last edit?"
"Where is
reset_slot_error_lockused across the entire project?"
The assistant resolves these by querying the index — no grepping through files, no guessing from training data, no reading 8000 Mbed OS headers into context.
Why not just use LSP?
LSP servers (clangd, ccls) are designed for interactive editing — go-to-definition, autocomplete, hover info. They're excellent at that. But they have limitations for AI-assisted code exploration:
- No full-text search across the codebase. LSP can find a symbol by name, but not "all functions related to modem initialization."
- No persistent index. The LSP index lives in memory; restart the server and it rebuilds from scratch. fw-context's index is a SQLite file — it survives reboots, and your AI assistant reads it in milliseconds.
- No AI-oriented interface. LSP speaks a protocol designed for editors, not for AI agents. fw-context's MCP tools are purpose-built for the assistant's workflow: lookup, search, explain, re-index.
- Grep is blind to build context.
grep -rdoesn't know which translation units are actually compiled, which#ifdefbranch is active, or whether a match is a definition or just a forward declaration. fw-context knows all three.
fw-context doesn't replace LSP — it complements it. Use clangd for editing, fw-context for AI-assisted exploration.
Three components
| Component | Runs as | Purpose |
|---|---|---|
CLI (fw-context) |
User command | Index build, status checks, project management |
| Indexer | Called by CLI | libclang parses every translation unit from compile_commands.json, extracts symbols with qualified names, signatures, and locations, stores them in SQLite + FTS5 |
MCP server (fw-context-mcp) |
Subprocess started by AI assistant | Exposes 12 tools over JSON-RPC 2.0 (stdin/stdout); optionally calls a local Ollama model for natural-language search and symbol explanation |
Key characteristics
- Zero daemon. No background process — the index is a file on disk that the MCP server reads on-demand. Each tool call opens the DB, runs a query, and closes.
- Ollama is optional.
explain_symbolandsmart_searchcan use a local LLM for natural-language queries, but when Ollama is disabled, the AI assistant processes results with its own model. Everything else works without any LLM at all. - Incremental by default. After the first index, only files with changed
modification time are re-parsed. Editing one file and running
fw-context indextakes under a second. - Auto-reindex on query.
search_codeandlookup_symboldetect stale files in their results and re-index them on the fly — so the typical edit→search workflow requires no manual steps. - Offline-first. The index lives at
~/.fw-context/index/. No cloud account needed. Ollama runs locally if you choose to use it.
Supported ecosystems
Works with any embedded build system that produces compile_commands.json:
Mbed OS, Zephyr RTOS, PlatformIO (Arduino, ESP-IDF, STM32Cube),
FreeRTOS, bare-metal ARM, and anything else compiled with GCC or Clang.
No LSP server, no c_cpp_properties.json, no manual configuration — it uses
the real compiler flags, so it sees what your compiler sees, #ifdefs and all.
First indexing depends on project size — from seconds for small bare-metal
projects (50–200 files) to 15–20 minutes for large Mbed OS codebases with
thousands of framework headers pulled in through #include. Subsequent runs
are incremental — only changed files are re-parsed, so they finish in
seconds.
Supported ecosystems
fw-context auto-detects your build system and source roots from the project
structure. The only hard requirement is compile_commands.json — if your
toolchain can produce one, fw-context can index it.
| Ecosystem | Auto-detection | Typical indexed scope | Index time (first run) | Notes |
|---|---|---|---|---|
| Mbed OS | mbed-os/, mbed_app.json |
2 000–9 000 files<br>20 000–80 000 symbols | 5–20 min | Framework + your code; #include chain pulls in large parts of mbed-os |
| Zephyr RTOS | west.yml, prj.conf |
1 000–15 000 files<br>10 000–100 000+ symbols | 3–20 min | West workspace includes modules, drivers, subsystems |
| PlatformIO | platformio.ini |
500–10 000 files<br>5 000–80 000 symbols | 1–15 min | Arduino (~500 files), ESP-IDF (~10 000), STM32Cube, Teensy… |
| Bare-metal / FreeRTOS | Any build with bear |
50–2 000 files<br>500–20 000 symbols | 5 s–3 min | No OS framework overhead; only your code + RTOS kernel headers |
| Custom / in-house RTOS | Any build with bear |
Scales to 10 000+ files<br>100 000+ symbols | 10–30 min | Proprietary toolchains that emit compile_commands.json work too |
The indexer auto-detects source directories (src, lib, app, include,
drivers, modules, zephyr, mbed-os) from your project structure and
compile_commands.json entries. Framework symbols your code actually
#includes are indexed automatically — no manual configuration needed.
What problem it solves
AI coding assistants are great at Python, JavaScript, and Go — languages with mature LSP servers, static analysis, and training data. Embedded firmware is different:
- Proprietary codebases the model has never seen — no training data.
- Massive dependency trees — Mbed OS ships ~8 000 C++ files, Zephyr workspace contains 15 000+ source files across subsystems and drivers, and ESP-IDF adds ~10 000 more. Too large to read into context.
- Build-time macros and
#ifdefs that change which code is actually compiled. - Custom build systems (
mbed compile,west build,pio run) whose include paths, defines, and compiler flags are opaque until you run them.
Without an index, the assistant resorts to grep — slow, imprecise, and blind to which translation units are actually part of the build. It hallucinates function names, misses overloads, and can't tell a definition from a declaration.
fw-context solves this by parsing your actual build. It reads
compile_commands.json (the exact same compilation database your build system
produces), parses every translation unit with libclang (the same parser as
your IDE), and stores the extracted symbols in a SQLite + FTS5 database on
disk. Your AI assistant then queries this database through an MCP server —
sub-millisecond lookups, zero hallucination, real C/C++ understanding.
How it works, at a glance
1. Your build system 2. fw-context index 3. AI assistant
┌──────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ bear / west / pio │ ──→ │ libclang parses │ ──→ │ lookup_symbol(…) │
│ compile_commands │ │ every .c/.cpp in TU │ │ search_code(…) │
│ .json │ │ SQLite + FTS5 db │ │ explain_symbol(…)│
└──────────────────┘ └─────────────────────┘ └──────────────────┘
Key technologies
| Tool | Role |
|---|---|
| libclang | C/C++ parser — traverses AST for each translation unit, extracts symbols with their qualified names, signatures, docstrings, and location. Uses the exact compiler flags from compile_commands.json (include paths, defines, standards) so it sees what the compiler sees. |
| SQLite + FTS5 | Storage and full-text search. The symbols table stores name, kind, signature, file/line, docstring, and definition-vs-declaration flag. The FTS5 index covers 6 columns: name (original C++ name), qualified_name (full namespace::class::method), signature (parameter types), docstring (documentation comments), file_path (relative path from project root, for module context), and name_tokens (camelCase/snake_case split for sub-token search — e.g. onConnectionComplete → on connection complete). FTS5 enables fast prefix/phrase/keyword queries without loading entire files. |
| MCP SDK | JSON-RPC 2.0 server framework. Handles protocol initialization, message framing, and tool registration. The server is stateless between calls — each tool invocation opens the DB, runs the query, and closes. |
| httpx | Async HTTP client for calling Ollama's REST API (/api/chat, /api/tags). Used by smart_search and explain_symbol. |
| Ollama (optional) | Local LLM runtime. Two models are used: qwen2.5-coder:14b for natural-language-to-FTS5 translation, query understanding, and symbol explanation; mxbai-embed-large:latest for semantic embedding search (Phase 4 — cosine similarity between query and 15K+ symbol descriptions). Both models auto-pull on first use. When disabled, the AI assistant processes results with its own LLM — no Ollama required. |
bear |
LD_PRELOAD-based build interception. Wraps your build command (bear -- python3 build_app.py) to produce compile_commands.json. Required once per build config change. |
What gets indexed
The indexer extracts every definition and declaration from the translation
units in compile_commands.json:
| Symbol kind | Example |
|---|---|
| Function | void uart_init(int baudrate) |
| Method | bool Modem::connect(const char *apn) |
| Constructor / Destructor | BleManager::BleManager() |
| Class / Struct | class BoxManager { … } |
| Enum | enum class State { IDLE, ACTIVE } |
| Enum constant | State::IDLE |
| Typedef / Using | using Callback = void(*)(int); typedef uint32_t tick_t |
| Variable / Field | int _counter; static constexpr size_t BUFFER_SIZE |
| Namespace | namespace zbox { … } |
All have qualified names (e.g. zbox::BleManager::start_advertising),
signatures, and file + line locations.
Each symbol is stored in the FTS5 index with 6 searchable columns:
| FTS5 column | Content |
|---|---|
name |
Original C++ name |
qualified_name |
Full namespace::class::method |
signature |
Parameter types |
docstring |
Documentation comments |
file_path |
Relative path from project root (module context) |
name_tokens |
camelCase/snake_case split — onConnectionComplete → on connection complete |
Source roots are auto-detected from your project structure (src, lib, app,
include, modules, zephyr, mbed-os) and compile_commands.json entries —
OS and framework symbols your project actually #includes are indexed
automatically. No manual configuration needed.
Quick start
# 1. Clone and install
git clone git@github.com:turbyho/fw-context-mcp.git ~/.fw-context/src
# or from the primary server:
# git clone git@git.montyho.com:turbyho/fw-context-mcp.git ~/.fw-context/src
uv venv ~/.fw-context/.venv --python 3.12
uv pip install --python ~/.fw-context/.venv/bin/python ~/.fw-context/src/
echo 'export PATH="$HOME/.fw-context/.venv/bin:$PATH"' >> ~/.bashrc
# 2. Register with your AI assistant
fw-context init
# 3. Generate compile_commands.json, then index
cd your-firmware-project
bear --output compile_commands.json -- python3 build_app.py --profile release # Mbed OS
fw-context index compile_commands.json
# Set as default for the project:
echo 'compile_commands = "compile_commands.json"' >> .fw-context/config.toml
# Done. Restart your AI assistant and start asking it about your code.
Installation & setup
For detailed prerequisites, installation steps, Ollama setup, and AI assistant integration, see Installation guide.
The short version:
git clone git@github.com:turbyho/fw-context-mcp.git ~/.fw-context/src
uv venv ~/.fw-context/.venv --python 3.12
uv pip install --python ~/.fw-context/.venv/bin/python ~/.fw-context/src/
echo 'export PATH="$HOME/.fw-context/.venv/bin:$PATH"' >> ~/.zshrc
fw-context init
Day-to-day workflow
compile_commands.json ──→ fw-context index ──→ AI assistant tools
↑ │
bear / west / pio │
│ lookup · search · explain
your build system
1. Generate compile_commands.json
Mbed OS:
bear -- python3 build_app.py --profile release --type DEV
Zephyr:
west build -b <board> -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
fw-context index build/compile_commands.json
PlatformIO:
pio run --target compiledb
2. Index the project
Run from the project root:
fw-context index
First run depends on project size — small projects finish in seconds, large Mbed OS codebases with 50 000+ symbols can take 15–20 minutes. Subsequent runs are incremental — only files with changed modification time are re-parsed.
Source roots are auto-detected by default. The indexer scans your project
for common source directories (src, lib, app, include, modules) and
OS/framework directories (zephyr, mbed-os), then supplements with
directories discovered from compile_commands.json. This means OS symbols
your project actually uses are indexed automatically — no manual config needed.
# Custom source roots (override auto-detection)
fw-context index --source-roots src lib vendor
# Index a different project
fw-context index --project /path/to/other/project
# Verbose output — shows per-file progress
fw-context index -v
3. Use with your AI assistant
Once indexed, your assistant can:
- "What does
modem_parser_oob_initdo?" →lookup_symbol+explain_symbol - "Find functions related to BLE advertising" →
search_codeorsmart_search - "Is the index up to date?" →
get_active_build - "I changed
main.cpp, re-index it" →reindex_file
4. Keep the index current
After editing source files, the index stays accurate in three ways:
- Automatic re-index —
search_codeandlookup_symboldetect stale files in their results and re-index them on the fly (up to 5 files, 30 s timeout), then re-run the query. No manual steps needed for typical edit→search workflows. - Automatic staleness detection — if
compile_commands.jsonitself changed, or auto-reindex failed, tools return a clear warning with next steps. - On-demand re-index — run
fw-context index(incremental, fast) or usereindex_file("src/main.cpp")via the MCP tool.
# Check if the index is stale
fw-context status
# Re-index changed files only
fw-context index
# Fresh start (after toolchain change, compiler upgrade, etc.)
fw-context reset
fw-context index
CLI & MCP tools
Full CLI reference, MCP tool catalog, and a detailed walkthrough of how each tool works internally: see Tools Reference.
Configuration
Complete configuration reference — global defaults, per-project overrides,
source root auto-detection, and all [index] / [llm] / [project] settings:
see Configuration.
Ollama & AI assistant setup
For Ollama installation, model selection (local and cloud), and AI assistant integration (Claude Code, OpenCode): see Installation guide.
Troubleshooting
"No index found"
Run fw-context index from the project root first. The index is per-project and
stored under ~/.fw-context/index/.
Index is stale
Run fw-context index — it's incremental and picks up only changed files.
Symbols missing or incomplete
By default source roots are auto-detected from your project structure and
compile_commands.json. If symbols from certain directories are missing:
- Check
.fw-context/config.toml— explicitsource_rootsoverrides auto-detection. Remove it to restore auto-detection, or add the missing directories. - Make sure the file is listed in
compile_commands.json. - For PlatformIO frameworks (Arduino, ESP-IDF) whose sources live outside
the project root (e.g.
~/.platformio/packages/), add them explicitly:source_roots = ["src", "lib", "/home/user/.platformio/packages/framework-arduinoespressif32"]
"Cannot connect to Ollama"
Ollama is optional. If you don't have it (no GPU, no cloud account), disable it in config:
# ~/.fw-context/config.toml
[llm]
enabled = false
Then explain_symbol returns the source code and prompt for the AI assistant
to answer itself, and smart_search falls back to direct keyword search —
no Ollama connection needed.
To use Ollama instead, install it from ollama.com and pull the models:
# LLM model — required for smart_search and explain_symbol
ollama pull qwen2.5-coder:14b
# Embedding model — required for semantic search (Phase 4)
ollama pull mxbai-embed-large:latest
Both models are auto-pulled on first use if not installed — but pre-pulling
avoids a long delay during the first smart_search call.
Then verify:
fw-context check_ollama
clang.cindex error / libclang not found
# Ubuntu/Debian
sudo apt install libclang-dev
# macOS
brew install llvm
echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
"No module named fw_context_mcp" after install
Make sure ~/.fw-context/.venv/bin is in your PATH:
export PATH="$HOME/.fw-context/.venv/bin:$PATH"
Reset after toolchain upgrade
Delete the old index and rebuild:
fw-context reset -y
fw-context index
Directory layout
~/.fw-context/
├── config.toml # global defaults (LLM model, db path)
├── .venv/ # Python virtual environment
│ └── bin/
│ ├── fw-context # CLI entry point
│ └── fw-context-mcp # MCP server entry point
└── index/
└── <project-id>/
└── index.db # SQLite database with FTS5 index
Project-level:
your-firmware/
├── .fw-context/
│ └── config.toml # per-project overrides (source roots, excludes)
└── compile_commands.json
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
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
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.