Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,15 @@ jobs:
# scripts/package.js, but with Arduino Agent's version: it is the version
# in the window title and About dialog, and buildDate is what makes the
# app load its bundled VS Code plugins (arduino-ide-electron-main.js).
# The name gives the app its own userData folder (and single-instance
# lock), separate from the official Arduino IDE's `arduino-ide`.
shell: bash
run: |
version=$(node -p "require('../arduino-mcp-extension/package.json').version")
cli_version=$(node -p "require('../arduino-ide-extension/package.json').arduino['arduino-cli'].version")
node ../node_modules/electron-builder/cli.js --dir --publish never \
-c.electronVersion=30.1.2 \
-c.extraMetadata.name=arduino-ide \
-c.extraMetadata.name=arduino-agent \
-c.extraMetadata.main=./arduino-ide-electron-main.js \
-c.extraMetadata.version="$version" \
-c.extraMetadata.theia.frontend.config.appVersion="$version" \
Expand All @@ -183,7 +185,9 @@ jobs:
run: |
$src = Get-ChildItem -Directory electron-app/dist |
Where-Object { $_.Name -like '*-unpacked' } | Select-Object -First 1
Compress-Archive -Path "$($src.FullName)/*" -DestinationPath "arduino-ide-mcp-${{ matrix.platform }}.zip"
Compress-Archive -Path "$($src.FullName)/*" -DestinationPath "arduino-agent-${{ matrix.platform }}.zip"
# Pre-0.7.0 asset name, kept so existing download links keep working.
Copy-Item "arduino-agent-${{ matrix.platform }}.zip" "arduino-ide-mcp-${{ matrix.platform }}.zip"

