driverge-mcp 0.1.0-beta.1 โ†’ 0.1.0-beta.3

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 (41) hide show
  1. package/README.md +145 -196
  2. package/dist/codegen/esp32.d.ts +8 -5
  3. package/dist/codegen/esp32.js +124 -54
  4. package/dist/codegen/esp32.js.map +1 -1
  5. package/dist/codegen/ident.d.ts +9 -0
  6. package/dist/codegen/ident.js +11 -0
  7. package/dist/codegen/ident.js.map +1 -1
  8. package/dist/codegen/lint.js +61 -9
  9. package/dist/codegen/lint.js.map +1 -1
  10. package/dist/codegen/portable-cpp.js +50 -42
  11. package/dist/codegen/portable-cpp.js.map +1 -1
  12. package/dist/codegen/portable.d.ts +20 -10
  13. package/dist/codegen/portable.js +166 -117
  14. package/dist/codegen/portable.js.map +1 -1
  15. package/dist/codegen/stm32.d.ts +8 -5
  16. package/dist/codegen/stm32.js +123 -53
  17. package/dist/codegen/stm32.js.map +1 -1
  18. package/dist/mcp/cache.js +1 -1
  19. package/dist/mcp/cache.js.map +1 -1
  20. package/dist/mcp/register.js +17 -3
  21. package/dist/mcp/register.js.map +1 -1
  22. package/dist/pdf/manufacturer.js +12 -0
  23. package/dist/pdf/manufacturer.js.map +1 -1
  24. package/dist/pdf/microchip-summary-table.d.ts +10 -0
  25. package/dist/pdf/microchip-summary-table.js +300 -0
  26. package/dist/pdf/microchip-summary-table.js.map +1 -0
  27. package/dist/pdf/onsemi-register-table.d.ts +7 -0
  28. package/dist/pdf/onsemi-register-table.js +271 -0
  29. package/dist/pdf/onsemi-register-table.js.map +1 -0
  30. package/dist/pdf/part.js +3 -0
  31. package/dist/pdf/part.js.map +1 -1
  32. package/dist/pdf/ti-field-descriptions.js +9 -3
  33. package/dist/pdf/ti-field-descriptions.js.map +1 -1
  34. package/dist/pdf/ti-register-map.js +44 -8
  35. package/dist/pdf/ti-register-map.js.map +1 -1
  36. package/dist/schema/assemble.js +15 -0
  37. package/dist/schema/assemble.js.map +1 -1
  38. package/dist/schema/validate.js +22 -0
  39. package/dist/schema/validate.js.map +1 -1
  40. package/package.json +1 -1
  41. package/schemas/datasheet.schema.json +4 -1
package/README.md CHANGED
@@ -4,23 +4,27 @@
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
- <a href="https://www.npmjs.com/package/driverge-mcp"><img alt="npm" src="https://img.shields.io/npm/v/driverge-mcp"></a>
7
+ <a href="https://www.npmjs.com/package/driverge-mcp"><img alt="npm (beta)" src="https://img.shields.io/npm/v/driverge-mcp/beta"></a>
8
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
9
  <a href="LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-blue"></a>
10
10
  <img alt="status" src="https://img.shields.io/badge/status-closed%20beta-orange">
11
11
  <img alt="mcp" src="https://img.shields.io/badge/MCP-server-black">
12
12
  </p>
