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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ older builds are on the

> **Tip — avoid "server failed to connect" when the IDE is closed.** The MCP
> server lives *inside* the IDE, so a direct HTTP connection fails whenever
> the IDE isn't running. Use the bundled stdio bridge instead and the server
> always connects, reporting "Arduino Agent is not running" only when you
> actually call a tool — and recovering by itself once you launch the IDE:
> the IDE isn't running. Use the bundled stdio bridge instead: the server
> always connects with its full tool list, and with `ARDUINO_AGENT_PATH` set
> a tool that needs the IDE starts it and waits for it — it recovers by
> itself when the IDE starts or restarts:
>
> ```json
> {
Expand Down
52 changes: 34 additions & 18 deletions arduino-mcp-extension/bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ whole server shows up as **failed to connect** whenever the IDE happens to be
closed — which looks like a broken integration rather than an idle one.

This bridge is a stdio MCP server that your client spawns as a child process, so
**connecting always succeeds**. It forwards requests to the IDE when it's up,
and when it isn't, tool calls come back with a plain, actionable message:

> Arduino Agent is not running, so the Arduino tools are unavailable.
> Launch the Arduino Agent IDE and try again — the connection recovers
> automatically, there is no need to restart this client.

When you launch the IDE later, the next request reconnects on its own. **No
client restart required.**
**connecting always succeeds**, and the Arduino tools stay usable while the IDE
is closed:

- The client always sees the real tool list, not an empty one.
- Browsing the tools (`list_tool_categories`, `get_category_tools`,
`search_tools`) and the prompts work without the IDE.
- A tool that needs the IDE **starts it and waits for it** when
`ARDUINO_AGENT_PATH` is set — the first call is just slower. Without it, the
call returns a plain "open the IDE" message.
- When the IDE comes up (or restarts) the bridge notices on its own and tells
the client if the tool list changed. **No client restart required.**

## Usage

Expand All @@ -27,15 +29,18 @@ client restart required.**
"mcpServers": {
"arduino": {
"command": "node",
"args": ["/path/to/arduino-mcp-extension/bridge/arduino-agent-bridge.js"]
"args": ["/path/to/arduino-mcp-extension/bridge/arduino-agent-bridge.js"],
"env": { "ARDUINO_AGENT_PATH": "/path/to/Arduino IDE executable" }
}
}
}
```

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.
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/`.

## Options

Expand All @@ -45,10 +50,12 @@ 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 made while the IDE is closed starts it in the background (rate-limited to one attempt per minute). That call still reports "not running" — retry once the IDE is up. |
| `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_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 |

Example with auto-launch on Windows:
Example on Windows:

```json
{
Expand All @@ -67,17 +74,26 @@ Example with auto-launch on Windows:
| Request | Response |
|---------|----------|
| `initialize` | Succeeds (answered locally) with the server's workflow `instructions` and the `prompts` capability, so the client connects fully featured |
| `tools/list` | The tools seen last time this bridge talked to the IDE, or `[]` on a cold start |
| `tools/call` | A tool result with `isError: true` and the message above |
| `resources/list`, `prompts/list` | Empty lists |
| `prompts/get` | The launch-the-IDE error |
| `tools/list` | The IDE's own list if this bridge has seen it, otherwise the compiled definitions for the tool mode in `~/.arduinoIDE/settings.json` (`arduino.mcp.toolMode`, router by default) |
| `tools/call` — `list_tool_categories`, `get_category_tools`, `search_tools` | Answered locally, identical to the server (both use `runRouterDiscoveryTool`) |
| `tools/call` — anything else | With `ARDUINO_AGENT_PATH`: starts the IDE, waits (sending progress notifications if the client asked for them), then runs the call. Otherwise, or if the IDE doesn't come up: a tool result with `isError: true` saying what to do |
| `prompts/list`, `prompts/get` | Answered locally from the compiled prompts |
| `resources/list`, `resources/templates/list` | Empty lists |

When the IDE comes up, the bridge fetches its tool list and sends
`notifications/tools/list_changed` if it differs from what the client was given
(for example, the IDE runs in `direct` mode).

The instructions are loaded from the compiled extension when present (source
of truth) with an embedded fallback; the smoke test asserts bridge/server
parity so the copies cannot drift silently.

Session handling is automatic: if the IDE restarts and invalidates the session
(HTTP 404) or rotates the token (HTTP 401), the bridge re-runs the handshake and
retries the request once.
retries the request once. A token that is still rejected comes back as a
readable tool error rather than a protocol failure.

Tests: `yarn test:bridge` in `arduino-mcp-extension` (after `yarn build`) runs
the bridge against a fake IDE server.

> stdout carries only the JSON-RPC stream; all diagnostics go to stderr.
Loading
Loading