- name: Zip artifact (Linux/macOS)
if: runner.os != 'Windows'
Expand All @@ -193,7 +197,9 @@ jobs:
src=$(ls -d electron-app/dist/*-unpacked 2>/dev/null | head -1 || true)
if [ -z "$src" ]; then src=$(ls -d electron-app/dist/mac* 2>/dev/null | head -1); fi
if [ -z "$src" ]; then echo "No build output found under electron-app/dist"; ls -la electron-app/dist; exit 1; fi
( cd "$src" && zip -ry "$GITHUB_WORKSPACE/arduino-ide-mcp-${{ matrix.platform }}.zip" . )
( cd "$src" && zip -ry "$GITHUB_WORKSPACE/arduino-agent-${{ matrix.platform }}.zip" . )
# Pre-0.7.0 asset name, kept so existing download links keep working.
cp "arduino-agent-${{ matrix.platform }}.zip" "arduino-ide-mcp-${{ matrix.platform }}.zip"

- name: Upload to draft release
uses: softprops/action-gh-release@v2
Expand All @@ -207,10 +213,12 @@ jobs:
Not affiliated with or endorsed by Arduino SA.

Each archive contains the unpacked application for its platform - unzip and run
`Arduino IDE` (`Arduino IDE.exe` on Windows). The MCP server starts on
`Arduino Agent` (`Arduino Agent.exe` on Windows). The MCP server starts on
`http://127.0.0.1:3847`; the connection config (with the auth token) is printed
to the IDE console on launch.

Artifacts are unsigned; on macOS you may need to allow the app in
System Settings > Privacy & Security.
files: arduino-ide-mcp-${{ matrix.platform }}.zip
files: |
arduino-agent-${{ matrix.platform }}.zip
arduino-ide-mcp-${{ matrix.platform }}.zip
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@ classes are `any` in our extension: no `override`, no inherited fields.
## Fast backend-only iteration (Windows dev box)

The MCP extension is bundled into `electron-app/lib/backend/main.js`. To test
a backend change against the installed app at `C:\Arduino` without full
packaging:
a backend change against an installed app (`<install>` below, the unzipped
release folder) without full packaging:

1. `npm run --prefix arduino-mcp-extension build`
2. In `electron-app/`: `node ../node_modules/@theia/cli/bin/theia build
--config webpack.config.js --mode production` (capture FULL output to a
file — piping through tail/grep has repeatedly swallowed the real error).
If it dies with ENOTEMPTY on `lib/backend/native-webpack-plugin`, delete
that directory and rerun (transient Windows file-lock race).
3. Kill "Arduino IDE" processes, copy `electron-app/lib/backend/main.js` over
`C:\Arduino\resources\app\lib\backend\main.js`, relaunch.
3. Kill "Arduino Agent" processes ("Arduino IDE" before 0.7.0), copy
`electron-app/lib/backend/main.js` over
`<install>\resources\app\lib\backend\main.js`, relaunch.
4. Confirm the copy landed by grepping the installed main.js for a distinctive
new string, and check `/health` uptime is small (a stale uptime means the
old process survived the kill).
Expand Down
37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,26 @@ making the IDE a first-class participant in the conversation:

Unsigned development builds of the **[latest release](https://github.com/mixelpixx/Arduino-Agent/releases/latest)** — these links always point at the newest version:

- [**Windows x64**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-ide-mcp-windows.zip)
- [**macOS**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-ide-mcp-macos.zip)
- [**Linux x64**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-ide-mcp-linux.zip)

Unzip and run `Arduino IDE` (`Arduino IDE.exe` on Windows). The window, About
box and icon say **Arduino Agent**; only the executable keeps upstream's name for
now. Arduino Agent doesn't check Arduino's update server; get new versions here.
Release notes and older builds are on the
[Releases](https://github.com/mixelpixx/Arduino-Agent/releases) page.

> **Have the official Arduino IDE installed too?** Both currently share settings
> and a single-instance lock, so starting one while the other is running hands
> off to the one already open. Close one before starting the other. Separating
> them properly is planned for v0.7.0.
- [**Windows x64**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-agent-windows.zip)
- [**macOS**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-agent-macos.zip)
- [**Linux x64**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-agent-linux.zip)

Unzip and run **Arduino Agent** (`Arduino Agent.exe` on Windows,
`Arduino Agent.app` on macOS, `arduino-agent` on Linux). Arduino Agent doesn't
check Arduino's update server; get new versions here. Release notes and older
builds are on the [Releases](https://github.com/mixelpixx/Arduino-Agent/releases)
page.

> **Got the official Arduino IDE too?** They run side by side as separate apps.
> They share what should be shared, your sketchbook, installed boards and
> libraries, and preferences (`~/.arduinoIDE`), but each keeps its own window
> state and opens independently.
>
> **Upgrading from 0.6.x?** The executable was called `Arduino IDE` before 0.7.0.
> Your window layout and per-sketch board selections carry over on first launch,
> and an MCP config whose `ARDUINO_AGENT_PATH` still names `Arduino IDE.exe`
> keeps working (the bridge finds the renamed executable), but update it when
> convenient.

> These are unsigned dev builds. On macOS you may need to allow the app under
> **System Settings → Privacy & Security**; on Windows, dismiss SmartScreen with
Expand Down Expand Up @@ -100,7 +106,8 @@ Release notes and older builds are on the
>
> Releases after v0.6.1 ship the bridge inside the app: `<install>` is the
> unzipped folder on Windows/Linux; on macOS the path is
> `Arduino IDE.app/Contents/Resources/mcp-bridge/bridge/arduino-agent-bridge.js`.
> `Arduino Agent.app/Contents/Resources/mcp-bridge/bridge/arduino-agent-bridge.js`
> (`Arduino IDE.app` in 0.6.2).
> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function enableFileLogger() {
process.env['XDG_CONFIG_HOME'] = xdgConfigHome;
}
setupFileLog({
appName: 'Arduino IDE',
appName: 'Arduino Agent', // Arduino Agent: own log folder, not the official IDE's
maxSize: 10 * 1024 * 1024,
});
for (const name of consoleLogFunctionNames) {
Expand Down Expand Up @@ -893,7 +893,7 @@ async function updateFrontendApplicationConfigFromPackageJson(
}

const fallbackFrontendAppConfig: FrontendApplicationConfig = {
applicationName: 'Arduino IDE',
applicationName: 'Arduino Agent', // Arduino Agent
defaultTheme: {
light: 'arduino-theme',
dark: 'arduino-theme-dark',
Expand All @@ -903,7 +903,7 @@ const fallbackFrontendAppConfig: FrontendApplicationConfig = {
defaultLocale: '',
electron: {
showWindowEarly: true,
uriScheme: 'arduino-ide',
uriScheme: 'arduino-agent', // Arduino Agent
},
reloadOnReconnect: true,
};
Expand Down
2 changes: 1 addition & 1 deletion arduino-mcp-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Expected response:
```json
{
"status": "ok",
"server": "arduino-ide-mcp",
"server": "arduino-agent",
"version": "0.5.0",
"uptime": 123,
"auth": true
Expand Down
10 changes: 5 additions & 5 deletions arduino-mcp-extension/bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ is closed:
"arduino": {
"command": "node",
"args": ["/path/to/mcp-bridge/bridge/arduino-agent-bridge.js"],
"env": { "ARDUINO_AGENT_PATH": "/path/to/Arduino IDE executable" }
"env": { "ARDUINO_AGENT_PATH": "/path/to/Arduino Agent executable" }
}
}
}
Expand All @@ -41,7 +41,7 @@ Where to find it:
| Running from | Bridge path |
|--------------|-------------|
| Release, Windows / Linux | `<unzipped folder>/resources/mcp-bridge/bridge/arduino-agent-bridge.js` |
| Release, macOS | `Arduino IDE.app/Contents/Resources/mcp-bridge/bridge/arduino-agent-bridge.js` |
| Release, macOS | `Arduino Agent.app/Contents/Resources/mcp-bridge/bridge/arduino-agent-bridge.js` (`Arduino IDE.app` in 0.6.2) |
| 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
Expand All @@ -60,7 +60,7 @@ All optional, set as environment variables:
|----------|---------|---------|
| `ARDUINO_MCP_URL` | `http://127.0.0.1:3847/mcp` | MCP endpoint to forward to |
| `ARDUINO_MCP_TOKEN` | *(reads the token file)* | Override the auth token |
| `ARDUINO_AGENT_PATH` | *(unset — never launches)* | Path to the IDE executable. When set, a tool call that needs the IDE while it is closed starts it, waits for its MCP server and then runs the call. Concurrent calls share one launch; if the IDE never answers, the bridge doesn't start it again until it has been seen running. |
| `ARDUINO_AGENT_PATH` | *(unset — never launches)* | Path to the IDE executable. When set, a tool call that needs the IDE while it is closed starts it, waits for its MCP server and then runs the call. Concurrent calls share one launch; if the IDE never answers, the bridge doesn't start it again until it has been seen running. A path written for a pre-0.7.0 release (`Arduino IDE.exe`, `Arduino IDE.app/...`) that no longer exists falls back to the renamed `Arduino Agent` executable. |
| `ARDUINO_MCP_LAUNCH_TIMEOUT` | `120` | Seconds to wait for a started IDE's MCP server |
| `ARDUINO_MCP_WATCH_INTERVAL` | `5` | Seconds between checks for the IDE coming up (a local TCP connect) |
| `ARDUINO_MCP_DEBUG` | *(off)* | Set to `1` for verbose logging on stderr |
Expand All @@ -72,8 +72,8 @@ Example on Windows:
"mcpServers": {
"arduino": {
"command": "node",
"args": ["C:/Arduino/resources/mcp-bridge/bridge/arduino-agent-bridge.js"],
"env": { "ARDUINO_AGENT_PATH": "C:/Arduino/Arduino IDE.exe" }
"args": ["C:/Arduino Agent/resources/mcp-bridge/bridge/arduino-agent-bridge.js"],
"env": { "ARDUINO_AGENT_PATH": "C:/Arduino Agent/Arduino Agent.exe" }
}
}
}
Expand Down
23 changes: 21 additions & 2 deletions arduino-mcp-extension/bridge/arduino-agent-bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const path = require('path');
const { spawn } = require('child_process');

const PROTOCOL_VERSION = '2024-11-05';
const BRIDGE_VERSION = '0.3.0';
const BRIDGE_VERSION = '0.3.1';
const ENDPOINT = process.env.ARDUINO_MCP_URL || 'http://127.0.0.1:3847/mcp';
const ARDUINO_DIR = path.join(os.homedir(), '.arduinoIDE');
const TOKEN_FILE = path.join(ARDUINO_DIR, 'mcp-token');
Expand Down Expand Up @@ -142,6 +142,25 @@ function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

/**
* ARDUINO_AGENT_PATH, following the 0.7.0 rename of the executable from
* `Arduino IDE` to `Arduino Agent` (`Arduino IDE.exe`, or on macOS
* `Arduino IDE.app/Contents/MacOS/Arduino IDE`). When a path written for an
* older release no longer exists but the renamed one does, use that, so
* existing MCP configs keep working after an upgrade.
*/
function resolveAgentExecutable(exe) {
if (!exe || fs.existsSync(exe) || !exe.includes('Arduino IDE')) return exe;
const candidates = [
path.join(path.dirname(exe), path.basename(exe).split('Arduino IDE').join('Arduino Agent')),
exe.split('Arduino IDE').join('Arduino Agent'),
];
const renamed = candidates.find((p) => fs.existsSync(p));
if (!renamed) return exe;
warn(`ARDUINO_AGENT_PATH points at ${exe}, which no longer exists; using ${renamed} instead (the executable was renamed in Arduino Agent 0.7.0 - update the setting)`);
return renamed;
}

/** The message shown when a tool needs the IDE and it is not reachable. */
function offlineMessage(detail) {
const lines = [
Expand Down Expand Up @@ -358,7 +377,7 @@ class Upstream {
}

async launchAndWaitOnce(onWaiting) {
const exe = process.env.ARDUINO_AGENT_PATH;
const exe = resolveAgentExecutable(process.env.ARDUINO_AGENT_PATH);
if (this.launchGaveUp) {
return {
ok: false,
Expand Down
4 changes: 2 additions & 2 deletions arduino-mcp-extension/src/node/mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ export class ArduinoMCPServer {
res.end(
JSON.stringify({
status: 'ok',
server: 'arduino-ide-mcp',
server: 'arduino-agent',
version: EXTENSION_VERSION,
uptime: Math.floor((Date.now() - this.startTime) / 1000),
auth: this.requireAuth,
Expand Down Expand Up @@ -802,7 +802,7 @@ export class ArduinoMCPServer {
*/
private createMCPServerInstance(): Server {
const server = new Server(
{ name: 'arduino-ide-mcp', version: EXTENSION_VERSION },
{ name: 'arduino-agent', version: EXTENSION_VERSION },
{
capabilities: { tools: {}, prompts: {} },
// Workflow guidance delivered to every client at initialize.
Expand Down
30 changes: 30 additions & 0 deletions arduino-mcp-extension/test/bridge/bridge.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,36 @@ test('an IDE that never answers gives a clear error and is not started twice', a
}
});

test('an ARDUINO_AGENT_PATH naming the pre-0.7.0 "Arduino IDE" executable finds the renamed one', async () => {
// A renamed node binary stands in for the renamed IDE executable.
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'arduino-bridge-rename-'));
const ext = process.platform === 'win32' ? '.exe' : '';
const renamed = path.join(dir, `Arduino Agent${ext}`);
try {
fs.linkSync(process.execPath, renamed);
} catch {
fs.copyFileSync(process.execPath, renamed);
}
const port = await freePort();
const bridge = startBridge(port, {
ARDUINO_AGENT_PATH: path.join(dir, `Arduino IDE${ext}`),
ARDUINO_MCP_LAUNCH_TIMEOUT: '10',
});
let ide;
try {
await bridge.init();
const pending = bridge.request('tools/call', { name: 'execute_tool', arguments: { tool_name: 'arduino_context', params: {} } }, 15000);
await new Promise((r) => setTimeout(r, 1000));
ide = await fakeIde(port, { tools: [] });
const res = await pending;
assert.strictEqual(res.result.isError, undefined);
assert.strictEqual(textOf(res), 'ran execute_tool');
} finally {
bridge.close();
await ide?.close();
}
});

test('a wrong ARDUINO_AGENT_PATH is reported, not waited on', async () => {
const bridge = startBridge(await freePort(), {
ARDUINO_AGENT_PATH: path.join(os.tmpdir(), 'no-such-dir', 'Arduino IDE.exe'),
Expand Down
16 changes: 12 additions & 4 deletions docs/BUILDING-WINDOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,20 @@ To produce an installable app (unpacked, unsigned):

```bash
cd electron-app
# Rebuild native modules against Electron's ABI. This must come BEFORE the
# production build, which bundles whatever pty.node is present (see CLAUDE.md).
yarn exec theia rebuild:electron
# Production bundle (bundled backend + minified frontend)
yarn exec theia build --config webpack.config.js --mode production
# Rebuild native modules against Electron's ABI
yarn exec theia rebuild:electron
# Package to electron-app/dist/win-unpacked
yarn exec electron-builder --dir --publish never
# Package to electron-app/dist/win-unpacked. Use bash (PowerShell mis-tokenizes
# -c.extraMetadata.main). The metadata matches the "Package (unpacked)" step in
# .github/workflows/release.yml; without `name` and `main` the app gets the
# wrong user-data folder and skips its bundled plugins.
node ../node_modules/electron-builder/cli.js --dir --publish never \
-c.electronVersion=30.1.2 \
-c.extraMetadata.name=arduino-agent \
-c.extraMetadata.main=./arduino-ide-electron-main.js \
-c.extraMetadata.theia.frontend.config.buildDate="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
```

The MCP server starts automatically at `http://127.0.0.1:3847` when the IDE
Expand Down
6 changes: 3 additions & 3 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,6 @@ This project is built on [GitHub Actions](https://github.com/arduino/arduino-ide

[Get](https://arduino.github.io/arduino-cli/installation) the desired version of `arduino-cli` for your platform and manually replace the one inside the IDE. The CLI can be found inside the IDE at:

- Windows: `C:\path\to\Arduino IDE\resources\app\lib\backend\resources\arduino-cli.exe`,
- macOS: `/path/to/Arduino IDE.app/Contents/Resources/app/lib/backend/resources/arduino-cli`, and
- Linux: `/path/to/Arduino IDE/resources/app/lib/backend/resources/arduino-cli`.
- Windows: `C:\path\to\Arduino Agent\resources\app\lib\backend\resources\arduino-cli.exe`,
- macOS: `/path/to/Arduino Agent.app/Contents/Resources/app/lib/backend/resources/arduino-cli`, and
- Linux: `/path/to/Arduino Agent/resources/app/lib/backend/resources/arduino-cli`.
Loading
Loading