13
13
 
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.
14
+ > ๐Ÿงช **Closed beta** โ€” install with **`npm i driverge-mcp@beta`** or run directly
15
+ > with `npx -y driverge-mcp@beta`. Generated drivers are reviewed *drafts*, not
16
+ > hardware-certified firmware, and the JSON schema may still change before the
17
+ > stable **v0.1.0** โ€” see [Maturity & status](#maturity--status), and
18
+ > [BETA.md](BETA.md) if you're testing.
21
19
 
22
20
  ---
23
21
 
22
+ **Contents:** [What is Driverge?](#what-is-driverge) ยท
23
+ [Quick start](#quick-start) ยท [Why Driverge?](#why-driverge) ยท
24
+ [What it does](#what-it-does) ยท [Maturity & status](#maturity--status) ยท
25
+ [Concepts](#concepts-behind-driverge) ยท [Installation](#installation) ยท
26
+ [Usage](#usage) ยท [Troubleshooting](#troubleshooting) ยท [Roadmap](#roadmap)
27
+
24
28
  ## What is Driverge?
25
29
 
26
30
  **Driverge is a client-agnostic [MCP](https://modelcontextprotocol.io) server**
@@ -28,10 +32,14 @@ that turns an IC datasheet PDF into an embedded C/C++ driver. It plugs into any
28
32
  MCP-capable host โ€” Claude Desktop, Claude Code (VS Code), Cursor, and others.
29
33
 
30
34
  Its guiding principle: **deterministic code parses and validates; the host AI
31
- reasons.** Driverge itself contains **no internal LLM and needs no API keys** โ€” a
32
- TypeScript pipeline extracts a *validated, structured JSON* model of the chip, and
33
- the host AI you're already talking to fills in the reasoning-heavy parts (init
34
- sequence, vendor quirks, docs). Your datasheet never leaves your machine.
35
+ reasons.** Driverge contains **no internal LLM and needs no API keys** โ€” a
36
+ TypeScript pipeline extracts a *validated, structured JSON* model of the chip,
37
+ and the host AI you're already talking to fills in the reasoning-heavy parts
38
+ (init sequence, vendor quirks, docs). Your datasheet never leaves your machine.
39
+
40
+ <p align="center">
41
+ <img src="assets/driverge-flow.png" alt="How Driverge works: a datasheet PDF flows through the deterministic analyze โ†’ frozen JSON โ†’ generate โ†’ validate pipeline; the host AI fills the TODO(driverge) gaps and the loop repeats until validation passes, producing driver.c/.h" width="760">
42
+ </p>
35
43
 
36
44
  ## Quick start
37
45
 
@@ -58,30 +66,22 @@ That's it โ€” no clone, no build, no API key. Details in
58
66
 
59
67
  Bringing up a new sensor or IC means hand-transcribing dozens of register
60
68
  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.
69
+ and a classic source of silent bugs (one wrong mask and the driver "works" but
70
+ reads garbage). Driverge does that mechanical part deterministically and leaves
71
+ the judgment to the AI you already use.
72
+
73
+ - **No hallucinated register maps.** Addresses, masks, and command codes are
74
+ *extracted from the datasheet and validated* โ€” not guessed. Invalid or
75
+ incomplete data is rejected before it can become code.
76
+ - **Bring your own client โ€” private by design.** A plain MCP server with no
77
+ embedded LLM: it reasons with the model you're already paying for, and the
78
+ datasheet is parsed locally, never uploaded โ€” safe for NDA'd parts.
75
79
  - **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.
80
+ the same driver skeleton โ€” reviewable, diff-able, and testable.
78
81
  - **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.
82
+ tiny per-bus thin-HAL seam; native targets (STM32, ESP32) pre-fill it. The AI
83
+ completes only the marked `TODO(driverge)` gaps, and `validate_driver` checks
84
+ the result.
85
85
 
86
86
  **Good for:** quickly evaluating a new sensor, prototyping, porting an existing
87
87
  driver to a different MCU, or just learning an unfamiliar chip's register map.
@@ -93,43 +93,40 @@ driver to a different MCU, or just learning an unfamiliar chip's register map.
93
93
  commands + CRC) and the bus protocol into a **frozen JSON contract**, gated by
94
94
  a validator.
95
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`
98
- telling the host AI exactly what to complete.
99
- 3. **Validate** the completed driver: thin-HAL purity, no leftover TODOs, register
100
- references exist, bit-field masks match the JSON.
96
+ **thin-HAL skeleton** โ€” register/bit-field constants, the per-bus seam,
97
+ function stubs โ€” with every reasoning gap marked `TODO(driverge)` plus a
98
+ `fill_in_brief` telling the host AI exactly what to complete.
99
+ 3. **Validate** the completed driver: thin-HAL purity, no leftover TODOs,
100
+ register references exist, bit-field masks match the JSON.
101
101
 
102
102
  ### Supported targets
103
103
 
104
- Every target specializes the same portable **[thin-HAL](https://en.wikipedia.org/wiki/Hardware_abstraction_layer)**
105
- seam โ€” the driver core is identical across platforms; only the seam implementation
104
+ Every target specializes the same portable
105
+ **[thin-HAL](https://en.wikipedia.org/wiki/Hardware_abstraction_layer)** seam โ€”
106
+ the driver core is identical across platforms; only the seam implementation
106
107
  changes.
107
108
 
108
109
  | Target | Bus binding | Buses | Language | Maturity |
109
110
  |---|---|---|---|---|
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) | IยฒC, SPI, UART, CAN | C / C++ | **Experimental** |
112
- | **STM32** | CubeHAL (`HAL_I2C_*`, `HAL_SPI_*` + GPIO CS, `HAL_UART_*`) | IยฒC, SPI, UART | C / C++ | **Experimental** |
111
+ | **Portable (thin-HAL)** | user-implemented `<part>_hal_*` seam | IยฒC, SPI, UART, CAN | C / C++ | **Beta** โ€” host-tested, gcc-compiled in CI; not yet on hardware |
112
+ | **ESP32** | ESP-IDF (`i2c_master_*`, `spi_master`, `uart`, TWAI) | IยฒC, SPI, UART, CAN | C / C++ | **Experimental** โ€” one informal IยฒC bring-up; SPI/UART/CAN never on hardware |
113
+ | **STM32** | CubeHAL (`HAL_I2C_*`, `HAL_SPI_*` + GPIO CS, `HAL_UART_*`) | IยฒC, SPI, UART | C / C++ | **Experimental** โ€” 3 hardware field tests (NUCLEO-G474RE, hand-completed); no automated compile gate yet |
113
114
  | **Arduino** | `Wire` / `SPI` | โ€” | C++ | not implemented |
114
115
 
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
116
+ STM32 CAN is planned (the bxCAN/FDCAN family split needs its own pass), and a
117
+ next wave of native targets โ€” RP2040/RP2350, TI MSPM0, NXP MCX/i.MX RT,
118
+ Nuvoton NuMicro, Microchip PIC, and eventually Zephyr โ€” is on the
119
+ [Roadmap](#roadmap). Asking a target for a bus it doesn't support fails fast
120
+ with a clear `UnsupportedBusError` rather than emitting a wrong seam. Pass
120
121
  `language: "cpp"` to `generate_driver` for a class-based C++ driver
121
122
  (`.hpp`/`.cpp`) instead of the default C output โ€” same registers, same seam,
122
- same validation.
123
-
124
- > โš ๏ธ **Generated code is a strong draft, not a certified driver.** Init sequences,
125
- > compensation formulas, and timing quirks are completed by the host AI and
126
- > **must be reviewed** before use on hardware. Not safety-certified.
123
+ same validation. What "Beta" and "Experimental" mean exactly is spelled out in
124
+ [Maturity & status](#maturity--status).
127
125
 
128
126
  ### Verified parts
129
127
 
130
128
  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):
129
+ **12 manufacturers**. Parts with fully automatic extraction:
133
130
 
134
131
  | Part | Manufacturer | Kind | Extracted |
135
132
  |---|---|---|---|
@@ -141,52 +138,40 @@ bit-fields, or a clean command set):
141
138
  | LSM6DSRX | STMicroelectronics | register map | 31 regs, 91 bit-fields |
142
139
  | MAX30102 | Maxim Integrated | register map | 20 regs, 33 bit-fields |
143
140
 
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
141
+ Other tested parts (ADXL345, MLX90614, AEAT-8811, PCA9685, VL53L3CX, TLE5014,
142
+ TCA6408A) extract **partially** or **defer** to the host AI โ€” the pipeline says so
143
+ explicitly instead of guessing. The full, always-current matrix lives in the
148
144
  [coverage scorecard](tests/scorecard/scorecard.snap.md).
149
145
 
150
146
  ## Maturity & status
151
147
 
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.
148
+ Driverge is in **closed beta** (`0.1.0-beta.x`, npm `beta` dist-tag).
155
149
 
156
150
  **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)).
151
+ - The full deterministic test suite is green (540-plus tests) on a clean
152
+ TypeScript build, and the **portable** driver is compiled by a real `gcc`
153
+ gate in CI (including a multi-driver link gate).
154
+ - The extraction pipeline is regression-tested against **14 real datasheets**
155
+ and reports its own coverage honestly โ€” 7 fully extracted, 4 partial, 3
156
+ deferred (see the [coverage scorecard](tests/scorecard/scorecard.snap.md)).
164
157
 
165
158
  **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
159
+ - **On-hardware behavior is not gated by CI.** STM32 has now run on real
160
+ hardware โ€” three NUCLEO-G474RE field tests (onsemi FXL6408 IยฒC, TI TUSS4470
161
+ SPI, Microchip CAP1206 IยฒC), each hand-completed and folded back into the
162
+ generator โ€” and ESP32 has one informal IยฒC bring-up (MPU-9250). There is still
163
+ no clean, repeatable automated hardware pass, so every generated driver needs
164
+ review before you flash it.
165
+ - The native ESP32/STM32 seams are not yet built by an automated compile gate
171
166
  (ESP-IDF / CubeIDE) โ€” only the portable target is.
172
167
  - Only one MCP client has been exercised end-to-end.
173
168
 
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.
169
+ > โš ๏ธ Treat every generated driver as a **reviewed draft**: check register
170
+ > addresses, the init sequence, and any compensation math against the datasheet
171
+ > before you flash it. Driverge is not safety-certified.
186
172
 
187
173
  **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.
174
+ run and how to send a field report.
190
175
 
191
176
  ## Concepts behind Driverge
192
177
 
@@ -194,34 +179,16 @@ Driverge splits driver-writing into two kinds of work: the **mechanical part**
194
179
  (register addresses, masks, command tables โ€” extracted and checked by
195
180
  deterministic code) and the **judgment part** (init ordering, timing quirks,
196
181
  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
- ```
182
+ that boundary sharp; the diagram at the top of this page shows how the pieces
183
+ connect.
216
184
 
217
185
  ### Deterministic core, reasoning at the edge
218
186
 
219
187
  Register geometry is mechanical: an address is right or wrong, a mask either
220
188
  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.
189
+ TypeScript โ€” no sampling, so the output is the same on every run. What genuinely
190
+ needs judgment (in what order to poke the registers, which timing quirk applies)
191
+ is left to the host AI.
225
192
 
226
193
  ### The frozen JSON contract
227
194
 
@@ -244,30 +211,27 @@ re-parse, and the full JSON stays readable at `driverge://datasheet/<ref>`.
244
211
  ### The thin-HAL seam
245
212
 
246
213
  Generated drivers touch hardware through a tiny per-bus seam โ€” and nothing
247
- else:
214
+ else. Seam symbols are **prefixed with the part's slug** (e.g.
215
+ `bme280_hal_i2c_read`) so two Driverge drivers can link into one firmware
216
+ image without symbol collisions:
248
217
 
249
- | Bus | Seam functions (plus `hal_delay_ms`) |
218
+ | Bus | Seam functions (plus `<part>_hal_delay_ms`) |
250
219
  |---|---|
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.
220
+ | IยฒC | `<part>_hal_i2c_read`, `<part>_hal_i2c_write` |
221
+ | SPI | `<part>_hal_spi_transfer(tx, rx, len)` (one call = one CS-framed **full-duplex** transaction; `rx` may be `NULL` for write-only, and write-then-read devices are served by padding `tx` with dummy bytes) |
222
+ | UART | `<part>_hal_uart_write`, `<part>_hal_uart_read` |
223
+ | CAN | `<part>_hal_can_transfer` (one call = one frame exchange) |
224
+
225
+ The transfer seams return `int` โ€” **`0` on success, non-zero on a bus error**
226
+ (NACK, timeout) โ€” and the generated register accessors propagate that status
227
+ instead of swallowing it; native seams (ESP32, STM32) return their vendor
228
+ status, which is already `0` on success. Native targets also emit a seam
229
+ companion header (`<part>_hal_stm32.h` / `<part>_hal_esp32.h`) declaring the
230
+ one-time `bind` call that points the seam at your peripheral handle.
231
+ `validate_driver` enforces seam purity: a driver that calls a vendor
232
+ peripheral API outside the seam fails the lint. Buses with no universal
233
+ register-access primitive (UART, CAN) get their device-specific framing as a
234
+ marked `TODO(driverge)` gap, completed by the host AI and then linted.
271
235
 
272
236
  ### The fill-in loop
273
237
 
@@ -281,28 +245,17 @@ matching the JSON โ€” and the loop repeats until it passes.
281
245
 
282
246
  **Prerequisites:** Node.js LTS (โ‰ฅ 18; CI-tested on Node 20 & 22).
283
247
 
284
- Add Driverge to your MCP client (no build step โ€”
285
- [npx](https://docs.npmjs.com/cli/commands/npx) fetches and runs it):
248
+ Install the beta from npm:
286
249
 
287
- **Claude Desktop** โ€” `claude_desktop_config.json`:
288
- ```json
289
- {
290
- "mcpServers": {
291
- "driverge": { "command": "npx", "args": ["-y", "driverge-mcp"] }
292
- }
293
- }
250
+ ```bash
251
+ npm i driverge-mcp@beta
294
252
  ```
295
253
 
296
- **Claude Code (VS Code)** โ€” `.mcp.json` in your workspace root:
297
- ```json
298
- {
299
- "mcpServers": {
300
- "driverge": { "command": "npx", "args": ["-y", "driverge-mcp"] }
301
- }
302
- }
303
- ```
254
+ Or skip the install entirely โ€” the config below launches Driverge via
255
+ [npx](https://docs.npmjs.com/cli/commands/npx), which downloads it from npm on
256
+ first run, caches it, and starts it automatically each time the client does
257
+ (`-y` skips npx's install prompt). Add one entry to your MCP client's config:
304
258
 
305
- **Cursor** โ€” `.cursor/mcp.json`:
306
259
  ```json
307
260
  {
308
261
  "mcpServers": {
@@ -311,25 +264,29 @@ Add Driverge to your MCP client (no build step โ€”
311
264
  }
312
265
  ```
313
266
 
314
- Other clients (Codex, Gemini CLI, โ€ฆ) take the same `command` + `args` pair in
315
- their own MCP config.
267
+ | Client | Config file |
268
+ |---|---|
269
+ | Claude Desktop | `claude_desktop_config.json` |
270
+ | Claude Code (VS Code) | `.mcp.json` in your workspace root |
271
+ | Cursor | `.cursor/mcp.json` |
272
+ | Others (Codex, Gemini CLI, โ€ฆ) | same `command` + `args` pair in their own MCP config |
316
273
 
317
274
  > **Beta testers:** pin the beta channel explicitly with `["-y",
318
275
  > "driverge-mcp@beta"]` as the `args`. Plain `driverge-mcp` resolves the npm
319
276
  > `latest` tag, which deliberately lags the beta during closed beta.
320
277
 
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.
278
+ You never run the server by hand โ€” to confirm it's wired up, ask your client to
279
+ run the `ping` tool, which replies `pong`. On Windows, prefer writing the
280
+ config file directly over `claude mcp add` โ€” see
281
+ [Troubleshooting](#troubleshooting). Cloning the repo (below) is only for
282
+ development.
327
283
 
328
284
  ### Configuration
329
285
 
330
286
  | Env var | Default | Purpose |
331
287
  |---|---|---|
332
288
  | `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. |
289
+ | `DRIVERGE_MAX_PDF_BYTES` | `67108864` (64 MiB) | Maximum PDF size `analyze_datasheet` will read. A larger file is rejected with a clear "PDF too large" error before any parsing โ€” bounds memory use on a hostile or accidentally huge input. |
333
290
 
334
291
  Set it in the MCP config's `env` block, e.g.:
335
292
 
@@ -348,30 +305,6 @@ Set it in the MCP config's `env` block, e.g.:
348
305
  Without `out_dir` the generated files are returned in the tool result only โ€”
349
306
  no disk writes, no configuration needed.
350
307
 
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
308
  ### Run from source (development)
376
309
 
377
310
  To contribute, or to run the latest unreleased changes:
@@ -413,13 +346,11 @@ Give your MCP client a datasheet and ask it to build a driver. The typical flow:
413
346
  Reusing the same `ref` with a different `target` re-renders with **no re-parse**.
414
347
 
415
348
  **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.
349
+ `extraction: deferred`, the host AI reconstructs the register map from the
350
+ `driverge://datasheet/<ref>` resource and persists it back with
351
+ `validate_datasheet({ "ref": "โ€ฆ", "json": { โ€ฆ } })` โ€” passing **both** `ref` and
352
+ the completed `json` overwrites the cached datasheet under that `ref`, so the
353
+ next `generate_driver` renders the real registers instead of a TODO stub.
423
354
 
424
355
  ### Worked example โ€” BME280 โ†’ portable driver
425
356
 
@@ -463,7 +394,7 @@ datasheet prose, and `validate_driver` checks the result.
463
394
  | Tool | `analyze_datasheet` | PDF โ†’ validated JSON, cached under a `ref` |
464
395
  | Tool | `generate_driver` | `ref` + `target` โ†’ driver skeleton + `fill_in_brief` |
465
396
  | Tool | `validate_driver` | static-lint a completed driver against its `ref` |
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` |
397
+ | Tool | `validate_datasheet` | re-run the validator over a `ref` or JSON; passing **both** persists the completed datasheet under that `ref` |
467
398
  | Tool | `ping` | health check โ€” confirms the server is running |
468
399
  | Resource | `driverge://datasheet/<ref>` | full parsed JSON for an analyzed datasheet |
469
400
  | Resource | `driverge://schema` | the frozen datasheet JSON-Schema contract |
@@ -487,18 +418,36 @@ datasheet prose, and `validate_driver` checks the result.
487
418
  - **`UnsupportedBusError` on a native target.** The target doesn't support that
488
419
  part's bus yet (today that means CAN on STM32, or a bus the parser couldn't
489
420
  identify). Generate the **portable** target instead and implement its seam.
421
+ - **Windows: prefer editing the config file over the `claude mcp add` CLI.**
422
+ `claude mcp add โ€ฆ -- npx -y driverge-mcp` can eat the `-y` itself (`unknown
423
+ option '-y'`), and PowerShell 5.1 mangles the nested quotes in `claude mcp
424
+ add-json`. Writing the JSON block directly into `.mcp.json` /
425
+ `claude_desktop_config.json` sidesteps both.
426
+ - **Windows: spawning the server from your own script.** Launching `npx.cmd`
427
+ from Node needs `shell: true` (otherwise `spawn EINVAL`). MCP clients handle
428
+ this for you; it only bites custom smoke-test scripts.
429
+ - **"Pending approval" in `claude mcp list`.** A project-scope `.mcp.json` may
430
+ show as `โธ Pending approval` in a separate `claude mcp list` process while
431
+ the `driverge` tools are already callable in your active session โ€” the
432
+ listing lags the running session.
490
433
 
491
434
  ## Roadmap
492
435
 
493
436
  - **v0.x** โ€” one reference sensor (BME280), portable thin-HAL core, MCP surface,
494
437
  multiple clients. โœ…
495
438
  - **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)*
439
+ beta โ†’ v0.1.0 gate)*, Arduino (next); multi-manufacturer extraction โœ…
440
+ (12 vendors โ€” see [Verified parts](#verified-parts)); multi-bus seam families
441
+ (IยฒC, SPI, UART, CAN) โœ…; C or C++ output โœ…. *(current)*
500
442
  - **v1.0** โ€” broader vendor/part coverage, STM32 CAN (bxCAN/FDCAN), and a
501
443
  stable, versioned JSON schema.
444
+ - **v1.x** โ€” new native MCU targets (same portable thin-HAL core, one seam
445
+ file per platform; prioritized by beta-tester demand): **RP2040/RP2350**
446
+ (pico-sdk), **TI MSPM0** (DriverLib), **NXP MCX / i.MX RT** (MCUXpresso
447
+ `fsl_lpi2c`/`fsl_lpspi`), **Nuvoton NuMicro** (BSP Standard Driver), and
448
+ **Microchip PIC** (MCC Melody for 8/16-bit; Harmony v3 for PIC32).
449
+ - **Later** โ€” a **Zephyr** meta-target (devicetree-based `i2c`/`spi` API):
450
+ one seam covering Nordic nRF and every other Zephyr-supported vendor.
502
451
 
503
452
  Day-to-day progress is tracked in the [CHANGELOG](CHANGELOG.md).
504
453
 
@@ -1,11 +1,14 @@
1
1
  import type { DatasheetJson } from "../schema/types.js";
2
2
  import type { CodegenLanguage, DriverArtifact } from "./types.js";
3
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.
4
+ * Portable core + an ESP-IDF seam implementation + a seam companion header for
5
+ * the same part. With language "c" (default) the seam is `<slug>_hal_esp32.c`;
6
+ * with language "cpp" it is `<slug>_hal_esp32.cpp` and its core include is
7
+ * `#include "<slug>.hpp"` (see coreInclude/seamPath above) โ€” everything else
8
+ * in the seam content is identical either way. The companion header
9
+ * (`<slug>_hal_esp32.h`) is always a plain `.h` file, in BOTH language runs.
10
+ * File order in the artifact: [core header, core source, companion header,
11
+ * seam source] โ€” see tests/codegen/esp32.test.ts.
9
12
  */
10
13
  export declare function generateEsp32Driver(json: DatasheetJson, opts?: {
11
14
  language?: CodegenLanguage;