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.
- package/README.md +371 -32
- package/dist/codegen/esp32.d.ts +11 -3
- package/dist/codegen/esp32.js +226 -15
- package/dist/codegen/esp32.js.map +1 -1
- package/dist/codegen/ident.d.ts +24 -2
- package/dist/codegen/ident.js +37 -4
- package/dist/codegen/ident.js.map +1 -1
- package/dist/codegen/index.d.ts +12 -7
- package/dist/codegen/index.js +17 -14
- package/dist/codegen/index.js.map +1 -1
- package/dist/codegen/lint.js +28 -8
- package/dist/codegen/lint.js.map +1 -1
- package/dist/codegen/portable-cpp.d.ts +8 -0
- package/dist/codegen/portable-cpp.js +280 -0
- package/dist/codegen/portable-cpp.js.map +1 -0
- package/dist/codegen/portable.d.ts +99 -3
- package/dist/codegen/portable.js +420 -99
- package/dist/codegen/portable.js.map +1 -1
- package/dist/codegen/stm32.d.ts +11 -3
- package/dist/codegen/stm32.js +166 -17
- package/dist/codegen/stm32.js.map +1 -1
- package/dist/codegen/types.d.ts +32 -0
- package/dist/codegen/types.js +27 -0
- package/dist/codegen/types.js.map +1 -1
- package/dist/mcp/cache.d.ts +15 -2
- package/dist/mcp/cache.js +32 -4
- package/dist/mcp/cache.js.map +1 -1
- package/dist/mcp/register.d.ts +17 -0
- package/dist/mcp/register.js +173 -22
- package/dist/mcp/register.js.map +1 -1
- package/dist/mcp/summary.js +8 -0
- package/dist/mcp/summary.js.map +1 -1
- package/dist/pdf/address.d.ts +4 -0
- package/dist/pdf/address.js +28 -0
- package/dist/pdf/address.js.map +1 -0
- package/dist/pdf/command.d.ts +21 -0
- package/dist/pdf/command.js +184 -16
- package/dist/pdf/command.js.map +1 -1
- package/dist/pdf/extract.js +50 -3
- package/dist/pdf/extract.js.map +1 -1
- package/dist/pdf/generic-register-table.d.ts +5 -0
- package/dist/pdf/generic-register-table.js +125 -0
- package/dist/pdf/generic-register-table.js.map +1 -0
- package/dist/pdf/index.d.ts +2 -0
- package/dist/pdf/index.js +2 -0
- package/dist/pdf/index.js.map +1 -1
- package/dist/pdf/interface-kind.d.ts +11 -0
- package/dist/pdf/interface-kind.js +18 -0
- package/dist/pdf/interface-kind.js.map +1 -1
- package/dist/pdf/manufacturer.js +39 -0
- package/dist/pdf/manufacturer.js.map +1 -1
- package/dist/pdf/maxim-register-map.d.ts +11 -0
- package/dist/pdf/maxim-register-map.js +544 -0
- package/dist/pdf/maxim-register-map.js.map +1 -0
- package/dist/pdf/part.js +10 -0
- package/dist/pdf/part.js.map +1 -1
- package/dist/pdf/prose-commands.d.ts +4 -0
- package/dist/pdf/prose-commands.js +90 -0
- package/dist/pdf/prose-commands.js.map +1 -0
- package/dist/pdf/register-table.d.ts +4 -0
- package/dist/pdf/register-table.js +4 -2
- package/dist/pdf/register-table.js.map +1 -1
- package/dist/pdf/st-bit-layout.d.ts +3 -0
- package/dist/pdf/st-bit-layout.js +238 -0
- package/dist/pdf/st-bit-layout.js.map +1 -0
- package/dist/pdf/ti-command-byte.d.ts +7 -0
- package/dist/pdf/ti-command-byte.js +165 -0
- package/dist/pdf/ti-command-byte.js.map +1 -0
- package/dist/pdf/ti-field-descriptions.d.ts +10 -0
- package/dist/pdf/ti-field-descriptions.js +87 -0
- package/dist/pdf/ti-field-descriptions.js.map +1 -0
- package/dist/pdf/ti-register-map.d.ts +5 -0
- package/dist/pdf/ti-register-map.js +84 -0
- package/dist/pdf/ti-register-map.js.map +1 -0
- package/dist/pdf/types.d.ts +15 -3
- package/dist/pdf/types.js +4 -1
- package/dist/pdf/types.js.map +1 -1
- package/dist/schema/assemble.d.ts +35 -3
- package/dist/schema/assemble.js +93 -13
- package/dist/schema/assemble.js.map +1 -1
- package/dist/schema/types.d.ts +21 -1
- package/dist/schema/validate.js +44 -10
- package/dist/schema/validate.js.map +1 -1
- package/dist/server.d.ts +18 -0
- package/dist/server.js +54 -3
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- 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="
|
|
8
|
-
<img alt="
|
|
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
|
-
>
|
|
13
|
-
>
|
|
14
|
-
>
|
|
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
|
|
37
|
-
skeleton** — register/bit-field constants, the
|
|
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 |
|
|
50
|
-
|
|
51
|
-
| **Portable (thin-HAL)** | user-implemented `
|
|
52
|
-
| **ESP32** | ESP-IDF `i2c_master_
|
|
53
|
-
| **STM32** | CubeHAL `
|
|
54
|
-
| **Arduino** | `Wire` / `SPI` | C++ |
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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) | 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** |
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
142
|
-
validates it, and emits `bme280.h` / `bme280.c` — register
|
|
143
|
-
`MASK`/`SHIFT` macros, the thin-HAL seam, and
|
|
144
|
-
write_register` stubs
|
|
145
|
-
|
|
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.
|
|
163
|
-
- **v0.y** — native
|
|
164
|
-
|
|
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
|
|
package/dist/codegen/esp32.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { DatasheetJson } from "../schema/types.js";
|
|
2
|
-
import type { DriverArtifact } from "./types.js";
|
|
3
|
-
/**
|
|
4
|
-
|
|
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;
|