diff --git a/README.md b/README.md index b1b2f24ccbd..d2f82e766fb 100644 --- a/README.md +++ b/README.md @@ -85,15 +85,20 @@ older builds are on the > "mcpServers": { > "arduino": { > "command": "node", - > "args": ["/path/to/arduino-mcp-extension/bridge/arduino-agent-bridge.js"] + > "args": ["/resources/mcp-bridge/bridge/arduino-agent-bridge.js"] > } > } > } > ``` > - > It needs no token configuration (it reads `~/.arduinoIDE/mcp-token` itself) - > and no dependencies. See [the bridge README](arduino-mcp-extension/bridge/README.md) - > for auto-launch and other options. + > Releases after v0.6.1 ship the bridge inside the app: `` is the + > unzipped folder on Windows/Linux; on macOS the path is + > `Arduino IDE.app/Contents/Resources/mcp-bridge/bridge/arduino-agent-bridge.js`. + > From a source checkout, use `arduino-mcp-extension/bridge/arduino-agent-bridge.js` + > after building the extension. It needs no token configuration (it reads + > `~/.arduinoIDE/mcp-token` itself) and no dependencies. See + > [the bridge README](arduino-mcp-extension/bridge/README.md) for auto-launch + > and other options. > **Claude Code users:** the server sends workflow guidance automatically, > and ships three slash commands (`/bringup`, `/debug-serial`, diff --git a/arduino-mcp-extension/bridge/README.md b/arduino-mcp-extension/bridge/README.md index 9e737259d9b..1038ced373f 100644 --- a/arduino-mcp-extension/bridge/README.md +++ b/arduino-mcp-extension/bridge/README.md @@ -29,18 +29,28 @@ is closed: "mcpServers": { "arduino": { "command": "node", - "args": ["/path/to/arduino-mcp-extension/bridge/arduino-agent-bridge.js"], + "args": ["/path/to/mcp-bridge/bridge/arduino-agent-bridge.js"], "env": { "ARDUINO_AGENT_PATH": "/path/to/Arduino IDE executable" } } } } ``` +Where to find it: + +| Running from | Bridge path | +|--------------|-------------| +| Release, Windows / Linux | `/resources/mcp-bridge/bridge/arduino-agent-bridge.js` | +| Release, macOS | `Arduino IDE.app/Contents/Resources/mcp-bridge/bridge/arduino-agent-bridge.js` | +| Source checkout | `arduino-mcp-extension/bridge/arduino-agent-bridge.js`, after `yarn build` in `arduino-mcp-extension` | + +Releases after v0.6.1 include the bridge; earlier zips don't, so use a checkout +with those. Either way the bridge sits next to the compiled extension modules it +loads (`../lib/common/`), which supply the offline tool list and answers. + No dependencies (node builtins only) and no token setup — it reads `~/.arduinoIDE/mcp-token` itself, re-reading per request so it survives the IDE -regenerating the token on restart. Run it from a **built** checkout or install -(`yarn build` in `arduino-mcp-extension`): the offline tool list and answers -come from the compiled extension in `lib/`. +regenerating the token on restart. ## Options @@ -62,7 +72,7 @@ Example on Windows: "mcpServers": { "arduino": { "command": "node", - "args": ["C:/path/to/bridge/arduino-agent-bridge.js"], + "args": ["C:/Arduino/resources/mcp-bridge/bridge/arduino-agent-bridge.js"], "env": { "ARDUINO_AGENT_PATH": "C:/Arduino/Arduino IDE.exe" } } } diff --git a/arduino-mcp-extension/test/bridge/bridge.test.js b/arduino-mcp-extension/test/bridge/bridge.test.js index a56995fcd3a..d93e4d9a24c 100644 --- a/arduino-mcp-extension/test/bridge/bridge.test.js +++ b/arduino-mcp-extension/test/bridge/bridge.test.js @@ -310,3 +310,30 @@ test('a rejected token gives a readable tool error, and tools/list still answers await ide.close(); } }); + +test('release packaging ships the bridge with every compiled module it loads', needsLib, () => { + // The bridge loads ../lib/common/ at runtime; electron-builder's + // extraResources must copy each of those (and what they require) next to it. + const loaded = [...fs.readFileSync(BRIDGE, 'utf8').matchAll(/loadCompiled\('([\w-]+)'\)/g)].map((m) => m[1]); + assert.ok(loaded.length > 0); + const needed = new Set(); + const visit = (name) => { + if (needed.has(name)) return; + needed.add(name); + const src = fs.readFileSync(path.join(LIB, `${name}.js`), 'utf8'); + for (const m of src.matchAll(/require\("\.\/([\w-]+)"\)/g)) visit(m[1]); + }; + loaded.forEach(visit); + + const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', '..', 'electron-app', 'package.json'), 'utf8')); + const resources = pkg.build.extraResources; + const bridgeEntry = resources.find((r) => r.from === '../arduino-mcp-extension/bridge'); + const libEntry = resources.find((r) => r.from === '../arduino-mcp-extension/lib/common'); + assert.ok(bridgeEntry && libEntry, 'electron-app extraResources must copy bridge/ and lib/common/'); + assert.ok(bridgeEntry.filter.includes(path.basename(BRIDGE))); + for (const name of needed) { + assert.ok(libEntry.filter.includes(`${name}.js`), `extraResources is missing lib/common/${name}.js`); + } + // Same relative layout as the checkout, so the bridge's ../lib/common still resolves. + assert.strictEqual(path.posix.dirname(bridgeEntry.to), path.posix.dirname(path.posix.dirname(libEntry.to))); +}); diff --git a/electron-app/package.json b/electron-app/package.json index 0d3e168330f..1356afeb71b 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -132,6 +132,23 @@ { "from": "plugins", "to": "app/plugins" + }, + { + "from": "../arduino-mcp-extension/bridge", + "to": "mcp-bridge/bridge", + "filter": [ + "arduino-agent-bridge.js" + ] + }, + { + "from": "../arduino-mcp-extension/lib/common", + "to": "mcp-bridge/lib/common", + "filter": [ + "mcp-instructions.js", + "mcp-prompts.js", + "mcp-tool-router.js", + "mcp-tools.js" + ] } ], "win": { diff --git a/skills/arduino-agent/SKILL.md b/skills/arduino-agent/SKILL.md index ff14a48385b..8338a9983bf 100644 --- a/skills/arduino-agent/SKILL.md +++ b/skills/arduino-agent/SKILL.md @@ -16,8 +16,9 @@ skill adds the judgment that comes from real hardware sessions. If the MCP config points at `http://127.0.0.1:3847/mcp` directly, the whole server shows "failed to connect" whenever the IDE is closed. Prefer the stdio -bridge (`arduino-mcp-extension/bridge/arduino-agent-bridge.js` in the repo or -install): it always connects with the full tool list, starts the IDE when a tool +bridge (`resources/mcp-bridge/bridge/arduino-agent-bridge.js` inside a release +install newer than v0.6.1, or `arduino-mcp-extension/bridge/` in a built +checkout): it always connects with the full tool list, starts the IDE when a tool needs it (with `ARDUINO_AGENT_PATH` set; otherwise it returns "open the IDE" as a normal tool error), and recovers without a client restart.