driverge-mcp 0.0.0 → 0.1.0-beta.1

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.
Files changed (88) hide show
  1. package/README.md +371 -32
  2. package/dist/codegen/esp32.d.ts +11 -3
  3. package/dist/codegen/esp32.js +226 -15
  4. package/dist/codegen/esp32.js.map +1 -1
  5. package/dist/codegen/ident.d.ts +24 -2
  6. package/dist/codegen/ident.js +37 -4
  7. package/dist/codegen/ident.js.map +1 -1
  8. package/dist/codegen/index.d.ts +12 -7
  9. package/dist/codegen/index.js +17 -14
  10. package/dist/codegen/index.js.map +1 -1
  11. package/dist/codegen/lint.js +28 -8
  12. package/dist/codegen/lint.js.map +1 -1
  13. package/dist/codegen/portable-cpp.d.ts +8 -0
  14. package/dist/codegen/portable-cpp.js +280 -0
  15. package/dist/codegen/portable-cpp.js.map +1 -0
  16. package/dist/codegen/portable.d.ts +99 -3
  17. package/dist/codegen/portable.js +420 -99
  18. package/dist/codegen/portable.js.map +1 -1
  19. package/dist/codegen/stm32.d.ts +11 -3
  20. package/dist/codegen/stm32.js +166 -17
  21. package/dist/codegen/stm32.js.map +1 -1
  22. package/dist/codegen/types.d.ts +32 -0
  23. package/dist/codegen/types.js +27 -0
  24. package/dist/codegen/types.js.map +1 -1
  25. package/dist/mcp/cache.d.ts +15 -2
  26. package/dist/mcp/cache.js +32 -4
  27. package/dist/mcp/cache.js.map +1 -1
  28. package/dist/mcp/register.d.ts +17 -0
  29. package/dist/mcp/register.js +173 -22
  30. package/dist/mcp/register.js.map +1 -1
  31. package/dist/mcp/summary.js +8 -0
  32. package/dist/mcp/summary.js.map +1 -1
  33. package/dist/pdf/address.d.ts +4 -0
  34. package/dist/pdf/address.js +28 -0
  35. package/dist/pdf/address.js.map +1 -0
  36. package/dist/pdf/command.d.ts +21 -0
  37. package/dist/pdf/command.js +184 -16
  38. package/dist/pdf/command.js.map +1 -1
  39. package/dist/pdf/extract.js +50 -3
  40. package/dist/pdf/extract.js.map +1 -1
  41. package/dist/pdf/generic-register-table.d.ts +5 -0
  42. package/dist/pdf/generic-register-table.js +125 -0
  43. package/dist/pdf/generic-register-table.js.map +1 -0
  44. package/dist/pdf/index.d.ts +2 -0
  45. package/dist/pdf/index.js +2 -0
  46. package/dist/pdf/index.js.map +1 -1
  47. package/dist/pdf/interface-kind.d.ts +11 -0
  48. package/dist/pdf/interface-kind.js +18 -0
  49. package/dist/pdf/interface-kind.js.map +1 -1
  50. package/dist/pdf/manufacturer.js +39 -0
  51. package/dist/pdf/manufacturer.js.map +1 -1
  52. package/dist/pdf/maxim-register-map.d.ts +11 -0
  53. package/dist/pdf/maxim-register-map.js +544 -0
  54. package/dist/pdf/maxim-register-map.js.map +1 -0
  55. package/dist/pdf/part.js +10 -0
  56. package/dist/pdf/part.js.map +1 -1
  57. package/dist/pdf/prose-commands.d.ts +4 -0
  58. package/dist/pdf/prose-commands.js +90 -0
  59. package/dist/pdf/prose-commands.js.map +1 -0
  60. package/dist/pdf/register-table.d.ts +4 -0
  61. package/dist/pdf/register-table.js +4 -2
  62. package/dist/pdf/register-table.js.map +1 -1
  63. package/dist/pdf/st-bit-layout.d.ts +3 -0
  64. package/dist/pdf/st-bit-layout.js +238 -0
  65. package/dist/pdf/st-bit-layout.js.map +1 -0
  66. package/dist/pdf/ti-command-byte.d.ts +7 -0
  67. package/dist/pdf/ti-command-byte.js +165 -0
  68. package/dist/pdf/ti-command-byte.js.map +1 -0
  69. package/dist/pdf/ti-field-descriptions.d.ts +10 -0
  70. package/dist/pdf/ti-field-descriptions.js +87 -0
  71. package/dist/pdf/ti-field-descriptions.js.map +1 -0
  72. package/dist/pdf/ti-register-map.d.ts +5 -0
  73. package/dist/pdf/ti-register-map.js +84 -0
  74. package/dist/pdf/ti-register-map.js.map +1 -0
  75. package/dist/pdf/types.d.ts +15 -3
  76. package/dist/pdf/types.js +4 -1
  77. package/dist/pdf/types.js.map +1 -1
  78. package/dist/schema/assemble.d.ts +35 -3
  79. package/dist/schema/assemble.js +93 -13
  80. package/dist/schema/assemble.js.map +1 -1
  81. package/dist/schema/types.d.ts +21 -1
  82. package/dist/schema/validate.js +44 -10
  83. package/dist/schema/validate.js.map +1 -1
  84. package/dist/server.d.ts +18 -0
  85. package/dist/server.js +54 -3
  86. package/dist/server.js.map +1 -1
  87. package/package.json +1 -1
  88. package/schemas/datasheet.schema.json +17 -3
