fw-context-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fw_context_mcp-0.1.0/.claude/settings.local.json +15 -0
- fw_context_mcp-0.1.0/.fw-context/config.toml +14 -0
- fw_context_mcp-0.1.0/.gitignore +25 -0
- fw_context_mcp-0.1.0/.lean-ctx/graph.db +0 -0
- fw_context_mcp-0.1.0/.lean-ctx/graph.meta.json +10 -0
- fw_context_mcp-0.1.0/PKG-INFO +215 -0
- fw_context_mcp-0.1.0/README-MCP.md +160 -0
- fw_context_mcp-0.1.0/README.md +195 -0
- fw_context_mcp-0.1.0/docs/configuration.md +111 -0
- fw_context_mcp-0.1.0/docs/installation.md +162 -0
- fw_context_mcp-0.1.0/docs/tools.md +572 -0
- fw_context_mcp-0.1.0/plans/2026-06-05-code-review-findings.md +412 -0
- fw_context_mcp-0.1.0/pyproject.toml +70 -0
- fw_context_mcp-0.1.0/server.json +25 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/__init__.py +3 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/cli.py +683 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/config/__init__.py +5 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/config/settings.py +231 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/config/tools.py +392 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/indexer/__init__.py +1 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/indexer/compile_commands.py +211 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/indexer/config_hash.py +72 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/indexer/db.py +1091 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/indexer/ops.py +164 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/indexer/runner.py +316 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/indexer/symbols.py +307 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/llm/__init__.py +0 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/llm/ollama.py +210 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/mcp/__init__.py +1 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/mcp/server.py +1251 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/__init__.py +12 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/cache.py +51 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/context.py +108 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/__init__.py +23 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/base.py +28 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/deduplicate.py +51 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/embedding.py +166 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/format.py +56 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/fts5_search.py +61 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/llm_query.py +121 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/refine.py +100 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/rough_search.py +87 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/phases/translate.py +52 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/pipeline.py +132 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/search/scoring.py +65 -0
- fw_context_mcp-0.1.0/src/fw_context_mcp/utils.py +47 -0
- fw_context_mcp-0.1.0/tests/__init__.py +0 -0
- fw_context_mcp-0.1.0/tests/conftest.py +118 -0
- fw_context_mcp-0.1.0/tests/test_compile_commands.py +209 -0
- fw_context_mcp-0.1.0/tests/test_config.py +121 -0
- fw_context_mcp-0.1.0/tests/test_config_hash.py +122 -0
- fw_context_mcp-0.1.0/tests/test_db.py +500 -0
- fw_context_mcp-0.1.0/tests/test_search_quality.py +477 -0
- fw_context_mcp-0.1.0/tests/test_server.py +254 -0
- fw_context_mcp-0.1.0/work/SMART_SEARCH_DEBUG.md +86 -0
- fw_context_mcp-0.1.0/work/fw_context.db +0 -0
- fw_context_mcp-0.1.0/work/plan-iterace3-opravy-vylepseni.md +281 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"mcp__fw-context__test_sampling",
|
|
5
|
+
"mcp__fw-context__search_code",
|
|
6
|
+
"mcp__lean-ctx__ctx_search",
|
|
7
|
+
"mcp__lean-ctx__ctx_read",
|
|
8
|
+
"mcp__lean-ctx__ctx_tree",
|
|
9
|
+
"mcp__lean-ctx__ctx_multi_read",
|
|
10
|
+
"mcp__lean-ctx__ctx_shell",
|
|
11
|
+
"WebSearch",
|
|
12
|
+
"mcp__fw-context__check_ollama"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
# name = "my-project" # defaults to directory name
|
|
3
|
+
|
|
4
|
+
[index]
|
|
5
|
+
compile_commands = "compile_commands.json"
|
|
6
|
+
source_roots = ["src", "lib"]
|
|
7
|
+
exclude_paths = ["build", "BUILD"]
|
|
8
|
+
|
|
9
|
+
# [llm]
|
|
10
|
+
# enabled = false # disable Ollama, return raw prompts for the agent to answer
|
|
11
|
+
# ollama_url = "http://localhost:11434"
|
|
12
|
+
# model = "qwen2.5-coder:7b-q4_K_M" # override global model for this project
|
|
13
|
+
# num_ctx = 8192
|
|
14
|
+
# debug_log = "~/.fw-context/llm-debug.jsonl" # write LLM prompts/responses to JSONL
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
.env
|
|
10
|
+
|
|
11
|
+
# fw-context runtime data
|
|
12
|
+
.fw-context/
|
|
13
|
+
|
|
14
|
+
.lean-ctx
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
19
|
+
|
|
20
|
+
# Tests
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
htmlcov/
|
|
24
|
+
.lean-ctx/
|
|
25
|
+
work/
|
|
Binary file
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fw-context-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Build-aware code intelligence MCP server for embedded C/C++ firmware (Mbed OS, Zephyr, PlatformIO)
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: c++,call-graph,code-intelligence,embedded,firmware,libclang,mcp
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: httpx>=0.27.0
|
|
9
|
+
Requires-Dist: libclang>=18.1.1
|
|
10
|
+
Requires-Dist: mcp[cli]>=1.0.0
|
|
11
|
+
Requires-Dist: sqlite-vec>=0.1.0
|
|
12
|
+
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
17
|
+
Provides-Extra: watch
|
|
18
|
+
Requires-Dist: watchfiles>=0.21; extra == 'watch'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# fw-context
|
|
22
|
+
<!-- mcp-name: io.github.turbyho/fw-context-mcp -->
|
|
23
|
+
mcp-name: io.github.turbyho/fw-context-mcp
|
|
24
|
+
|
|
25
|
+
**MCP server for embedded C/C++ firmware** — gives AI assistants (Claude Code,
|
|
26
|
+
Cursor, OpenCode, etc.) real understanding of your codebase. Parses your actual build
|
|
27
|
+
with [libclang](https://clang.llvm.org/), extracts every symbol, and builds a
|
|
28
|
+
persistent index with full-text search, call graph, and vector embeddings.
|
|
29
|
+
|
|
30
|
+
No hallucination. No grepping. No reading thousands of framework headers into context.
|
|
31
|
+
|
|
32
|
+
## What it does
|
|
33
|
+
|
|
34
|
+
Your AI assistant goes from guessing to knowing:
|
|
35
|
+
|
|
36
|
+
> *"What does `uart_init` do and who calls it?"*
|
|
37
|
+
> → `get_symbol_context("uart_init")` — body, callers, callees in one call.
|
|
38
|
+
>
|
|
39
|
+
> *"Find all BLE advertising functions and how they're connected."*
|
|
40
|
+
> → `search_code("ble advertising", kind="function")` → `find_call_path("gap_init", "start_advertising")`
|
|
41
|
+
>
|
|
42
|
+
> *"Show me the implementation of `adc_read` — not the declaration."*
|
|
43
|
+
> → `get_source("adc_read")` — exact body via libclang, no file reading.
|
|
44
|
+
>
|
|
45
|
+
> *"What would break if I change `spi_transfer`?"*
|
|
46
|
+
> → `find_all_callers_recursive("spi_transfer")` — every caller, direct and indirect.
|
|
47
|
+
>
|
|
48
|
+
> *"Give me a map of `modem_msg.cpp` before I read it."*
|
|
49
|
+
> → `get_file_map("src/modem_msg.cpp")` — 426 symbols grouped by kind.
|
|
50
|
+
|
|
51
|
+
**19 MCP tools** — symbol search, source reading, call-graph traversal, hotspot
|
|
52
|
+
analysis, dead code detection, vector search. All backed by real compiler flags
|
|
53
|
+
from `compile_commands.json` — `#ifdef`-aware, not grep.
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 1. Install
|
|
59
|
+
git clone git@github.com:turbyho/fw-context-mcp.git ~/.fw-context/src
|
|
60
|
+
uv venv ~/.fw-context/.venv --python 3.12
|
|
61
|
+
uv pip install --python ~/.fw-context/.venv/bin/python ~/.fw-context/src/
|
|
62
|
+
echo 'export PATH="$HOME/.fw-context/.venv/bin:$PATH"' >> ~/.zshrc
|
|
63
|
+
|
|
64
|
+
# 2. Register with your AI assistant
|
|
65
|
+
fw-context init
|
|
66
|
+
|
|
67
|
+
# 3. Generate compile_commands.json and index
|
|
68
|
+
cd your-firmware-project
|
|
69
|
+
|
|
70
|
+
# Mbed OS:
|
|
71
|
+
bear -- mbed compile --profile release
|
|
72
|
+
|
|
73
|
+
# Zephyr:
|
|
74
|
+
west build -b <board> -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
|
75
|
+
fw-context index build/compile_commands.json
|
|
76
|
+
|
|
77
|
+
# PlatformIO:
|
|
78
|
+
pio run --target compiledb
|
|
79
|
+
fw-context index
|
|
80
|
+
|
|
81
|
+
# CMake / Make:
|
|
82
|
+
bear -- make
|
|
83
|
+
# or: bear -- cmake --build build
|
|
84
|
+
|
|
85
|
+
fw-context index
|
|
86
|
+
|
|
87
|
+
# 4. Restart your assistant and start asking about your code
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
For detailed prerequisites, Ollama setup, and AI assistant integration:
|
|
91
|
+
**[Installation guide →](docs/installation.md)**
|
|
92
|
+
|
|
93
|
+
## Why not just use LSP?
|
|
94
|
+
|
|
95
|
+
LSP servers (clangd, ccls) are excellent for interactive editing.
|
|
96
|
+
But they have limitations for AI-assisted exploration:
|
|
97
|
+
|
|
98
|
+
| Limitation | fw-context solution |
|
|
99
|
+
|-----------|---------------------|
|
|
100
|
+
| No full-text search across the codebase | FTS5 over 6 columns — find "all functions related to modem init" |
|
|
101
|
+
| Index dies with the server — rebuild from scratch | Persistent SQLite file — survives reboots, reads in milliseconds |
|
|
102
|
+
| Editor protocol, not AI protocol | MCP tools purpose-built for AI assistant workflow |
|
|
103
|
+
| Blind to which `#ifdef` branch is active | Uses real compiler flags from `compile_commands.json` |
|
|
104
|
+
|
|
105
|
+
Use **clangd for editing**, **fw-context for AI-assisted exploration**.
|
|
106
|
+
|
|
107
|
+
## Architecture
|
|
108
|
+
|
|
109
|
+
### Data flow
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
BUILD INDEX QUERY
|
|
113
|
+
===== ===== =====
|
|
114
|
+
bear / west / pio libclang parses each TU AI assistant calls
|
|
115
|
+
cmake / make extracts symbols + refs MCP tools over
|
|
116
|
+
│ generates embeddings JSON-RPC (stdio)
|
|
117
|
+
▼ │ │
|
|
118
|
+
compile_commands SQLite db on disk lookup_symbol(…)
|
|
119
|
+
.json ~/.fw-context/index/ search_code(…)
|
|
120
|
+
│ │ find_callers(…)
|
|
121
|
+
▼ ▼ explain_symbol(…)
|
|
122
|
+
symbols + refs vec0 get_symbol_context(…)
|
|
123
|
+
(FTS5 index) (vector KNN) │
|
|
124
|
+
▼
|
|
125
|
+
AI assistant answers
|
|
126
|
+
your question about
|
|
127
|
+
the code
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Components
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
CLI (fw-context) MCP server (fw-context-mcp) Ollama (optional)
|
|
134
|
+
================ =========================== ==================
|
|
135
|
+
fw-context index exposes 19 tools over local LLM runtime
|
|
136
|
+
fw-context export JSON-RPC (stdio) HTTP :11434
|
|
137
|
+
fw-context watch │ │
|
|
138
|
+
fw-context status search_code ───────────── lookup smart_search ──▶ translates NL → FTS5 terms
|
|
139
|
+
fw-context reset lookup_symbol ─────────── prefix explain_symbol ─▶ explains function
|
|
140
|
+
fw-context init smart_search ──────────── NL embeddings ────▶ mxbai-embed-large
|
|
141
|
+
fw-context search get_file_map ──────────── file structure by kind
|
|
142
|
+
get_source ────────────── body
|
|
143
|
+
get_symbol_context ────── body+callers+callees
|
|
144
|
+
find_callers ──────────── direct callers
|
|
145
|
+
find_references ───────── all uses
|
|
146
|
+
find_call_path ────────── BFS in call graph
|
|
147
|
+
find_all_callers_recursive transitive callers
|
|
148
|
+
find_callees_recursive ── transitive callees
|
|
149
|
+
find_dead_code ────────── never called
|
|
150
|
+
find_hotspots ─────────── most-called
|
|
151
|
+
get_active_build ──────── index health
|
|
152
|
+
reindex_file ──────────── re-parse one file
|
|
153
|
+
reset_index ───────────── delete + rebuild
|
|
154
|
+
list_projects ─────────── all indexed projects
|
|
155
|
+
check_ollama ──────────── verify LLM
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Component | Runs as | Purpose |
|
|
159
|
+
|-----------|---------|---------|
|
|
160
|
+
| **CLI** (`fw-context`) | User command | Index, export, watch, status, reset, init, search |
|
|
161
|
+
| **Indexer** | Called by CLI | libclang parses every TU, stores in SQLite + FTS5 + vec0 |
|
|
162
|
+
| **MCP server** (`fw-context-mcp`) | Subprocess (AI assistant) | 19 tools over JSON-RPC — search, graph, source, maintenance |
|
|
163
|
+
| **Ollama** *(optional)* | Local daemon | NL search, symbol explanation, embedding generation |
|
|
164
|
+
|
|
165
|
+
## Key capabilities
|
|
166
|
+
|
|
167
|
+
- **Fast lookups** — FTS5 full-text search, prefix/exact symbol lookup, call-graph traversal
|
|
168
|
+
- **Natural-language search** — *"how does the modem connect?"* → finds `network_registration`, `modem_attach`, … (Ollama, optional)
|
|
169
|
+
- **Vector search** — semantic similarity via `sqlite-vec` + Ollama embeddings, hybrid FTS5+vector re-ranking
|
|
170
|
+
- **Graph analytics** — call paths, transitive callers/callees, dead code detection, hotspot analysis
|
|
171
|
+
- **Incremental indexing** — only changed files re-parsed; auto-reindex on query detects and fixes staleness
|
|
172
|
+
- **Offline-first** — index is a file on disk at `~/.fw-context/index/`. No daemon, no cloud, no network.
|
|
173
|
+
- **`#ifdef`-aware** — uses real compiler flags; sees exactly what your compiler sees
|
|
174
|
+
|
|
175
|
+
## Supported ecosystems
|
|
176
|
+
|
|
177
|
+
Works with **any build system** that produces `compile_commands.json`:
|
|
178
|
+
|
|
179
|
+
| Ecosystem | Auto-detection |
|
|
180
|
+
|-----------|---------------|
|
|
181
|
+
| **Mbed OS** | `mbed-os/` directory or `mbed_app.json` |
|
|
182
|
+
| **Zephyr RTOS** | `west.yml` or `prj.conf` |
|
|
183
|
+
| **PlatformIO** | `platformio.ini` |
|
|
184
|
+
| **Bare-metal / FreeRTOS** | Any build with `bear` |
|
|
185
|
+
| **Custom toolchain** | Any build with `bear` |
|
|
186
|
+
|
|
187
|
+
Subsequent runs are **incremental** — seconds for a few changed files.
|
|
188
|
+
|
|
189
|
+
## Documentation
|
|
190
|
+
|
|
191
|
+
| Document | Covers |
|
|
192
|
+
|----------|--------|
|
|
193
|
+
| **[Installation](docs/installation.md)** | Prerequisites, install, upgrade, Ollama setup, AI assistant integration |
|
|
194
|
+
| **[Tools Reference](docs/tools.md)** | All 18 MCP tools, 8 CLI commands, internal workings, search pipeline |
|
|
195
|
+
| **[Configuration](docs/configuration.md)** | `.fw-context/config.toml` — global defaults, per-project overrides, every setting |
|
|
196
|
+
| **[MCP Server](README-MCP.md)** | JSON-RPC protocol, tool schemas, error handling, debugging |
|
|
197
|
+
|
|
198
|
+
## Directory layout
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
~/.fw-context/
|
|
202
|
+
├── config.toml # global defaults
|
|
203
|
+
├── .venv/ # Python virtual environment
|
|
204
|
+
│ └── bin/
|
|
205
|
+
│ ├── fw-context # CLI
|
|
206
|
+
│ └── fw-context-mcp # MCP server
|
|
207
|
+
└── index/
|
|
208
|
+
└── <project-id>/
|
|
209
|
+
└── index.db # SQLite + FTS5 + vec0 + refs
|
|
210
|
+
|
|
211
|
+
your-firmware/
|
|
212
|
+
├── .fw-context/
|
|
213
|
+
│ └── config.toml # per-project overrides
|
|
214
|
+
└── compile_commands.json
|
|
215
|
+
```
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# fw-context-mcp — MCP Server
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server that exposes the fw-context symbol index
|
|
4
|
+
as tools for AI assistants. Communicates over **JSON-RPC 2.0 via stdio**.
|
|
5
|
+
|
|
6
|
+
## What it is
|
|
7
|
+
|
|
8
|
+
A persistent subprocess started by your AI assistant (Claude Code, OpenCode, …)
|
|
9
|
+
that provides **19 tools** for navigating embedded firmware codebases:
|
|
10
|
+
|
|
11
|
+
| Category | Tools |
|
|
12
|
+
|----------|-------|
|
|
13
|
+
| **Search & lookup** | `search_code`, `lookup_symbol`, `smart_search` |
|
|
14
|
+
| **Understanding** | `get_file_map`, `get_source`, `explain_symbol`, `get_symbol_context` |
|
|
15
|
+
| **Call graph** | `find_callers`, `find_references`, `find_call_path`, `find_all_callers_recursive`, `find_callees_recursive`, `find_dead_code`, `find_hotspots` |
|
|
16
|
+
| **Maintenance** | `get_active_build`, `reindex_file`, `reset_index`, `list_projects` |
|
|
17
|
+
| **Setup** | `check_ollama` |
|
|
18
|
+
|
|
19
|
+
Full tool reference with schemas and examples: **[Tools Reference](docs/tools.md)**
|
|
20
|
+
|
|
21
|
+
## Protocol
|
|
22
|
+
|
|
23
|
+
JSON-RPC 2.0 over stdin/stdout. One JSON object per line, terminated by `\n`.
|
|
24
|
+
|
|
25
|
+
### Initialization
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Client → Server:
|
|
29
|
+
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05",…}}
|
|
30
|
+
|
|
31
|
+
Server → Client:
|
|
32
|
+
{"jsonrpc":"2.0","id":1,"result":{…}}
|
|
33
|
+
|
|
34
|
+
Client → Server:
|
|
35
|
+
{"jsonrpc":"2.0","method":"notifications/initialized"}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Calling a tool
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Client → Server:
|
|
42
|
+
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_code","arguments":{"query":"uart init"}}}
|
|
43
|
+
|
|
44
|
+
Server → Client:
|
|
45
|
+
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"<json>"}],"isError":false}}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `text` field is a JSON-encoded string — parse it to get the return value.
|
|
49
|
+
|
|
50
|
+
### Listing tools
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Client → Server: {"jsonrpc":"2.0","id":99,"method":"tools/list","params":{}}
|
|
54
|
+
Server → Client: {"jsonrpc":"2.0","id":99,"result":{"tools":[{…},…]}}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Integration
|
|
58
|
+
|
|
59
|
+
### Claude Code
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
claude mcp add --scope user fw-context ~/.fw-context/.venv/bin/fw-context-mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run `fw-context init` to do this automatically and inject usage instructions
|
|
66
|
+
into `~/.claude/CLAUDE.md`.
|
|
67
|
+
|
|
68
|
+
### OpenCode
|
|
69
|
+
|
|
70
|
+
Add to `~/.config/opencode/opencode.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcp": {
|
|
75
|
+
"fw-context": {
|
|
76
|
+
"command": ["/home/<user>/.fw-context/.venv/bin/fw-context-mcp"],
|
|
77
|
+
"enabled": true,
|
|
78
|
+
"type": "local"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Other MCP clients
|
|
85
|
+
|
|
86
|
+
Start the server as a subprocess:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
/path/to/.fw-context/.venv/bin/fw-context-mcp
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Send JSON-RPC on stdin, read responses from stdout. The server has no CLI
|
|
93
|
+
interface — it only speaks MCP.
|
|
94
|
+
|
|
95
|
+
## Error handling
|
|
96
|
+
|
|
97
|
+
**JSON-RPC errors** (protocol level):
|
|
98
|
+
```json
|
|
99
|
+
{"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid request parameters"}}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Tool-level errors** (tool ran but couldn't complete):
|
|
103
|
+
```json
|
|
104
|
+
{"result":{"content":[{"type":"text","text":"{\"error\": \"No index found for …\"}"}],"isError":false}}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Tool errors use `isError: false` — the tool call succeeded, but returned an
|
|
108
|
+
error field. Only JSON-RPC protocol errors use the top-level `error` key.
|
|
109
|
+
|
|
110
|
+
### Staleness and auto-reindex
|
|
111
|
+
|
|
112
|
+
`search_code` and `lookup_symbol` automatically detect stale files in results,
|
|
113
|
+
re-index them (up to 5 files, 30 s timeout), and re-run the query. When
|
|
114
|
+
auto-reindex fails or `compile_commands.json` itself is stale, a warning
|
|
115
|
+
entry is included:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
[
|
|
119
|
+
{"warning": "Auto-reindex failed for 1 file(s): …"},
|
|
120
|
+
{"name": "spi_init", …}
|
|
121
|
+
]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Database corruption
|
|
125
|
+
|
|
126
|
+
The server runs `PRAGMA integrity_check` on every database open. If corruption
|
|
127
|
+
is detected, tools return a structured error:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"error": "Database corruption detected: …",
|
|
132
|
+
"action": "reset_index",
|
|
133
|
+
"hint": "Run reset_index() then fw-context index to rebuild."
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Call `reset_index(confirm=true)` to delete the database, then `fw-context index`
|
|
138
|
+
to rebuild.
|
|
139
|
+
|
|
140
|
+
## Environment
|
|
141
|
+
|
|
142
|
+
- **CWD:** The server resolves project root from `$PWD` when `project_root`
|
|
143
|
+
is not provided. Launch it from the firmware project root.
|
|
144
|
+
- **No network required** for core tools (search, lookup, source, graph).
|
|
145
|
+
`smart_search` and `explain_symbol` optionally use local Ollama.
|
|
146
|
+
- **Stateless** between calls — each tool opens the DB, queries, closes.
|
|
147
|
+
|
|
148
|
+
## Debugging
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Manual tool listing
|
|
152
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
|
|
153
|
+
| /path/to/.fw-context/.venv/bin/fw-context-mcp
|
|
154
|
+
|
|
155
|
+
# Verbose logging
|
|
156
|
+
FW_CONTEXT_LOG_LEVEL=DEBUG /path/to/.fw-context/.venv/bin/fw-context-mcp
|
|
157
|
+
|
|
158
|
+
# Ollama debug log (when debug_log is set in config)
|
|
159
|
+
tail -f ~/.fw-context/llm-debug.jsonl
|
|
160
|
+
```
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# fw-context
|
|
2
|
+
<!-- mcp-name: io.github.turbyho/fw-context-mcp -->
|
|
3
|
+
mcp-name: io.github.turbyho/fw-context-mcp
|
|
4
|
+
|
|
5
|
+
**MCP server for embedded C/C++ firmware** — gives AI assistants (Claude Code,
|
|
6
|
+
Cursor, OpenCode, etc.) real understanding of your codebase. Parses your actual build
|
|
7
|
+
with [libclang](https://clang.llvm.org/), extracts every symbol, and builds a
|
|
8
|
+
persistent index with full-text search, call graph, and vector embeddings.
|
|
9
|
+
|
|
10
|
+
No hallucination. No grepping. No reading thousands of framework headers into context.
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
Your AI assistant goes from guessing to knowing:
|
|
15
|
+
|
|
16
|
+
> *"What does `uart_init` do and who calls it?"*
|
|
17
|
+
> → `get_symbol_context("uart_init")` — body, callers, callees in one call.
|
|
18
|
+
>
|
|
19
|
+
> *"Find all BLE advertising functions and how they're connected."*
|
|
20
|
+
> → `search_code("ble advertising", kind="function")` → `find_call_path("gap_init", "start_advertising")`
|
|
21
|
+
>
|
|
22
|
+
> *"Show me the implementation of `adc_read` — not the declaration."*
|
|
23
|
+
> → `get_source("adc_read")` — exact body via libclang, no file reading.
|
|
24
|
+
>
|
|
25
|
+
> *"What would break if I change `spi_transfer`?"*
|
|
26
|
+
> → `find_all_callers_recursive("spi_transfer")` — every caller, direct and indirect.
|
|
27
|
+
>
|
|
28
|
+
> *"Give me a map of `modem_msg.cpp` before I read it."*
|
|
29
|
+
> → `get_file_map("src/modem_msg.cpp")` — 426 symbols grouped by kind.
|
|
30
|
+
|
|
31
|
+
**19 MCP tools** — symbol search, source reading, call-graph traversal, hotspot
|
|
32
|
+
analysis, dead code detection, vector search. All backed by real compiler flags
|
|
33
|
+
from `compile_commands.json` — `#ifdef`-aware, not grep.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 1. Install
|
|
39
|
+
git clone git@github.com:turbyho/fw-context-mcp.git ~/.fw-context/src
|
|
40
|
+
uv venv ~/.fw-context/.venv --python 3.12
|
|
41
|
+
uv pip install --python ~/.fw-context/.venv/bin/python ~/.fw-context/src/
|
|
42
|
+
echo 'export PATH="$HOME/.fw-context/.venv/bin:$PATH"' >> ~/.zshrc
|
|
43
|
+
|
|
44
|
+
# 2. Register with your AI assistant
|
|
45
|
+
fw-context init
|
|
46
|
+
|
|
47
|
+
# 3. Generate compile_commands.json and index
|
|
48
|
+
cd your-firmware-project
|
|
49
|
+
|
|
50
|
+
# Mbed OS:
|
|
51
|
+
bear -- mbed compile --profile release
|
|
52
|
+
|
|
53
|
+
# Zephyr:
|
|
54
|
+
west build -b <board> -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
|
55
|
+
fw-context index build/compile_commands.json
|
|
56
|
+
|
|
57
|
+
# PlatformIO:
|
|
58
|
+
pio run --target compiledb
|
|
59
|
+
fw-context index
|
|
60
|
+
|
|
61
|
+
# CMake / Make:
|
|
62
|
+
bear -- make
|
|
63
|
+
# or: bear -- cmake --build build
|
|
64
|
+
|
|
65
|
+
fw-context index
|
|
66
|
+
|
|
67
|
+
# 4. Restart your assistant and start asking about your code
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For detailed prerequisites, Ollama setup, and AI assistant integration:
|
|
71
|
+
**[Installation guide →](docs/installation.md)**
|
|
72
|
+
|
|
73
|
+
## Why not just use LSP?
|
|
74
|
+
|
|
75
|
+
LSP servers (clangd, ccls) are excellent for interactive editing.
|
|
76
|
+
But they have limitations for AI-assisted exploration:
|
|
77
|
+
|
|
78
|
+
| Limitation | fw-context solution |
|
|
79
|
+
|-----------|---------------------|
|
|
80
|
+
| No full-text search across the codebase | FTS5 over 6 columns — find "all functions related to modem init" |
|
|
81
|
+
| Index dies with the server — rebuild from scratch | Persistent SQLite file — survives reboots, reads in milliseconds |
|
|
82
|
+
| Editor protocol, not AI protocol | MCP tools purpose-built for AI assistant workflow |
|
|
83
|
+
| Blind to which `#ifdef` branch is active | Uses real compiler flags from `compile_commands.json` |
|
|
84
|
+
|
|
85
|
+
Use **clangd for editing**, **fw-context for AI-assisted exploration**.
|
|
86
|
+
|
|
87
|
+
## Architecture
|
|
88
|
+
|
|
89
|
+
### Data flow
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
BUILD INDEX QUERY
|
|
93
|
+
===== ===== =====
|
|
94
|
+
bear / west / pio libclang parses each TU AI assistant calls
|
|
95
|
+
cmake / make extracts symbols + refs MCP tools over
|
|
96
|
+
│ generates embeddings JSON-RPC (stdio)
|
|
97
|
+
▼ │ │
|
|
98
|
+
compile_commands SQLite db on disk lookup_symbol(…)
|
|
99
|
+
.json ~/.fw-context/index/ search_code(…)
|
|
100
|
+
│ │ find_callers(…)
|
|
101
|
+
▼ ▼ explain_symbol(…)
|
|
102
|
+
symbols + refs vec0 get_symbol_context(…)
|
|
103
|
+
(FTS5 index) (vector KNN) │
|
|
104
|
+
▼
|
|
105
|
+
AI assistant answers
|
|
106
|
+
your question about
|
|
107
|
+
the code
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Components
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
CLI (fw-context) MCP server (fw-context-mcp) Ollama (optional)
|
|
114
|
+
================ =========================== ==================
|
|
115
|
+
fw-context index exposes 19 tools over local LLM runtime
|
|
116
|
+
fw-context export JSON-RPC (stdio) HTTP :11434
|
|
117
|
+
fw-context watch │ │
|
|
118
|
+
fw-context status search_code ───────────── lookup smart_search ──▶ translates NL → FTS5 terms
|
|
119
|
+
fw-context reset lookup_symbol ─────────── prefix explain_symbol ─▶ explains function
|
|
120
|
+
fw-context init smart_search ──────────── NL embeddings ────▶ mxbai-embed-large
|
|
121
|
+
fw-context search get_file_map ──────────── file structure by kind
|
|
122
|
+
get_source ────────────── body
|
|
123
|
+
get_symbol_context ────── body+callers+callees
|
|
124
|
+
find_callers ──────────── direct callers
|
|
125
|
+
find_references ───────── all uses
|
|
126
|
+
find_call_path ────────── BFS in call graph
|
|
127
|
+
find_all_callers_recursive transitive callers
|
|
128
|
+
find_callees_recursive ── transitive callees
|
|
129
|
+
find_dead_code ────────── never called
|
|
130
|
+
find_hotspots ─────────── most-called
|
|
131
|
+
get_active_build ──────── index health
|
|
132
|
+
reindex_file ──────────── re-parse one file
|
|
133
|
+
reset_index ───────────── delete + rebuild
|
|
134
|
+
list_projects ─────────── all indexed projects
|
|
135
|
+
check_ollama ──────────── verify LLM
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Component | Runs as | Purpose |
|
|
139
|
+
|-----------|---------|---------|
|
|
140
|
+
| **CLI** (`fw-context`) | User command | Index, export, watch, status, reset, init, search |
|
|
141
|
+
| **Indexer** | Called by CLI | libclang parses every TU, stores in SQLite + FTS5 + vec0 |
|
|
142
|
+
| **MCP server** (`fw-context-mcp`) | Subprocess (AI assistant) | 19 tools over JSON-RPC — search, graph, source, maintenance |
|
|
143
|
+
| **Ollama** *(optional)* | Local daemon | NL search, symbol explanation, embedding generation |
|
|
144
|
+
|
|
145
|
+
## Key capabilities
|
|
146
|
+
|
|
147
|
+
- **Fast lookups** — FTS5 full-text search, prefix/exact symbol lookup, call-graph traversal
|
|
148
|
+
- **Natural-language search** — *"how does the modem connect?"* → finds `network_registration`, `modem_attach`, … (Ollama, optional)
|
|
149
|
+
- **Vector search** — semantic similarity via `sqlite-vec` + Ollama embeddings, hybrid FTS5+vector re-ranking
|
|
150
|
+
- **Graph analytics** — call paths, transitive callers/callees, dead code detection, hotspot analysis
|
|
151
|
+
- **Incremental indexing** — only changed files re-parsed; auto-reindex on query detects and fixes staleness
|
|
152
|
+
- **Offline-first** — index is a file on disk at `~/.fw-context/index/`. No daemon, no cloud, no network.
|
|
153
|
+
- **`#ifdef`-aware** — uses real compiler flags; sees exactly what your compiler sees
|
|
154
|
+
|
|
155
|
+
## Supported ecosystems
|
|
156
|
+
|
|
157
|
+
Works with **any build system** that produces `compile_commands.json`:
|
|
158
|
+
|
|
159
|
+
| Ecosystem | Auto-detection |
|
|
160
|
+
|-----------|---------------|
|
|
161
|
+
| **Mbed OS** | `mbed-os/` directory or `mbed_app.json` |
|
|
162
|
+
| **Zephyr RTOS** | `west.yml` or `prj.conf` |
|
|
163
|
+
| **PlatformIO** | `platformio.ini` |
|
|
164
|
+
| **Bare-metal / FreeRTOS** | Any build with `bear` |
|
|
165
|
+
| **Custom toolchain** | Any build with `bear` |
|
|
166
|
+
|
|
167
|
+
Subsequent runs are **incremental** — seconds for a few changed files.
|
|
168
|
+
|
|
169
|
+
## Documentation
|
|
170
|
+
|
|
171
|
+
| Document | Covers |
|
|
172
|
+
|----------|--------|
|
|
173
|
+
| **[Installation](docs/installation.md)** | Prerequisites, install, upgrade, Ollama setup, AI assistant integration |
|
|
174
|
+
| **[Tools Reference](docs/tools.md)** | All 18 MCP tools, 8 CLI commands, internal workings, search pipeline |
|
|
175
|
+
| **[Configuration](docs/configuration.md)** | `.fw-context/config.toml` — global defaults, per-project overrides, every setting |
|
|
176
|
+
| **[MCP Server](README-MCP.md)** | JSON-RPC protocol, tool schemas, error handling, debugging |
|
|
177
|
+
|
|
178
|
+
## Directory layout
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
~/.fw-context/
|
|
182
|
+
├── config.toml # global defaults
|
|
183
|
+
├── .venv/ # Python virtual environment
|
|
184
|
+
│ └── bin/
|
|
185
|
+
│ ├── fw-context # CLI
|
|
186
|
+
│ └── fw-context-mcp # MCP server
|
|
187
|
+
└── index/
|
|
188
|
+
└── <project-id>/
|
|
189
|
+
└── index.db # SQLite + FTS5 + vec0 + refs
|
|
190
|
+
|
|
191
|
+
your-firmware/
|
|
192
|
+
├── .fw-context/
|
|
193
|
+
│ └── config.toml # per-project overrides
|
|
194
|
+
└── compile_commands.json
|
|
195
|
+
```
|