package/README.md CHANGED
@@ -4,14 +4,20 @@
4
4
  <p align="center"><em>Datasheet PDF → embedded C/C++ driver, from any MCP client.</em></p>
5
5
 
6
6
  <p align="center">
7
- <img alt="license" src="https://img.shields.io/badge/license-MIT-blue">
8
- <img alt="status" src="https://img.shields.io/badge/status-pre--release-orange">
7
+ <a href="https://www.npmjs.com/package/driverge-mcp"><img alt="npm" src="https://img.shields.io/npm/v/driverge-mcp"></a>
8
+ <a href="https://github.com/MehmetTopuz/driverge-mcp/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/MehmetTopuz/driverge-mcp/actions/workflows/ci.yml/badge.svg"></a>
9
+ <a href="LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-blue"></a>
10
+ <img alt="status" src="https://img.shields.io/badge/status-closed%20beta-orange">
9
11
  <img alt="mcp" src="https://img.shields.io/badge/MCP-server-black">
10
12
  </p>
11
13
 
12
- > 🚧 **Pre-release** — under active development; not yet published to npm. The
13
- > `npx driverge-mcp` command below is the intended install; until it's published,
14
- > [run from source](#run-from-source-pre-release).
14
+ > 🧪 **Closed beta** — [`driverge-mcp`](https://www.npmjs.com/package/driverge-mcp)
15
+ > is published to npm under the `beta` dist-tag; install the beta channel with
16
+ > **`npx -y driverge-mcp@beta`**. It is **not yet hardware-verified through the
17
+ > release gate** — generated drivers are reviewed *drafts*, not certified
18
+ > firmware. APIs and the JSON schema may still change before the stable **v0.1.0**.
19
+ > See **[Maturity & status](#maturity--status)**, and **[BETA.md](BETA.md)** if
20
+ > you're testing.
15
21
 
16
22
  ---
17
23
 
@@ -27,15 +33,68 @@ TypeScript pipeline extracts a *validated, structured JSON* model of the chip, a
27
33
  the host AI you're already talking to fills in the reasoning-heavy parts (init
28
34
  sequence, vendor quirks, docs). Your datasheet never leaves your machine.
29
35
 
36
+ ## Quick start
37
+
38
+ Add one entry to your MCP client's config (Claude Desktop, Claude Code, Cursor —
39
+ paths per client in [Installation](#installation)):
40
+
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "driverge": { "command": "npx", "args": ["-y", "driverge-mcp"] }
45
+ }
46
+ }
47
+ ```
48
+
49
+ Restart the client, then ask:
50
+
51
+ > "Analyze the datasheet at `C:/ds/bme280.pdf` with driverge, then generate a
52
+ > portable driver."
53
+
54
+ That's it — no clone, no build, no API key. Details in
55
+ [Installation](#installation) and [Usage](#usage).
56
+
57
+ ## Why Driverge?
58
+
59
+ Bringing up a new sensor or IC means hand-transcribing dozens of register
60
+ addresses, bit-field masks, and command codes out of a 40-page PDF — slow work,
61
+ and a classic source of silent bugs (one wrong mask or transposed address and the
62
+ driver "works" but reads garbage). Driverge does that mechanical part
63
+ deterministically and leaves the reasoning to the AI you already use.
64
+
65
+ - **No hallucinated register maps.** Addresses, bit-field masks, and command codes
66
+ are *extracted from the datasheet and validated* — not guessed. That's the
67
+ failure mode of "just ask an LLM to write the whole driver"; here, invalid or
68
+ incomplete data is rejected before it ever reaches code generation.
69
+ - **Bring your own client — no API keys, no lock-in.** Driverge is a plain MCP
70
+ server with no embedded LLM. It runs inside whatever MCP client you already use
71
+ (Claude Desktop, Claude Code, Cursor, …) and reasons with the model you're
72
+ already paying for — no separate subscription or service.
73
+ - **Private & offline.** The datasheet is parsed locally and never uploaded — safe
74
+ for NDA'd or unreleased parts.
75
+ - **Deterministic & reproducible.** The same PDF always yields the same JSON and
76
+ the same driver skeleton — reviewable, diff-able, and testable, not a one-shot
77
+ black box.
78
+ - **Portable by construction.** One driver core targets any platform through a
79
+ tiny per-bus thin-HAL seam (2–3 functions); the native targets (STM32, ESP32)
80
+ pre-fill that seam for you — switch platforms without touching driver logic.
81
+ - **The AI does only what it's good at.** Register geometry is deterministic;
82
+ init-sequence ordering, timing quirks, and compensation math need judgment.
83
+ Driverge marks exactly those spots with `TODO(driverge)` and a `fill_in_brief`,
84
+ the host AI completes them, then `validate_driver` checks the result.
85
+
86
+ **Good for:** quickly evaluating a new sensor, prototyping, porting an existing
87
+ driver to a different MCU, or just learning an unfamiliar chip's register map.
88
+
30
89
  ## What it does
31
90
 
32
91
  1. **Analyze** a datasheet PDF → detect format, manufacturer, and interface kind
33
92
  (register-map vs. command-set), then extract registers / bit-fields (or
34
93
  commands + CRC) and the bus protocol into a **frozen JSON contract**, gated by
35
94
  a validator.
36
- 2. **Generate** a driver for a target platform: a deterministic **thin-HAL
37
- skeleton** — register/bit-field constants, the 5-function HAL seam, function
38
- stubs — with every reasoning gap marked `TODO(driverge)` plus a `fill_in_brief`
95
+ 2. **Generate** a driver for a target platform, in C or C++: a deterministic
96
+ **thin-HAL skeleton** — register/bit-field constants, the per-bus thin-HAL
97
+ seam, function stubs — with every reasoning gap marked `TODO(driverge)` plus a `fill_in_brief`
39
98
  telling the host AI exactly what to complete.
40
99
  3. **Validate** the completed driver: thin-HAL purity, no leftover TODOs, register
41
100
  references exist, bit-field masks match the JSON.
@@ -46,26 +105,183 @@ Every target specializes the same portable **[thin-HAL](https://en.wikipedia.org
46
105
  seam — the driver core is identical across platforms; only the seam implementation
47
106
  changes.
48
107
 
49
- | Target | Bus binding | Language | Status |
50
- |---|---|---|---|
51
- | **Portable (thin-HAL)** | user-implemented `hal_i2c_*` / `hal_spi_*` / `hal_delay_ms` | C | |
52
- | **ESP32** | ESP-IDF `i2c_master_*` | C | |
53
- | **STM32** | CubeHAL `HAL_I2C_Mem_Read/Write` | C | |
54
- | **Arduino** | `Wire` / `SPI` | C++ | planned |
55
-
56
- - **Offline & private** the datasheet is parsed locally; nothing is uploaded.
57
- - **Deterministic** the same PDF yields the same JSON and the same skeleton.
58
- - **Cross-platform** one parsed model, many target bindings.
108
+ | Target | Bus binding | Buses | Language | Maturity |
109
+ |---|---|---|---|---|
110
+ | **Portable (thin-HAL)** | user-implemented `hal_*` seam | I²C, SPI, UART, CAN | C / C++ | **Beta** |
111
+ | **ESP32** | ESP-IDF (`i2c_master_*`, `spi_master`, `uart`, TWAI) | C, SPI, UART, CAN | C / C++ | **Experimental** |
112
+ | **STM32** | CubeHAL (`HAL_I2C_*`, `HAL_SPI_*` + GPIO CS, `HAL_UART_*`) | C, SPI, UART | C / C++ | **Experimental** |
113
+ | **Arduino** | `Wire` / `SPI` | — | C++ | not implemented |
114
+
115
+ "Maturity" here is *codegen* maturity vs. *hardware-verified* maturity see
116
+ [Maturity & status](#maturity--status) for exactly what has and hasn't run on
117
+ silicon. STM32 CAN is planned (the bxCAN/FDCAN family split needs its own pass); asking
118
+ a target for a bus it doesn't support fails fast with a clear
119
+ `UnsupportedBusError` rather than emitting a wrong seam. Pass
120
+ `language: "cpp"` to `generate_driver` for a class-based C++ driver
121
+ (`.hpp`/`.cpp`) instead of the default C output — same registers, same seam,
122
+ same validation.
59
123
 
60
124
  > ⚠️ **Generated code is a strong draft, not a certified driver.** Init sequences,
61
125
  > compensation formulas, and timing quirks are completed by the host AI and
62
126
  > **must be reviewed** before use on hardware. Not safety-certified.
63
127
 
128
+ ### Verified parts
129
+
130
+ The extraction pipeline is regression-tested against real datasheets from
131
+ **12 manufacturers**. Parts with fully automatic extraction (registers *and*
132
+ bit-fields, or a clean command set):
133
+
134
+ | Part | Manufacturer | Kind | Extracted |
135
+ |---|---|---|---|
136
+ | BME280 | Bosch Sensortec | register map | 16 regs, 19 bit-fields |
137
+ | MCP23017 | Microchip | register map | 12 regs, 96 bit-fields |
138
+ | SHT3x | Sensirion | command set | 6 commands + CRC |
139
+ | TMAG5170 | Texas Instruments | register map (SPI) | 21 regs, 79 bit-fields |
140
+ | DHT20 | Aosong | command set | 2 commands |
141
+ | LSM6DSRX | STMicroelectronics | register map | 31 regs, 91 bit-fields |
142
+ | MAX30102 | Maxim Integrated | register map | 20 regs, 33 bit-fields |
143
+
144
+ Other tested parts (ADXL345, MLX90614, AEAT-8811, PCA9685, VL53L3CX, TLE5014)
145
+ extract **partially** or **defer** to the host AI — the pipeline says so
146
+ explicitly instead of guessing, and the generated skeleton tells the host AI
147
+ what to complete. The full, always-current matrix lives in the
148
+ [coverage scorecard](tests/scorecard/scorecard.snap.md).
149
+
150
+ ## Maturity & status
151
+
152
+ Driverge is in **closed beta** (`0.1.0-beta.x`, npm `beta` dist-tag). Here is
153
+ exactly what that means — what's proven, what isn't, and how much to trust the
154
+ output.
155
+
156
+ **Proven today (host-level):**
157
+ - The full deterministic test suite is green (440-plus tests) on a clean
158
+ TypeScript build.
159
+ - The **portable** driver is compiled by a real `gcc` gate in CI.
160
+ - The extraction pipeline is regression-tested against **13 real datasheets**,
161
+ and reports its own coverage honestly — **7 fully extracted, 3 partial, 3
162
+ deferred** to the host AI (see the
163
+ [coverage scorecard](tests/scorecard/scorecard.snap.md)).
164
+
165
+ **Not yet proven — this is the beta → v0.1.0 gate:**
166
+ - **On-hardware behavior is not gated.** Only one informal ESP32 bring-up has run
167
+ (MPU-9250, hand-completed), and it surfaced the bus-error and address bugs
168
+ fixed in this release. There is no clean, repeatable hardware pass yet, and
169
+ **STM32 has never run on silicon.**
170
+ - The **native** ESP32/STM32 seams are not yet built by an automated compile gate
171
+ (ESP-IDF / CubeIDE) — only the portable target is.
172
+ - Only one MCP client has been exercised end-to-end.
173
+
174
+ **Per-target maturity:**
175
+
176
+ | Target | Buses with any on-hardware exposure | Maturity |
177
+ |---|---|---|
178
+ | Portable (thin-HAL) | I²C (host-compiled only) | **Beta** — host-tested + gcc-compiled, not on hardware |
179
+ | ESP32 (ESP-IDF) | I²C (one informal bring-up) | **Experimental** — codegen shipped; SPI/UART/CAN never on hardware |
180
+ | STM32 (CubeHAL) | none | **Experimental** — codegen shipped; never on hardware |
181
+ | Arduino · STM32 CAN | — | **Not implemented** |
182
+
183
+ > Treat every generated driver as a **reviewed draft**: check register addresses,
184
+ > the init sequence, and any compensation math against the datasheet before you
185
+ > flash it. Driverge is not safety-certified.
186
+
187
+ **Testing the beta?** [BETA.md](BETA.md) has the identity-register smoke test to
188
+ run and how to send a field report — the MPU-9250 report in
189
+ `raw/DRIVERGE_ISSUES.md` is the template.
190
+
191
+ ## Concepts behind Driverge
192
+
193
+ Driverge splits driver-writing into two kinds of work: the **mechanical part**
194
+ (register addresses, masks, command tables — extracted and checked by
195
+ deterministic code) and the **judgment part** (init ordering, timing quirks,
196
+ compensation math — completed by the host AI). Everything below exists to keep
197
+ that boundary sharp.
198
+
199
+ ```mermaid
200
+ flowchart LR
201
+ PDF["Datasheet PDF"]
202
+ subgraph D["Driverge — deterministic, no LLM"]
203
+ A["analyze_datasheet<br>L1–L5 parse + validate"]
204
+ J[("frozen JSON<br>cached under a ref")]
205
+ G["generate_driver<br>thin-HAL skeleton +<br>TODO(driverge) markers"]
206
+ V["validate_driver<br>static lint"]
207
+ end
208
+ subgraph H["Host AI — reasoning"]
209
+ F["fill the TODOs: init sequence,<br>quirks, compensation docs"]
210
+ end
211
+ OUT["driver.c / driver.h"]
212
+ PDF --> A --> J --> G --> F --> V
213
+ V -->|pass| OUT
214
+ V -->|fail| F
215
+ ```
216
+
217
+ ### Deterministic core, reasoning at the edge
218
+
219
+ Register geometry is mechanical: an address is right or wrong, a mask either
220
+ matches the datasheet or it doesn't. Driverge handles that part with plain
221
+ TypeScript — no internal LLM, no API keys, no sampling — so the output is the
222
+ same on every run. What genuinely needs judgment (in what order to poke the
223
+ registers, which timing quirk applies, how to document a compensation formula)
224
+ is left to the host AI you're already talking to.
225
+
226
+ ### The frozen JSON contract
227
+
228
+ `analyze_datasheet` runs a five-stage pipeline (L1–L5): detect the PDF type,
229
+ map keyword pages, identify the manufacturer and interface kind, extract the
230
+ register table (or command set + CRC), and validate the result against a frozen
231
+ draft-07 [JSON-Schema contract](schemas/datasheet.schema.json) — also exposed
232
+ as the `driverge://schema` resource. Anything that fails validation is rejected
233
+ *before* code generation, so a bad extraction can never silently become a bad
234
+ driver.
235
+
236
+ ### The `ref` handle
237
+
238
+ Parsing a datasheet yields a large JSON document; shuttling it through the chat
239
+ context on every call would be slow and lossy. Instead, the parsed model is
240
+ cached server-side under a content-stable `ref`, and the tools pass that handle
241
+ around. The same `ref` with a different `target` re-renders instantly with no
242
+ re-parse, and the full JSON stays readable at `driverge://datasheet/<ref>`.
243
+
244
+ ### The thin-HAL seam
245
+
246
+ Generated drivers touch hardware through a tiny per-bus seam — and nothing
247
+ else:
248
+
249
+ | Bus | Seam functions (plus `hal_delay_ms`) |
250
+ |---|---|
251
+ | I²C | `hal_i2c_read`, `hal_i2c_write` |
252
+ | SPI | `hal_spi_transfer` (one call = one CS-framed transaction) |
253
+ | UART | `hal_uart_write`, `hal_uart_read` |
254
+ | CAN | `hal_can_transfer` (one call = one frame exchange) |
255
+
256
+ The register-access transfer seams (`hal_i2c_*`, `hal_spi_transfer`,
257
+ `hal_can_transfer`) return `int` — **`0` on success, non-zero on a bus error**
258
+ (NACK, timeout) — and `<part>_read_register`/`_write_register` **propagate** that
259
+ status, so a failed transfer surfaces to the caller instead of being silently
260
+ swallowed. (`hal_uart_read` returns the byte count actually read; `hal_delay_ms`
261
+ is `void`.) A native seam (ESP32, STM32) returns its vendor status directly
262
+ (`esp_err_t`/`HAL_OK`-mapped), which is already `0` on success.
263
+
264
+ The driver core is therefore identical across platforms; a native target
265
+ (ESP32, STM32) just pre-fills the seam with the vendor calls.
266
+ `validate_driver` enforces this purity: a driver that calls a vendor peripheral
267
+ API outside the seam fails the lint. Buses with no universal register-access
268
+ primitive (UART, CAN) keep the same discipline — the device-specific framing is
269
+ a marked `TODO(driverge)` reasoning gap (`framing_todo`), completed by the host
270
+ AI and then linted.
271
+
272
+ ### The fill-in loop
273
+
274
+ The skeleton marks every reasoning gap with a `TODO(driverge)` comment and
275
+ ships a `fill_in_brief` describing what belongs there. The host AI completes
276
+ the markers using the datasheet resource, then `validate_driver` statically
277
+ checks the result — no leftover TODOs, every register reference real, masks
278
+ matching the JSON — and the loop repeats until it passes.
279
+
64
280
  ## Installation
65
281
 
66
- **Prerequisites:** Node.js LTS (≥ 18).
282
+ **Prerequisites:** Node.js LTS (≥ 18; CI-tested on Node 20 & 22).
67
283
 
68
- Once published, add Driverge to your MCP client (no build step —
284
+ Add Driverge to your MCP client (no build step —
69
285
  [npx](https://docs.npmjs.com/cli/commands/npx) fetches and runs it):
70
286
 
71
287
  **Claude Desktop** — `claude_desktop_config.json`:
@@ -98,9 +314,67 @@ Once published, add Driverge to your MCP client (no build step —
98
314
  Other clients (Codex, Gemini CLI, …) take the same `command` + `args` pair in
99
315
  their own MCP config.
100
316
 
101
- ### Run from source (pre-release)
317
+ > **Beta testers:** pin the beta channel explicitly with `["-y",
318
+ > "driverge-mcp@beta"]` as the `args`. Plain `driverge-mcp` resolves the npm
319
+ > `latest` tag, which deliberately lags the beta during closed beta.
320
+
321
+ **No clone, no global install.** The config above tells your MCP client to
322
+ launch `npx -y driverge-mcp`; npx downloads Driverge from the npm registry on
323
+ first run, caches it, and starts it automatically each time the client does
324
+ (`-y` skips npx's install prompt). You never run it by hand — to confirm it's
325
+ wired up, ask your client to run the `ping` tool, which replies `pong`. Cloning
326
+ the repo (below) is only for development.
327
+
328
+ ### Configuration
329
+
330
+ | Env var | Default | Purpose |
331
+ |---|---|---|
332
+ | `DRIVERGE_OUT_ROOT` | server's working directory | Root that `generate_driver`'s `out_dir` writes are confined to. Any `out_dir` that resolves outside this root is rejected (`out_dir "…" escapes the allowed root`). Set it to the directory you want drivers written into. |
333
+
334
+ Set it in the MCP config's `env` block, e.g.:
335
+
336
+ ```json
337
+ {
338
+ "mcpServers": {
339
+ "driverge": {
340
+ "command": "npx",
341
+ "args": ["-y", "driverge-mcp"],
342
+ "env": { "DRIVERGE_OUT_ROOT": "C:/work/drivers" }
343
+ }
344
+ }
345
+ }
346
+ ```
102
347
 
103
- Until the npm package is published:
348
+ Without `out_dir` the generated files are returned in the tool result only —
349
+ no disk writes, no configuration needed.
350
+
351
+ ### Windows & npx notes
352
+
353
+ The config blocks above (writing `.mcp.json` / `claude_desktop_config.json`
354
+ **directly**) are the most reliable way to add Driverge on Windows. A few
355
+ environment-specific snags worth knowing:
356
+
357
+ - **Prefer editing the config file over `claude mcp add … -- npx -y driverge-mcp`.**
358
+ The `-y` after `npx` can be parsed by the `claude` CLI itself
359
+ (`unknown option '-y'`) rather than passed through. Writing the JSON block
360
+ directly sidesteps it. (`-y` still belongs in the `args` array, as shown above —
361
+ it only misbehaves as a bare CLI flag.)
362
+ - **PowerShell 5.1 + `claude mcp add-json`.** Nested double-quotes in the inline
363
+ JSON can get mangled before the CLI sees them (`Invalid configuration: Invalid
364
+ input`). Again, write the `.mcp.json` file directly instead of passing JSON on
365
+ the command line.
366
+ - **Spawning the server yourself?** On Windows, launching `npx.cmd` from Node
367
+ needs `shell: true` (otherwise `spawn EINVAL`) — Node no longer runs `.cmd`
368
+ shims without a shell. MCP clients handle this for you; this only bites custom
369
+ smoke-test scripts.
370
+ - **"Pending approval" in `claude mcp list`.** A project-scope `.mcp.json` may show
371
+ as `⏸ Pending approval` in a separate `claude mcp list` process while the
372
+ `driverge` tools are already callable in your active session — the listing lags
373
+ the running session, it does not mean the server failed to load.
374
+
375
+ ### Run from source (development)
376
+
377
+ To contribute, or to run the latest unreleased changes:
104
378
 
105
379
  ```bash
106
380
  git clone https://github.com/MehmetTopuz/driverge-mcp.git
@@ -124,8 +398,13 @@ Give your MCP client a datasheet and ask it to build a driver. The typical flow:
124
398
 
125
399
  1. **`analyze_datasheet`** — `{ "pdf_path": "/abs/path/bme280.pdf" }` → returns a
126
400
  compact summary and a `ref` handle; the full JSON is available as a resource.
401
+ If auto-detection picks the wrong vendor or interface style, the optional
402
+ `manufacturer_hint` (free text) and `interface_kind_hint`
403
+ (`"register_map"` | `"command_set"`) parameters steer it.
127
404
  2. **`generate_driver`** — `{ "ref": "…", "target": "portable" }` → returns the
128
- driver files + a `fill_in_brief`. (`out_dir` also writes them to disk.)
405
+ driver files + a `fill_in_brief`. (`out_dir` also writes them to disk;
406
+ `language: "cpp"` renders a class-based `.hpp`/`.cpp` driver instead of the
407
+ default C.)
129
408
  3. The host AI completes the `TODO(driverge)` markers using the brief and the
130
409
  `driverge://datasheet/<ref>` resource.
131
410
  4. **`validate_driver`** — `{ "ref": "…", "files": [...] }` → static checks; loop
@@ -133,16 +412,49 @@ Give your MCP client a datasheet and ask it to build a driver. The typical flow:
133
412
 
134
413
  Reusing the same `ref` with a different `target` re-renders with **no re-parse**.
135
414
 
415
+ **Completing a `deferred` datasheet.** When `analyze_datasheet` reports
416
+ `extraction: deferred` (the register/command section was detected but not
417
+ auto-extracted — common for split product-spec/register-map documents), the host
418
+ AI reconstructs the map from the `driverge://datasheet/<ref>` resource and
419
+ **persists it back** with `validate_datasheet({ "ref": "…", "json": { … } })` —
420
+ passing **both** `ref` and the completed `json` overwrites the cached datasheet
421
+ under that `ref`. The next `generate_driver({ "ref": "…" })` then renders the real
422
+ registers instead of a TODO stub. This closes the loop without re-analyzing.
423
+
136
424
  ### Worked example — BME280 → portable driver
137
425
 
138
426
  > "Analyze the datasheet at `C:/ds/bme280.pdf` with driverge, then generate a
139
427
  > portable driver."
140
428
 
141
- Driverge parses the BME280 memory map (14 registers, bit-fields, I²C address),
142
- validates it, and emits `bme280.h` / `bme280.c` — register `#define`s, bit-field
143
- `MASK`/`SHIFT` macros, the thin-HAL seam, and `bme280_init/read_register/
144
- write_register` stubs. The host AI then fills the init sequence and compensation
145
- docs from the datasheet prose.
429
+ Driverge parses the BME280 memory map (16 registers, 19 bit-fields, I²C
430
+ address), validates it, and emits `bme280.h` / `bme280.c` — register
431
+ `#define`s, bit-field `MASK`/`SHIFT` macros, the thin-HAL seam, and the
432
+ `bme280_init/read_register/write_register` stubs:
433
+
434
+ ```c
435
+ /* bme280.h — generated by Driverge (excerpt) */
436
+ #define BME280_I2C_ADDR 0x76
437
+
438
+ #define BME280_REG_CTRL_MEAS 0xF4
439
+ #define BME280_REG_STATUS 0xF3
440
+ /* … */
441
+ #define BME280_TEMP_XLSB_TEMP_XLSB_MASK 0xF0
442
+ #define BME280_TEMP_XLSB_TEMP_XLSB_SHIFT 4
443
+ ```
444
+
445
+ ```c
446
+ /* bme280.c — every reasoning gap is a marked TODO (excerpt) */
447
+ int bme280_init(bme280_t *dev) {
448
+ /* … */
449
+ /* TODO(driverge): implement the power-on / reset init sequence — the
450
+ * correct register write order and values, plus any required startup
451
+ * delay. See fill_in_brief.init_sequence_todo. */
452
+ return 0;
453
+ }
454
+ ```
455
+
456
+ The host AI then fills the init sequence and compensation docs from the
457
+ datasheet prose, and `validate_driver` checks the result.
146
458
 
147
459
  ### MCP surface
148
460
 
@@ -151,17 +463,44 @@ docs from the datasheet prose.
151
463
  | Tool | `analyze_datasheet` | PDF → validated JSON, cached under a `ref` |
152
464
  | Tool | `generate_driver` | `ref` + `target` → driver skeleton + `fill_in_brief` |
153
465
  | Tool | `validate_driver` | static-lint a completed driver against its `ref` |
154
- | Tool | `validate_datasheet` | re-run the L5 validator over a `ref` or JSON |
466
+ | Tool | `validate_datasheet` | re-run the L5 validator over a `ref` or JSON; passing **both** `ref` + `json` persists the completed datasheet under that `ref` |
467
+ | Tool | `ping` | health check — confirms the server is running |
155
468
  | Resource | `driverge://datasheet/<ref>` | full parsed JSON for an analyzed datasheet |
156
469
  | Resource | `driverge://schema` | the frozen datasheet JSON-Schema contract |
157
470
  | Prompt | `generate-driver` | guided analyze → generate → fill → validate flow |
158
471
 
472
+ ## Troubleshooting
473
+
474
+ - **Is the server even running?** Ask your client to run the `ping` tool — it
475
+ replies `pong`. If npx fails to start, check `node --version` (≥ 18 required).
476
+ - **Scanned / image-only PDFs.** Driverge parses text-based PDFs; scanned or
477
+ mixed PDFs are detected and reported with an explicit warning, and parsing
478
+ will be incomplete (OCR is deferred to a future release).
479
+ - **`extraction: partial` or `deferred` is not a failure.** `partial` means the
480
+ registers were found but detail (e.g. bit-fields) is incomplete; `deferred`
481
+ means the register/command section was detected but not auto-extracted. In
482
+ both cases the generated skeleton tells the host AI exactly what to complete
483
+ from the datasheet resource. Only `none` is a genuine parse failure.
484
+ - **`out_dir "…" escapes the allowed root`.** Disk writes are confined under
485
+ `DRIVERGE_OUT_ROOT` (default: the server's working directory) — see
486
+ [Configuration](#configuration).
487
+ - **`UnsupportedBusError` on a native target.** The target doesn't support that
488
+ part's bus yet (today that means CAN on STM32, or a bus the parser couldn't
489
+ identify). Generate the **portable** target instead and implement its seam.
490
+
159
491
  ## Roadmap
160
492
 
161
493
  - **v0.x** — one reference sensor (BME280), portable thin-HAL core, MCP surface,
162
- multiple clients. *(current)*
163
- - **v0.y** — native targets: ESP32 ✅, STM32 ✅, Arduino (next).
164
- - **v1.0**multi-manufacturer coverage and a stable, versioned JSON schema.
494
+ multiple clients.
495
+ - **v0.y** — native codegen: ESP32 ✅, STM32 *(on-hardware verification is the
496
+ beta → v0.1.0 gate see [Maturity & status](#maturity--status))*, Arduino (next);
497
+ multi-manufacturer extraction ✅ (12 vendors tested — see
498
+ [Verified parts](#verified-parts)); multi-bus seam families (I²C, SPI, UART,
499
+ CAN) ✅; C or C++ output ✅. *(current)*
500
+ - **v1.0** — broader vendor/part coverage, STM32 CAN (bxCAN/FDCAN), and a
501
+ stable, versioned JSON schema.
502
+
503
+ Day-to-day progress is tracked in the [CHANGELOG](CHANGELOG.md).
165
504
 
166
505
  ## Contributing
167
506
 
@@ -1,4 +1,12 @@
1
1
  import type { DatasheetJson } from "../schema/types.js";
2
- import type { DriverArtifact } from "./types.js";
3
- /** Portable core + an ESP-IDF seam implementation for the same part. */
4
- export declare function generateEsp32Driver(json: DatasheetJson): DriverArtifact;
2
+ import type { CodegenLanguage, DriverArtifact } from "./types.js";
3
+ /**
4
+ * Portable core + an ESP-IDF seam implementation for the same part. With
5
+ * language "c" (default) the seam is `<slug>_hal_esp32.c`, unchanged from
6
+ * before. With language "cpp" the seam becomes `<slug>_hal_esp32.cpp` and its
7
+ * core include is `#include "<slug>.hpp"` (see coreInclude/seamPath above) —
8
+ * everything else in the seam content is identical either way.
9
+ */
10
+ export declare function generateEsp32Driver(json: DatasheetJson, opts?: {
11
+ language?: CodegenLanguage;
12
+ }): DriverArtifact;