← Commit history
SKILL.md+261−80
@@ -1,116 +1,297 @@ ----name: fusion-mcp-and-preferences-description: >--  Use Autodesk's Fusion MCP server (the Autodesk + Anthropic text-to-CAD connector) from an Adom-  cloud AI, and change ANY Fusion preference including ones the Python API does not expose. The MCP-  server binds loopback on the user's machine so a cloud container cannot reach it; the fusion-  bridge proxies it. Covers turning it on for the user (no API exists for the toggle, so the bridge-  drives the Preferences dialog), the MCP session handshake, the four Autodesk tools, and when to-  prefer a native fusion_* verb instead. Trigger words - fusion mcp, fusion mcp server, autodesk-  mcp, claude connector for fusion, text to cad, 127.0.0.1:27182, enable fusion mcp, fusion-  preferences, change a fusion setting, fusion prefs dialog, api preferences, developer tools-  fusion, fusion setting not in the api.+name: adom-desktop-fusion+public: true+description: "Launch and drive Fusion 360 on the user's laptop from this container via the adom-desktop CLI. Includes FAST cloud-file search via APS (Autodesk Platform Services) — search/browse/open the team's Fusion cloud designs in seconds (set up once per company, each user signs in once, never charged). Plus open .f3d/.f3z designs, open cloud files, close documents, dismiss recovery dialogs, capture Fusion window screenshots, send keys + click, run the add-in (STEP/GLB/.lbr import-export, BOM/parameters). Trigger words: fusion, Fusion 360, fast cloud search, fusion_aps_search, search fusion files, find a design, browse cloud files, fusion_aps_open, aps, open fusion, launch fusion, open design, f3d, f3z, import STEP, export STEP, export GLB, export lbr, Fusion electronics, Fusion PCB, BOM from Fusion, Fusion parameters, Fusion API, dismiss recovery, Fusion screenshot, fusion_click, fusion_send_key, fusion bridge, close fusion." --- -# Fusion MCP server + preferences+# adom-desktop — Fusion 360 bridge -Two capabilities that turned out to be the same trick: Fusion exposes far less to its Python API-than to its UI, and the UI is drivable.+All commands dispatch to the Fusion Python bridge. From this container: -## The MCP server+```bash+adom-desktop fusion_<action> '<json_args>'+``` -Autodesk + Anthropic ship a **local** MCP server at `http://127.0.0.1:27182/mcp` exposing Fusion's-own text-to-CAD surface.+The leading `fusion_` routes to the Fusion plugin; `<action>` is the plugin-side name. -**It binds loopback on the user's machine.** An Adom AI runs in a cloud container and cannot reach-it. The fusion bridge runs on that machine, so it proxies:+## FIRST-TIME / COLD-START — read before you panic++A fresh laptop may not have Fusion running (or may run a stale add-in). That is EXPECTED, not defeat.+Branch on the stable `errorCode`, never the prose:++| response (`errorCode`) | what it means | what you do |+|---|---|---|+| `ready:true` (fusion_readiness) | Fusion up + add-in current | drive |+| `running:false` / `fusion_not_running` | installed, not launched | `fusion_start` (returns when the add-in is ready, or `stillLaunching` on a slow first launch) |+| `stillLaunching:true` (fusion_start) | first launch, add-in not up yet (can take 2-4 min) | do NOT re-call `fusion_start`; POLL `fusion_readiness` every ~10s until `ready:true` |+| `installed:false` / `host_app_not_installed` (AD >=1.9.79, + a `hostApp` field) | no Fusion host app | OFFER to install, then **`fusion_install_fusion`** (no shell approval; elevation-aware, streams the free trial) -> poll `fusion_readiness` (`installing:true`) until `installed:true` -> `fusion_start`. Details: [fusion-onboarding](skills/fusion-onboarding/SKILL.md) |+| `needsSignin:true` (`fusion_start` **and** `fusion_readiness`) | Fusion is running but stuck at the first-run Autodesk sign-in (main UI blocked, add-in can't serve, so `ready:false`) | AUTOMATE it (proven sequence, see [fusion-onboarding](skills/fusion-onboarding/SKILL.md)): (1) clear STALE `signin.autodesk.com` tabs (a dead tab = expired code = "Sign-in request expired"); (2) `desktop_ui_click` "Retry browser sign-in" / "Sign In" for ONE fresh request; (3) click "Open Product" with **`desktop_click` (SendInput=isTrusted)** — `desktop_ui_click` (UIA) does NOT fire the protocol; (4) on the in-page "open Autodesk Identity Manager?" overlay (NOT an owned popup) tick "Always allow" + "Open" via `desktop_click`, FAST; (5) poll `fusion_readiness`. `nbrowser` (CDP) can do 3-4 headlessly if it's on the SAME browser window. NEVER auto-enter password/2FA. To ask the user, `notify_user` works directly (AD ≥1.9.84); add a cross-AD `target` if you're on a VM |+| `addinStale:true` / `addin_stale` | Fusion runs an OLD add-in that never re-synced (silent-failure risk) | `fusion_stop` then `fusion_start` — the bridge re-syncs the add-in from cache on start (Fusion must be CLOSED) |+| `bridge_restarting` + `stillRunning:true` (AD ≥1.9.77) | the bridge is respawning after a `bridge_install` | NEVER a not-installed/fatal signal: poll `fusion_readiness` until `ready:true`, then retry. Read-only verbs (`fusion_readiness`/`fusion_describe`/`fusion_get_app_state`) forward straight through even mid-respawn — no sleep-and-recheck hacks needed |+| `blockingDialogs:[…]` | a modal is blocking the add-in | dismiss in the BACKGROUND, then the op auto-resumes |++**Runtime note:** AD **provisions Python itself** (a portable no-UAC 3.12.13 runtime if none is on PATH — the bridge never bootstraps its own; check AD's `runtimes` verb). You never install Python.++## The 8 conventions (from the Bridge SDK)++1. **Poll readiness, don't launch blindly.** `fusion_readiness` (or AD's `bridge_readiness`) is read-only — it reports `installed`/`running`/`ready`/`addinStale` WITHOUT launching anything. Loop: readiness → act on the row above → drive.+2. **`fusion_readiness` is the standardized cold-start probe.** (Fusion is a detected HOST APP with no heavy download, so there's no `prewarm` step — readiness is the whole gate.)+3. **Core verbs live in the table below; `fusion_describe '{}'` is the full machine-readable catalog.** Read the table to act fast; hit `describe` for completeness — don't guess a verb.+4. **Background by default — automate first, NOTIFY only at a true wall.** This bridge drives the AI's workspace, not the user's screen: dismiss dialogs in the BACKGROUND (`fusion_close_window`, never `fusion_send_key`/Escape, which steals focus). AUTOMATE sign-in / dialogs yourself (see the proven sign-in sequence in [fusion-onboarding](skills/fusion-onboarding/SKILL.md)); reserve an AD **`notify_user`** for something only the user can do — a **UAC/elevation** prompt, a license accept, a **2FA/password** step you must not auto-fill, or a licensing **DECISION** that risks their other machine. **`notify_user` works directly over the bridge's direct API (AD ≥1.9.84) — call it, don't relay a payload for the AI to send.** When this bridge runs UNATTENDED (e.g. on a VM) and the user is elsewhere, add a cross-AD **`target`** (a peer clientName from the `targets` verb, or `"all"`) so the toast lands where they are. Only foreground Fusion to SHOW the user something. See [fusion-driving](skills/fusion-driving/SKILL.md) and [fusion-onboarding](skills/fusion-onboarding/SKILL.md).+5. **`ok:true` is not enough — verify.** A long op that "times out" at ~60s is usually STILL RUNNING server-side (the relay caps the request, not the work). Verify by artifacts (the saved file / a screenshot / `fusion_get_app_state`), don't blindly retry.+6. **Reuse the running Fusion — and when a restart IS needed, YOU do it, never the user.** Fusion is a singleton: `fusion_readiness` before `fusion_start`; if `ready`, just drive. But a stale/missing add-in or a wedged session needs a restart, and that is YOUR job — `fusion_stop` then `fusion_start`. Restarting Fusion and enabling the add-in are the bridge's job, not something to hand back to the user — doing it for them is the entire point of this bridge. (Add-in installs land in ALL Fusion add-in dirs — 2025+ Fusion scans `%APPDATA%\Autodesk\FusionAddins`, older builds use `...\Autodesk Fusion[ 360]\API\AddIns` — so a restart always picks it up.)+7. **Surface `_hint` verbatim + branch on `errorCode`.** Every verb returns an actionable `_hint`; relay it to the user unchanged and key your logic off the stable `errorCode`, not the prose.+8. **Never write files to a shared cloud ROOT.** Uploads default to an `Adom AI Workspace` folder; see [fusion-cloud-hygiene](skills/fusion-cloud-hygiene/SKILL.md).++## [!] Electronics PCBs: ALWAYS open the PROJECT file (never the schematic / board / 3D)++A Fusion electronics design is NOT one file. It is a parent/child chain of FOUR files, each a+different `productType`: -```bash-adom-desktop fusion_mcp_status '{}'          # up? serverInfo + live tool list-adom-desktop fusion_mcp_enable '{}'          # turn it ON for the user-adom-desktop fusion_mcp_tools  '{}'          # tools WITH input schemas-adom-desktop fusion_mcp_call   '{"tool":"fusion_mcp_electronics_read",-                                 "arguments":{"entity_type":"electronics.Element"}}'-adom-desktop fusion_mcp_resources '{}'       # electronics entity schemas ```+PROJECT        EcadDesignProductType     <-- OPEN THIS ONE, ALWAYS+  |- schematic   SchematicProductType+  |- board/.brd  BoardProductType+  |- 3D PCB      DesignProductType        (generated from the .brd - the LEAF; opening it gives NO editable board)+```++- **Open the PROJECT** (`EcadDesignProductType`) with `fusion_open_cloud_file` / `fusion_open_by_urn`.+  Fusion then brings up its schematic + board as children; move between them with the schematic view,+  `fusion_show_2d_board`, and `fusion_show_3d_board`.+- **NEVER open the schematic, the .brd, or the 3D file directly** to "get the board." You get an+  isolated/empty view and waste time trying to derive a 2D board from the 3D. The 3D is the LEAF,+  generated FROM the .brd - it is not the board, and you cannot pull a board out of it.+- Same-named copies in OTHER projects (e.g. a drone assembly) are usually 3D-model derivatives+  (`DesignProductType`), NOT the project. Don't open those.+- `fusion_open_cloud_file` / `fusion_open_by_urn` REPRIMAND you in the response `_hint` when you open+  a child file instead of the project - read it and re-open the `EcadDesignProductType` project.++## ⚡ Fast cloud search (APS) — READ THIS FIRST for any "find/search/open a cloud design" task++To search the team's Fusion cloud files, use **`fusion_aps_search`**, NOT the legacy+`fusion_search_cloud_files` (which walks folders one-by-one and takes 30+ MINUTES). APS hits+Autodesk's **server-side index** → results in **seconds**, and works with Fusion closed.++| Verb | Does |+|---|---|+| `fusion_aps_search {query, projectId?, limit?}` | Fast name search across the whole team hub; newest-first; shows each file's project. |+| `fusion_aps_browse {} / {projectId} / {projectId,folderId}` | Browse hubs→projects→folders→files (cloud explorer, no Fusion). |+| `fusion_aps_open {query}` | Search + open the best match in Fusion (opens by exact URN — works for nested files). Returns immediately; the open finishes in the background — poll `fusion_get_app_state` (or pass `{wait:true}`). |+| `fusion_aps_recent {limit?}` | Most-recently-modified designs across the hub, newest-first. |+| `fusion_aps_file_info {query}` | Version history for a file — count, who/when last modified, size. |+| `fusion_aps_status {}` | configured? signedIn? token live? + never-charge usage meter. **Call this first** — its `_hint` tells you the next step. |+| `fusion_aps_signin {}` | One-time per user: opens Autodesk consent, captures the token. |+| `fusion_aps_set_client_id {clientId}` | Set the org's APS Client ID. |++**Auth model (important): set up ONCE per company, each user signs in once.** An admin registers a free APS app for the org one time (the AI drives the whole setup — see the `fusion-aps-signin` skill) → a **Client ID** (a public PKCE id, no secret, safe to share within the org). Then every teammate just runs `fusion_aps_signin` once with their own Autodesk login — **no plan, no card, no setup** for them. It's **per-organization** (each company runs its own setup; don't ship one global Client ID). **Nobody is ever charged** — the card is identity-verification only, and the bridge is free-endpoint-only + usage-capped.++If `fusion_aps_status` says not configured/signed in, follow its `_hint`. Full why + step-by-step setup: the bundled **`fusion-aps-search`** and **`fusion-aps-signin`** skills.++## ⛔ "Expired subscription — Read Only" / trial / personal Fusion — what it does NOT block++**An expired / "Read Only" / personal-use Fusion can OPEN, VIEW, and BROWSE files just fine.**+Autodesk keeps the account in **Basic Access for 365 days** after a subscription lapses. So in+read-only Fusion you CAN: **open cloud designs, view/inspect them, browse the Data Panel, and run+APS search** — everything this bridge does for *reading* works.++Read-only ONLY blocks **save, export, modify, and creating new designs** (the Save/Export buttons+are dimmed). So:++- ⛔ **Do not attribute an `open` / `view` / `search` failure to "the Fusion subscription+  is expired / read-only."** That is FALSE and confuses users (it's been said wrongly many times).+  If `fusion_aps_open` / `fusion_open_cloud_file` hangs or fails, it is a **code / approach+  problem in the open path** (URN resolution, the Electronics design picker, a slow assembly+  download, a stuck main thread) — debug/fix THAT, not the license. Try harder; don't blame the+  subscription.+- ✅ The ONLY place read-only is a legitimate explanation is **save / export** — e.g.+  `fusion_export_*` may be blocked (Save/Export dimmed). There, "renew the subscription" is valid.++(Source: Autodesk — "Term has Expired or Usage Limits Exceeded"; data stays in Basic Access ~365 days.)++## Commands (core bridge)++| CLI form | Action | Purpose | Key args |+|---|---|---|---|+| `fusion_start` / `fusion_launch` | `start` / `launch` | Launch Fusion 360 (handles webdeploy glob, waits for bridge, dismisses startup picker) | — |+| `fusion_open_design` | `open_design` | Open a local `.f3d` / `.f3z` design file | `path` |+| `fusion_open_cloud_file` | `open_cloud_file` | Open a design from the user's Fusion cloud | `project`, `path` |+| `fusion_close_document` | `close_document` | Close the active document | — |+| `fusion_close_window` | `close_window` | Close a specific Fusion window by HWND | `hwnd` |+| `fusion_close` | `close` | Close Fusion 360 entirely | — |+| `fusion_dismiss_recovery` | `dismiss_recovery` | Dismiss the "recover unsaved work" dialog | — |+| `fusion_relocate_recovery` | `relocate_recovery` | Move recovery dialog offscreen (for recording) | — |+| `fusion_window_info` | `window_info` | Enumerate Fusion windows (HWND, title, bounds) | — |+| `fusion_screenshot_fusion` | `screenshot_fusion` | Screenshot the main Fusion window | — |+| `fusion_screenshot_all` | `screenshot_all` | Screenshot every Fusion window | — |+| `fusion_click_fusion` | `click_fusion` | Click at (x, y) in the Fusion main window | `x`, `y` |+| `fusion_send_key` | `send_key` | Send a keyboard shortcut to Fusion | `hwnd`, `key` |+| `fusion_addin_status` | `addin_status` | Report whether the in-process Adom add-in is loaded + healthy | — |+| `fusion_generate_package` | `generate_package` | Generate an IPC package via Fusion's built-in EPG (~50 families) + raised-WHITE marking (longest-axis, measured 10% margin, multi-line MPN+variant) + colored STEP + sidecar manifest - in ONE call | `type`, `params` (mm), `etch`, `etchStyle`, `outputStep` |+| `fusion_set_preference` | `set_preference` | Set appearance/navigation prefs LIVE (no restart): `theme` (light/darkblue/darkgray/classic/device/dark), `invertScrollZoom`, `orbitScheme` (fusion360/alias/inventor/solidworks/tinkercad/powermill), `modelingOrientation` (yup/zup), `gestureNav`, `cameraPivot`, `lengthUnit` (mm/cm/m/in/ft). Per-key result in `applied`; some themes aren't in every build (falls back) | any of the keys |+| `fusion_get_preferences` | `get_preferences` | Read current appearance/navigation prefs as the friendly values `set_preference` accepts | — | -### Do not ask the user to enable it+## Extended commands via the in-process add-in -It is OFF by default and there is **no API for the toggle**. `fusion_mcp_enable` does it for them:-opens Preferences, expands General, selects API, ticks the box, Apply, OK, then verifies the port-is listening. Tell them it is handled. Only fall back to instructions if the verb reports failure.+If the Adom add-in is installed (`cd ~/project/adom-desktop/plugins/fusion360 && python install_addin.py` on the laptop), Fusion 360 exposes an additional API over its own ports. Typical extra commands proxied through the bridge: -### The trap that costs a debugging cycle+- `fusion_import_step` — import a STEP file into the active document+- `fusion_export_step` — export active body/occurrence to STEP+- `fusion_export_glb` — export to GLB (used by the 3D viewer)+- `fusion_export_lbr` — export an Eagle library (used by the Eagle workflow)+- `fusion_open_lbr` / `fusion_open_symbol` — open symbol libraries+- `fusion_command` — generic `{"command": "<fusion_api_name>", "args": {...}}` pass-through for any Fusion API call+- `fusion_list_cloud_projects`, `fusion_list_cloud_files`, `fusion_walk_cloud_tree`, `fusion_search_cloud_files`, `fusion_open_cloud_file`, `fusion_export_cloud_file` — Autodesk cloud Data API operations -**The setting is discarded unless Apply is clicked.** Restart or kill Fusion with the dialog open-and it reverts, while the checkbox looked correct. If the port is closed but the user says they-enabled it, this is almost certainly why. Re-run `fusion_mcp_enable`.+**Opening multi-design electronics files — pass `fileExtension`.** A Fusion electronics data file that links to multiple designs (common for boards with variants) makes `fusion_open_cloud_file` pop Fusion's native **"Select Electronics Design File" picker** — a CEF/Chromium modal whose list is **not keyboard- or Win32-navigable** (and screen-coord clicks are unreliable on multi-monitor/HiDPI), so it **can't be resolved headlessly**. Avoid it by opening the specific file directly: `fusion_open_cloud_file {"fileName":"<name>","fileExtension":"fbrd", ...}` (the `.fbrd` board lands straight in PCB Editor — live-confirmed). Opening the bare name or the `.fprj` project triggers the picker. If a picker still appears, `_diagnose_addin_timeout`/`blockingDialogs` will classify it (see below). -### The handshake is handled+### Blocking-dialog awareness -Streamable-HTTP: a bare POST returns `400 Missing MCP-Session-Id`. You must `initialize`, capture-the `MCP-Session-Id` **response header**, send `notifications/initialized`, then pass that header.-The bridge does this and re-establishes dropped sessions, so `fusion_mcp_call` just works.+Several Fusion modals (the multi-link "Select Electronics Design File" picker, "Fusion needs to update", Document Recovery, "Save changes?") block the add-in's main thread. The bridge now **classifies them by title** and, on a blocked command, returns `blockingDialogs: [{hwnd, title, category, resolution}]` with the precise cause + fix — instead of a misleading "add-in not responding / may have crashed". So a blocked open tells you *which* modal is up and how to clear it. -### Autodesk's four tools+Resolution notes:+- **Multi-link picker** is a **CEF/web modal whose list is NOT keyboard- or Win32-navigable** (SendInput reaches only the native Cancel/OK; screen-coord clicks are unreliable on multi-monitor/HiDPI). It currently **must be resolved on the desktop**. *Headless fix tracked separately:* have the add-in open the specific linked design via the Fusion API, avoiding the picker entirely.+- **Update nag** is reported, not auto-dismissed (auto-confirming could start an update mid-run) — clear it on the desktop; doing so also commonly resolves recurring add-in crashes (host/add-in version drift).+- **Recovery** prompts: `fusion_dismiss_recovery` / `fusion_relocate_recovery`. -| tool | for |+Check `fusion_addin_status` first — if the add-in isn't loaded, these will fail with a `_hint` pointing at the install script.++## Cloud search — defensive contract (add-in v1.0.2+)++`fusion_search_cloud_files` is the most-used long-running verb. Its v1.0.2 behavior is engineered specifically so the AI never misreports a failed search as "file not found":++| You read in the response | Mean | |---|---|-| `fusion_mcp_read` | geometric properties / data from the active model |-| `fusion_mcp_update` | update the active model |-| `fusion_mcp_execute` | execute operations |-| `fusion_mcp_electronics_read` | Electronics design data by `entity_type` |+| `searchComplete: true` + `totalFound: 0` | **Confirmed negative.** No match in the searched scope. |+| `searchComplete: true` + `totalFound: N` | All matches returned. |+| `searchComplete: false` (any reason) | **Search stopped early.** May or may not be more matches. Do NOT say "not found" to the user. Re-run with broader caps OR narrower `folderPath`. |++`searchComplete` is `true` ONLY when every one of these is clean: not `folderLimitReached`, not result-`truncated`, not `searchTimedOut`, `foldersSkipped == 0`, `filesSkipped == 0`. Five conditions, all five must pass.++### Performance + the slow-search etiquette++`fusion_search_cloud_files` runs at ~1 folder/sec because Autodesk's free Fusion 360 Python API has no indexed search endpoint — it walks the cloud Data API one folder at a time (30+ min on a real hub). **⚠️ Prefer `fusion_aps_search` instead** (see the APS section at the top) — it uses Autodesk Platform Services' server-side index and returns in **seconds**. This legacy walk verb remains only as a fallback for when APS isn't set up; if a user is hitting slow searches, set up APS (`fusion_aps_status` → its `_hint`) rather than tuning the walk. -`entity_type` covers the EAGLE object model: `electronics.Board`, `.Element`, `.Contact`,-`.ContactRef`, `.Device`, `.DeviceSet`, `.Gate`, `.Bus`, `.Error` (ERC/DRC), `.Frame`, `.Grid`,-`.Attribute`. `fusion_mcp_resources` returns the schemas.+**Before firing a broad search**, ask the user which subfolder is likely. Naming a folder like "Molecules" cuts 5-40 min searches down to ~10s. The verb's response includes a `costAnalysis` block (`foldersPerSecond`, `estimatedSecondsPer100Folders`) so the AI can budget realistically. -MCP acts on the **active document**. Open one first (`fusion_aps_open`).+### Streaming progress -### MCP vs native verbs+For interactive sessions, wrap long searches in `watch` so the AI gets one JSON-line progress event per `interval` seconds:++```bash+adom-desktop watch '{+  "command": "fusion_search_cloud_files",+  "args": {+    "query": "cosmiic",+    "projectName": "Main",+    "folderPath": "Molecules",+    "recursive": true,+    "maxFolders": 300,+    "maxDepth": 10,+    "searchTimeout": 340,+    "timeout": 400+  },+  "interval": 3+}'+```++Output:+```+{"event":"started", ...}+{"event":"progress", "foldersVisited": 12, "filesFound": 1, "elapsedSeconds": 15.1, "currentFolder": "Molecules/COSMIIC", ...}+{"event":"progress", "foldersVisited": 173, "filesFound": 3, "elapsedSeconds": 168.3, ...}+{"event":"complete", "result": {... full response with searchComplete:true ...}}+``` -**Prefer a native `fusion_*` verb when one exists.** They are tested, they return hints that teach-the next step, they handle the awkward parts (kit-aware BOM counting, the 2-minute sign-in clock,-GLB optimization), and they work without a Fusion subscription or MCP enabled.+The `watch` wrapper composes with the `Monitor` tool — each stdout line becomes a notification. -**Reach for MCP** for Autodesk's text-to-CAD surface and the structured Electronics object model,-which is richer for querying a board than anything hand-rolled.+### Args (no hard upper caps in v1.0.2+) -Requirements: Fusion subscription, Fusion running, loopback only.+| Arg | Default | Notes |+|---|---|---|+| `query` | (required) | Substring of file name. **Case-insensitive both directions.** |+| `projectName` | active | Cloud project name. |+| `folderPath` | project root | Starting subfolder. **NARROW HERE — biggest perf lever.** |+| `recursive` | `false` | Descend into subfolders. |+| `maxDepth` | 2 | Recursion depth cap. |+| `maxFolders` | 10 | Folder-visit cap. |+| `maxResults` | 20 | Result cap. |+| `folderTimeout` | 30s | Per-folder cloud-API timeout. Bridge skips slow folders. |+| `searchTimeout` | 120s | Overall wall-clock timeout. Set 300+ for big walks. |+| `timeout` | 620s | HTTP envelope timeout (CLI+direct API). Bump if `searchTimeout` > 180. | -## Any preference, via the UI+Real-world live test: `Main/Molecules` recursive depth-10 cosmiic search hit 173 folders in 168s with `searchComplete:true, totalFound:3, foldersSkipped:0, filesSkipped:0`. Fusion stayed `main_thread: responsive` throughout. -The API exposes a thin slice (`generalPreferences`: theme/orbit/units; `apiPreferences`:-`debuggingPort`, `isDeveloperToolsEnabled`). Everything else is UI-only.+## Quick examples  ```bash-adom-desktop fusion_prefs_open  '{"section":"api"}'   # opens + navigates + screenshots-# look at the screenshot, then click what you see:-adom-desktop desktop_click '{"space":"image","shotId":"<from response>","x":690,"y":476,"hwnd":<hwnd>}'-adom-desktop fusion_prefs_close '{"save":true}'       # Apply + OK+# Launch Fusion+adom-desktop fusion_start '{}'++# Open a local design+adom-desktop fusion_open_design '{"path":"C:/designs/board.f3d"}'++# Screenshot all Fusion windows+adom-desktop fusion_screenshot_all '{}'++# Run a Fusion API command (add-in required)+adom-desktop fusion_command '{+  "command":"export_step",+  "args":{"occurrence":"body1","output":"C:/tmp/part.step"}+}'++# Check add-in health+adom-desktop fusion_addin_status '{}' ``` -Sections: `general`, `api`, `design`, `manufacture`, `electronics`, `render`, `drawing`,-`material`, `graphics`, `network`, `preview features`.+## Error shape++Every failing response includes a `_hint` field. Surface it verbatim when a Fusion command fails.++## Bridge architecture (v1.8.31+) — dynamic ports++Earlier versions used hardcoded port `8773` for the bridge and `8774` for the in-process add-in. **v1.8.31 made the bridge use OS-assigned ephemeral ports.** The add-in port is still 8774 (it's inside Fusion's process and the manifest pins it), but that's an internal detail.++- The CLI (`adom-desktop fusion_*`) routes through adom-desktop's direct API → the bridge's runtime port. No callers need port numbers.+- The add-in v1.0.2 ships a stale-lock watchdog in `http_server.py`: if a command holds the main-thread lock longer than `per_command_timeout + 30s` (e.g. Fusion's native cloud SDK wedges on a slow API call), the watchdog force-releases so subsequent commands work without restarting Fusion. The wedged command is orphaned but no longer blocks everything else.+- The `searchComplete` contract (see above) prevents false-negative reporting on capped/skipped searches. Always check `data.searchComplete` before telling the user "file not found".++## How this bridge is packaged + delivered (3 artifacts)++This bridge is ONE wiki page (`wiki.adom.inc/adom/adom-desktop-fusion-bridge`) that ships **three things**:++1. **The page git repo** — the ONE source of truth (all source, `skills/`, the docs, `page.json`, and the+   bridge manifest). Edit + push here.+2. **The Release zip + the bridge manifest** (`adom-bridge-fusion-manifest.json`) — what **adom-desktop**+   consumes on `bridge_install`: the manifest tells adom-desktop who this bridge is (name, version, the+   Release-zip URL, sha256, size, verb prefixes, health), and it streams that zip into its bridge cache.+3. **The adom-wiki skills pkg** (`adom-wiki pkg install adom/adom-desktop-fusion-bridge`) — **skills only**.+   It deploys these skills into a container's `~/.claude/skills/` so the AI knows how to drive this bridge+   and Fusion.++**Install handshake:** when an AI has adom-desktop install this bridge, adom-desktop installs the Release+AND tells the AI to `adom-wiki pkg install` this pkg — so the container gets all these skills. An AI can+also install the pkg itself; then the skills **auto-update** via the pkg mechanism (the goal). Full detail++ the publish ritual + the hero-relink reminder: the **`fusion-bridge-dev`** skill. -### Why it returns an image instead of control names+## Related -`Commands.Start PreferencesCommand` opens the dialog, and the **top-level** section tree IS in the-UIA tree. But the children under General (API, Design, Manufacture, Electronics, Render, Drawing,-Simulation) are rendered **lazily by Qt** and never appear there, even after `desktop_ui_expand`-reports success. So child navigation is an image-space click, which takes the foreground and is-announced with a caption.+- **Building component libraries (symbol + footprint + RENDERING 3D):** **`fusion-libraries`** (one part) and **`fusion-multipart-libraries`** (many parts in one `.lbr`, each with real 3D) — the latter's SOLVED recipe is `fusion_make_3d_package` (STEP → orient → f3d → upload → `wip_urn`, hand-written into the library, **no GUI dialogs**). Full tutorial: the **`fusion-multipart-libraries`** skill's `CREATING_BASIC_PARTS_LIBRARIES.md`. Operate Fusion safely with **`fusion-driving`** + **`fusion-cloud-save`**.+- **`fusion-aps-search`** + **`fusion-aps-signin`** — APS fast-search reasoning + the AI-driven one-time setup (bundled in this repo's `skills/`).+- `adom-desktop-kicad` — sibling bridge for KiCad.+- `adom-desktop-direct-api` — the direct API contract that callers route through.+- **Canonical repo:** the wiki page `https://wiki.adom.inc/adom/adom-desktop-fusion-bridge` (+ GitHub backup `adom-inc/adom-desktop-fusion-bridge`). The old `adom-inc/adom-desktop/plugins/fusion360/` is a stale seed. -### Rules+## Quick demo on first install (sample library included) -1. **Always** finish with `fusion_prefs_close {save:true}`. Nothing persists otherwise.-2. **Verify the effect**, do not trust the click. For MCP, probe the port. For a render setting,-   observe the change.-3. Prefer `fusion_set_preference` for anything in the API-exposed slice; it is background and does-   not touch the user's screen.-4. Never leave the dialog open. A modal Preferences window blocks other Fusion verbs.+The repo ships a ready sample so you can prove the bridge end to end right after installing:+`examples/ADS8588SIPM.fusion.lbr` (a real 4-sided LQFP-64 library) + `examples/ADS8588SIPM.step` (its 3D). -## Full write-ups+1. Download both from this page's Files tab (or `adom-wiki repo clone`), then stage them onto Windows+   (serve + `Invoke-WebRequest`, since there is no container->Windows push verb).+2. **Import demo:** `fusion_open_lbr {"filePath":"C:/.../ADS8588SIPM.fusion.lbr","verify":true}` -> the+   symbol + 4-sided footprint render in the Electronics Library editor.+3. **3D-attach demo:** `fusion_attach_3d_package {"filePath":"C:/.../ADS8588SIPM.fusion.lbr",+   "modelPath":"C:/.../ADS8588SIPM.step","packageName":"ADS8588SIPM"}` -> click the Save dialog per the+   returned hint -> the real chip binds onto the footprint (Content Manager shows the 3D under the package). -- [docs/fusion-mcp-server.md](../../docs/fusion-mcp-server.md)-- [docs/fusion-preferences.md](../../docs/fusion-preferences.md) (with screenshots of all three steps)+See the **`fusion-libraries`** skill's `MAKING_LIBRARIES.md` for the full walkthrough + screenshots.
docs/3d-viewer-and-export.mdadded+107
@@ -0,0 +1,107 @@+# 3D board view, export, and STEP colorization++Getting a Fusion design out as 3D geometry that renders well on the web, and recoloring imported+STEP so a machine reads as a machine instead of a grey blob.++## The 3D PCB view++```bash+adom-desktop fusion_show_3d_board '{}'+```++The 3D PCB is **generated from the `.brd`** and is a leaf in the electronics chain. You cannot pull+an editable board back out of it, which is why you always open the PROJECT rather than the 3D child.+See [schematics](schematics.md).++## Export formats++| verb | format |+|---|---|+| `fusion_export_step` | STEP |+| `fusion_export_iges` | IGES |+| `fusion_export_stl` | STL |+| `fusion_export_3mf` | 3MF |+| `fusion_export_obj` | OBJ |+| `fusion_export_usdz` | USDZ |+| `fusion_export_optimized_glb` | **web-grade GLB, see below** |++All carry 300s timeouts. These drive the desktop `exportManager`, so they need the Fusion desktop+app. On Linux, where desktop Fusion has no build, use the web-export path instead (see the+`fusion-web-export` skill).++## `fusion_export_optimized_glb`, and why raw GLB is unusable++A raw Fusion GLB of a populated board is roughly **16 MB across ~25,000 draw calls**, which stalls a+web viewer. The optimized path exports STEP (plus optional silkscreen) and runs it through+service-step2glb in molecule mode: anchor to the machine pins, bake silkscreen, gold pins,+join/weld/prune, then Draco compression.++Result: about **465 KB and ~31 draw calls**. That is the difference between a page that renders and+one that hangs.++```bash+adom-desktop fusion_export_optimized_glb '{"outputPath":"C:/tmp/board.glb","silkscreen":true}'+adom-desktop fusion_fetch_optimized_glb  '{"jobId":"..."}'   # finish a big board+```++It bounded-waits (`wait`, default 90s). Small boards return `complete`; big boards return+`{pending, jobId}` and you finish with `fusion_fetch_optimized_glb`.++### Stackup rules for a board GLB++Getting these wrong produces a model that looks right and measures wrong:++- The **board bbox is the FR4 body**, never the whole assembly. Components overhang the edges and+  tower above it.+- Each overlay sits at **its own layer's z**: copper at FR4-top, silk above mask.+- Copper traces are real 35um 3D bodies, colored like the FR4 by default. To make routing visible,+  recolor them and make the mask translucent **in the raw GLB before optimizing**.+- Put the measured stackup table on the board's wiki page. `fusion_board_stackup` gives you the+  numbers.++## Colorizing an imported STEP++A STEP file imported into Fusion typically arrives as one undifferentiated grey solid. Every body is+the same color, so a machine assembly reads as a featureless blob and is useless as a viewer asset+or a diagram.++Fusion's API can set appearance per body or per occurrence, so this is scriptable through+`fusion_run_modeling_script`: walk the occurrences, match components by name, and assign an+appearance from the material library to each group. Frame, rails, motors, tooling and enclosure each+get their own color, and the result exports through the same GLB path above.++The pattern:++```python+# inside fusion_run_modeling_script+# 1. walk design.rootComponent.occurrences+# 2. bucket components by name (frame / rail / motor / tooling / cover ...)+# 3. assign an appearance per bucket from the appearance library+# 4. export via fusion_export_optimized_glb+```++Because it is name-driven, it survives a re-import of an updated STEP: the same rules recolor the+new geometry without hand-picking bodies in the UI.++> **Referenced example, not yet documented here.** Drew posted a before/after in Google Chat showing+> exactly this on the Essemtec machine, a grey STEP turned into a colored assembly. I could not+> locate that post while writing this (searched Workcell, Hydrogen, Workcell Vision System and the+> other active spaces for "essemtec", "coloriz*" and today's 3D messages, no match). If you point me+> at it, the before/after images belong right here as the worked example.++## Library 3D packages++Separate from board export, the bridge builds 3D packages for component libraries:+`fusion_make_3d_package`, `fusion_attach_3d_package`, `fusion_build_library_3d`,+`fusion_capture_library_views`. See [electronics libraries](electronics-libraries.md).++One relevant fix: auto-orient used to rotate the **smallest** bbox dimension to Z, which is right for+a flat SMD chip and wrong for a tall through-hole pin, so tall parts were laid on their side. Tall+parts now stay vertical.++## Gotchas++- Export verbs need the **desktop** app. There is no Linux desktop Fusion.+- A relay timeout at ~60s on a large export does not mean failure; the work continues on the bridge.+- Do not quantize an optimized GLB's bounds; the bounds lie afterwards. Use `--instance false` when+  optimizing library group GLBs.
docs/aps-cloud-search.mdadded+111
@@ -0,0 +1,111 @@+# APS cloud search++Fast, server-indexed search over your team's Fusion cloud files, plus browse, download, upload and+version history. This is the single most-used part of the bridge, and it replaced a path that was+genuinely unusable.++## Why this exists++Fusion's in-app Python API has **no indexed file-search endpoint**. The only way to find a file was+to walk the cloud Data API one folder at a time, each folder a separate HTTP round trip to+Autodesk. On a real team hub that took **30+ minutes** and could crash Fusion outright with+`WinError 10054`.++Autodesk Platform Services (APS) Data Management **does** expose indexed search. Same data, same+account, roughly **2 seconds** instead of half an hour.++So the old verbs are gone. `fusion_search_cloud_files` and `fusion_walk_cloud_tree` are+**hard-blocked** and return a refusal that carries live APS state and points at `fusion_aps_search`.+APS is not an optimization here, it is the only cloud-search path.++## It never costs money++Two independent guarantees, both enforced in code:++1. **Allowlist.** The bridge calls only Data Management (`/project/v1/`, `/data/v1/`) and OSS+   storage (`/oss/v2/`), which are in APS's always-free set. The paid APIs (Model Derivative,+   Design Automation, Reality Capture) are never called. A path outside the allowlist is refused+   before the request leaves.+2. **A monthly call cap**, well under the free quota, that fails closed.++A card on file at Autodesk is only ever for identity verification. `fusion_aps_status` reports+`callsThisMonth`, `monthlyCap` and `capRemaining` so this is auditable.++## Setup, once per company plus once per user++```bash+adom-desktop fusion_aps_status '{}'                                  # configured? signed in?+adom-desktop fusion_aps_set_client_id '{"clientId":"..."}'           # once per company+adom-desktop fusion_aps_signin '{"profile":"chrome:[email protected]"}'   # once per user+```++Register a Desktop/PKCE app at [aps.autodesk.com](https://aps.autodesk.com) with the Data+Management API enabled and the callbacks the status verb lists. Only the Client ID is stored; the+bridge runs its own PKCE OAuth with no client secret.++**Sign-in is usually silent.** If your browser profile already holds an Autodesk session, consent+goes through without a password or 2FA. The best moment to run it is **immediately after a Fusion+sign-in**, while that SSO session is warm. `fusion_readiness` checks APS on every launch and says so+in its hint, precisely so this is not a second login for the user.++No Adom browser extension? Still fine. Sign-in falls back to `nbrowser_open_os_window`, an+extension-free open of the consent URL in a real browser profile. The extension only buys+automation (the assistant can click Approve for you); it is never a prerequisite.++## Searching++```bash+adom-desktop fusion_aps_search '{"query":"charger","limit":10}'+adom-desktop fusion_aps_open   '{"query":"BQ25792"}'      # search then open the best match+```++`fusion_aps_search` returns name, project, hub and URN. `fusion_aps_open` resolves a name straight+to an open document, which is the verb you usually want.++## The rest of the surface++| verb | what it does |+|---|---|+| `fusion_aps_browse` | walk hubs / projects / folders |+| `fusion_aps_recent` | recently modified files |+| `fusion_aps_file_info` | metadata for one file |+| `fusion_aps_versions` | version history |+| `fusion_aps_download` | pull a file out of the cloud |+| `fusion_aps_upload` | push a local file in |+| `fusion_aps_create_folder` | make a folder |+| `fusion_aps_get` | raw authenticated GET, for development |+| `fusion_aps_set_browser` / `get_browser` / `forget_browser` | remember which browser profile authed |+| `fusion_open_by_urn` | open a specific URN directly |++## Reading `fusion_aps_status` honestly++`signedIn` means **usable right now**, not "a token file exists". It refreshes if it can. Two+companion fields exist because those are different states:++- `hasStoredToken` - a token is on disk, possibly dead+- `refreshFailed` - the stored token is dead and could not be refreshed++This distinction was added after status cheerfully reported `signedIn: true` while every search+failed with `APS token_expired`. If you see `refreshFailed: true`, run `fusion_aps_signin` again.++## When a search returns nothing++- Confirm `fusion_aps_status` shows `signedIn: true` and `refreshFailed: false`.+- APS indexes the **cloud**. Unsaved local documents are not searchable.+- The design may live in a hub the signed-in account cannot see. `fusion_aps_browse` shows which+  hubs are reachable.++## Electronics file hierarchy++A Fusion electronics design is a parent/child chain, and **you open the PROJECT**, never a child:++```+PROJECT        EcadDesignProductType   <- open this+  |- schematic   SchematicProductType+  |- board/.brd  BoardProductType+  |- 3D PCB      DesignProductType     (generated from the .brd; a leaf)+```++Opening a child gives an isolated or empty view. The open verbs warn in their `_hint` when you open+a non-`EcadDesignProductType` file. See [schematics](schematics.md) and+[2D board layout](board-layout-2d.md).
docs/board-layout-2d.mdadded+84
@@ -0,0 +1,84 @@+# 2D board layout and manufacturing output++The PCB side of Fusion Electronics: the board editor, design rules, layer discovery, and every+artifact a fab or assembler needs.++## Getting to the board++```bash+adom-desktop fusion_aps_open   '{"query":"BQ25792"}'   # open the PROJECT (not the .brd)+adom-desktop fusion_show_2d_board '{}'                 # PCB editor+adom-desktop fusion_board_info '{}'                    # size, layer count, component count+```++Opening the `.brd` child directly gives an isolated view. See [schematics](schematics.md) for why.++## Design rules++```bash+adom-desktop fusion_load_design_rules '{}'                       # what is loaded now+adom-desktop fusion_set_design_rules  '{"preset":"adom"}'        # apply Adom's DRU+```++Adom's DRU rules ship with the bridge under `resources/`. Applying a house ruleset before routing is+what keeps a board manufacturable at the fab you use.++## Layers++`fusion_detect_layers` reports the real layer stack of the open board rather than assuming a+2-layer or 4-layer default. It is backed by a layer-detect ULP shipped in `resources/`.++`fusion_board_stackup` returns the physical stackup: per-layer thickness, copper weight and+material. This matters more than it sounds. When you render a board in 3D, the **board bbox is the+FR4 body**, never the whole assembly, because components overhang the edges and tower above it.+Getting that wrong produces a 3D model that looks correct and measures wrong.++## Routing++Fusion Electronics is EAGLE underneath, so routing is driven with EAGLE commands:++```bash+adom-desktop fusion_electron_run '{"command":"RATSNEST;"}'+adom-desktop fusion_electron_run '{"command":"AUTO ;"}'      # autoroute+```++`fusion_execute_text_command` is **space-delimited**. Note the space in `AUTO ;`.++## Manufacturing output++| verb | produces |+|---|---|+| `fusion_export_gerbers` | Gerbers + drill files, via a CAM job |+| `fusion_export_bom` | electronics BOM (this is the **electronics-only** one, see below) |+| `fusion_export_cpl` | component placement list for pick-and-place |+| `fusion_export_dxf` / `fusion_export_dwg` | 2D vector output |++JLCPCB CAM jobs ship with the bridge in `resources/`, so gerbers come out in the shape JLC expects+rather than a generic set you have to fix.++```bash+adom-desktop fusion_export_gerbers '{"outputPath":"C:/tmp/fab"}'+adom-desktop fusion_export_bom     '{"outputPath":"C:/tmp/bom.csv"}'+adom-desktop fusion_export_cpl     '{"outputPath":"C:/tmp/cpl.csv"}'+```++### `fusion_export_bom` is electronics-only++On a mechanical assembly it returns `No board open (current workspace: Design)`. That is expected.+For mechanical assemblies use [`fusion_assembly_bom`](mechanical-bom.md), which is kit-aware and+matches Fusion's Manage -> BOM.++## Publish the whole design, not just the 3D++When a board gets a wiki page, that page repo should carry **every** exportable file: `.f3d`/`.step`+/`.usdz`, EAGLE `.brd` + `.sch`, Fusion `.fbrd` + `.fsch`, gerbers, BOM, CPL, GLBs and renders. The+page exists so someone else can build the design, and a 3D model alone does not let them.++## Gotchas++- Wrong workspace is the usual cause of a refused verb. Check `isElectronics` in+  `fusion_get_app_state`.+- A blocking dialog (recovery prompt, update nag) will swallow commands silently. Clear it with+  `fusion_dismiss_blocking_dialogs`.+- Gerber export runs a CAM job and is genuinely slow on a dense board. A relay timeout at ~60s does+  not mean it failed; the work continues on the bridge, so poll.
docs/electronics-libraries.mdadded+80
@@ -0,0 +1,80 @@+# Electronics library manager++Building and maintaining Fusion Electronics libraries: symbols, footprints, and real 3D packages,+generated rather than hand-drawn.++## What a library part needs++A usable Adom part is three things that must agree with each other:++1. a **symbol** (schematic)+2. a **footprint** (the pads the part solders to)+3. a **3D package** (what it looks like on the board)++Get the third one misaligned and the board renders wrong; get the second wrong and it does not+solder.++## Opening and inspecting++```bash+adom-desktop fusion_open_lbr '{"lbrPath":"C:/tmp/AdomBasicParts.lbr"}'+```++`.lbr` is the EAGLE library format Fusion Electronics uses.++## Generating packages++```bash+adom-desktop fusion_generate_package '{"package":"R_4k7", ...}'+```++`fusion_generate_package` builds a parametric IPC package, including laser-etched markings, rather+than drawing pads by hand. IPC-derived geometry is what keeps footprints consistent across a+library.++## 3D packages++```bash+adom-desktop fusion_make_3d_package   '{...}'      # build one+adom-desktop fusion_attach_3d_package '{...}'      # bind it to a footprint+adom-desktop fusion_build_library_3d  '{"lbrPath":"C:/tmp/newlib/AdomBasicParts2.lbr",+                                        "parts":[{"package":"R_4k7",+                                                  "lbrPath":"C:/tmp/newlib/R_4k7.lbr",+                                                  "modelPath":"C:/tmp/newlib/R_4k7.step"}]}'+adom-desktop fusion_capture_library_views '{...}'  # render views for a wiki page+```++`fusion_build_library_3d` is the batch path: hand it a list of parts with their STEP models and it+binds 3D to each package in one pass, returning per-part success plus screenshots.++### The auto-orient rule++Auto-orient used to rotate the **smallest** bounding-box dimension to Z. That is correct for a flat+SMD chip and badly wrong for a tall through-hole machine pin, which ended up lying on its side.++Tall parts now stay vertical: if `dz` is already the largest dimension, the part is left alone and+only flat SMD parts get tipped. If you are adding a part type with unusual proportions, check the+3D orientation before committing it to a library.++## Verifying a part before you commit it++A part that is subtly wrong is worse than a missing one, because it silently produces bad boards.+Check:++- **Pin 1** is where the footprint says it is.+- The **seat plane** is at z=0, so the part sits on the board rather than floating or sinking.+- The 3D body's footprint **matches the pad geometry**.++The `adom-chipfit` tool exists for exactly this and validates a 3D chip GLB against its+`.kicad_mod`: pin-1 alignment, package-family guard, seat-plane delta-z and size match.++## Publish the whole part++When a component gets a wiki page, publish everything: symbol, footprint, 3D model, the source+`.lbr`, and the STEP. Someone else's board depends on being able to use the part, not just look at+it.++## Related++- [3D board view and export](3d-viewer-and-export.md) for the GLB pipeline these models feed.+- [2D board layout](board-layout-2d.md) for how footprints become manufacturing output.
docs/fusion-mcp-server.mdadded+135
@@ -0,0 +1,135 @@+# Autodesk's Fusion MCP server++Autodesk and Anthropic shipped a **Fusion MCP server**: Fusion's own text-to-CAD surface, exposed+over MCP. This bridge proxies it, so an Adom AI running in the cloud can use it.++## Why the bridge has to proxy it++The MCP server is a **local HTTP/JSON-RPC endpoint**:++```+http://127.0.0.1:27182/mcp+```++It binds **loopback on the user's machine** and was designed for Claude Desktop running on that+same machine. An Adom AI runs in a **cloud container** and cannot reach the user's `127.0.0.1` at+all.++The bridge already runs on that machine. So it is exactly the right proxy: these verbs hand a cloud+AI Autodesk's MCP tools without reimplementing any of them.++## Turning it on++It is **off by default**, and there is **no API for the toggle** (verified by enumerating every+attribute on `apiPreferences`). The bridge turns it on for the user by driving the Preferences UI:++```bash+adom-desktop fusion_mcp_enable '{}'+```++which opens Preferences, expands General, selects API, ticks+`Fusion MCP Server (runs locally on this device)`, clicks Apply then OK, and then **verifies the+port is listening** before reporting success.++![The API page with the MCP server toggle](img/prefs-3-api-mcp.png)++Manual path, if you would rather: profile icon (top right) -> Preferences -> General -> API -> tick+the box -> Apply.++> ⛔ **The setting is discarded unless Apply is clicked.** Restarting or killing Fusion with the+> dialog open reverts it, and the port stays closed while the checkbox *looked* right. This is a+> real trap, it cost a debugging cycle here.++See [driving Fusion's preferences](fusion-preferences.md) for the general technique.++## The handshake, which the bridge handles for you++The server speaks MCP **streamable-HTTP**, so a bare POST fails:++```+400  {"error": "Missing MCP-Session-Id header"}+```++You must `initialize`, capture the `MCP-Session-Id` **response header**, send+`notifications/initialized`, then pass that header on every later call. The bridge does all of it+and silently re-establishes a dropped session, so you never see it.++Handshake confirmed live: protocol `2024-11-05`, serverInfo `MCP Server Adapter 1.0.0`.++## The verbs++| verb | what it does |+|---|---|+| `fusion_mcp_status` | is it up? serverInfo + live tool list |+| `fusion_mcp_enable` | turn it on via the Preferences UI, then verify the port |+| `fusion_mcp_tools` | full tool list **with input schemas**, for building a valid call |+| `fusion_mcp_call` | call any tool: `{tool, arguments, timeout}` |+| `fusion_mcp_resources` | list resources, or read one by `uri` |++## What Autodesk exposes++Four tools:++| tool | args | what it does |+|---|---|---|+| `fusion_mcp_read` | `queryType`, `apiCategory`, `filter`, `name`, `operation`, `project`, `direction`, `height`, `antiAliasing` | read geometric properties and data from the active model |+| `fusion_mcp_update` | `featureType` | update the active model |+| `fusion_mcp_execute` | `featureType`, `object` | execute operations in the active model |+| `fusion_mcp_electronics_read` | `entity_type`, `object` | read Electronics design data (schematic, board, library) |++`fusion_mcp_electronics_read` is the interesting one for us. Its `entity_type` covers the whole+EAGLE-derived object model: `electronics.Board`, `electronics.Element`, `electronics.Contact`,+`electronics.ContactRef`, `electronics.Device`, `electronics.DeviceSet`, `electronics.Gate`,+`electronics.Bus`, `electronics.Error` (ERC/DRC), `electronics.Frame`, `electronics.Grid`,+`electronics.Attribute`, `electronics.Dimension`, `electronics.Circle` and more.++Plus resources describing those schemas:++```+resource://mcp.electronics_entity_types+resource://mcp.electronics_schema_board+resource://mcp.electronics_schema_contact+...+```++```bash+adom-desktop fusion_mcp_resources '{}'+adom-desktop fusion_mcp_resources '{"uri":"resource://mcp.electronics_entity_types"}'+```++## Calling a tool++```bash+adom-desktop fusion_mcp_tools '{}'                      # get the input schemas first+adom-desktop fusion_mcp_call '{"tool":"fusion_mcp_electronics_read",+                               "arguments":{"entity_type":"electronics.Element"}}'+```++MCP acts on the **active Fusion document**, so open one first (`fusion_aps_open`). A complaint about+no active document usually means nothing is open, not that the call was malformed.++## MCP tools vs this bridge's verbs++They complement each other; neither replaces the other.++**Prefer a native `fusion_*` verb when one exists.** They are tested here, they carry hints that+teach the calling AI what to do next, they handle the awkward parts (kit-aware BOM counting, the+2-minute sign-in clock, GLB optimization), and they work whether or not the user has enabled MCP or+holds a Fusion subscription.++**Reach for MCP** when you want Autodesk's own text-to-CAD surface: natural-language geometry+creation, feature execution, and the structured Electronics object model, which is richer for+querying a board than anything hand-rolled here.++## Requirements and limits++- **Fusion subscription.** Autodesk gates the MCP server to subscribers.+- **Fusion must be running.** The server lives inside Fusion; if it is not running, the port is+  closed.+- **Loopback only**, hence this proxy.+- The toggle is UI-only, so `fusion_mcp_enable` briefly uses the foreground and says why.++## Sources++- [Fusion MCP server, Autodesk developer blog](https://blog.autodesk.io/fusion-mcp-server/)+- [Bringing Fusion onto Claude for Creative Work, APS blog](https://aps.autodesk.com/blog/bringing-fusion-claude-creative-work)
docs/fusion-preferences.mdadded+113
@@ -0,0 +1,113 @@+# Driving Fusion's preferences++Fusion's Python API exposes only a **thin slice** of its preferences. Everything else, including+the Fusion MCP Server toggle, is UI-only. This is how the bridge reaches all of it, and why the+technique matters well beyond one checkbox.++## What the API gives you++| object | what you can set |+|---|---|+| `generalPreferences` | theme, orbit scheme, modeling orientation, scroll-zoom direction, camera pivot, gesture nav |+| `apiPreferences` | `debuggingPort`, `defaultScriptLanguage`, `defaultPathForScriptsAndAddIns`, `isDeveloperToolsEnabled` |+| `defaultUnitsPreferences`, `graphicsPreferences`, `networkPreferences`, `materialPreferences` | a handful each |++That is it. There is **no API for the MCP server toggle**, verified by enumerating every+non-callable attribute on `apiPreferences` on Fusion 2703.1.20. So a useful setting was+unreachable from code.++`fusion_set_preference` / `fusion_get_preferences` cover the API-exposed slice. Everything below is+for the rest.++## What works: Preferences opens by command++```+Commands.Start PreferencesCommand+```++No menu hunting, no clicking the profile icon, no coordinate guessing to get the dialog up. Fire it+through `fusion_execute_text_command` and the dialog appears.++Better still, the **left-hand section tree is exposed to UIA**, so top-level sections (General,+Tokens, Material, Graphics, Network, Data Collection and Use, Unit and Value Display, Default Units,+Preview Features, Compatibility & Troubleshooting) can be found by name in the background.++## The catch: child sections are invisible to UIA++The sections *under* General (API, Design, Manufacture, Electronics, Render, Drawing, Simulation)+are rendered **lazily by Qt** and never enter the UIA tree, even after `desktop_ui_expand` reports+success. Dumping the dialog's controls returns the ten top-level `TreeItem`s and nothing else.++So navigating into a child section needs an **image-space click**, which takes the foreground. That+is why the bridge announces it with a short on-screen caption saying why, and why `fusion_prefs_open`+hands you a screenshot to click rather than pretending control names exist.++## The three steps, as they look++**1. Preferences opens on General.** Note the collapse arrow beside "General".++![Preferences open on the General page](img/prefs-1-open.png)++**2. General expanded.** API, Design, Manufacture, Electronics, Render, Drawing and Simulation+appear. None of these are in the UIA tree.++![General expanded to reveal API](img/prefs-2-general-expanded.png)++**3. The API page**, with `Fusion MCP Server (runs locally on this device)` and its port, 27182.++![The API page with the MCP server toggle](img/prefs-3-api-mcp.png)++## Using it++```bash+adom-desktop fusion_prefs_open  '{"section":"api"}'   # opens + navigates + screenshots+# LOOK at the returned screenshot, then click any control:+adom-desktop desktop_click '{"space":"image","shotId":"<from response>","x":690,"y":476,"hwnd":<hwnd>}'+adom-desktop fusion_prefs_close '{"save":true}'       # Apply + OK+```++`fusion_prefs_open` returns `shotId`, `imageWidth`, `imageHeight`, `screenshot` and `knownSections`.+Sections it can jump to: `general`, `api`, `design`, `manufacture`, `electronics`, `render`,+`drawing`, `material`, `graphics`, `network`, `preview features`.++## The rule that bites: nothing is saved until Apply++> Changes are **discarded** unless Apply/OK is clicked. Killing or restarting Fusion with the dialog+> still open loses them.++This is not theoretical. It is exactly how an earlier MCP enable silently reverted: the box was+ticked, Fusion was restarted before Apply, and the port stayed closed while everything *looked*+correct. Always finish with `fusion_prefs_close {"save":true}`.++## Worked example: enabling the MCP server++That whole flow is wrapped as one verb, and it is the model for automating any other preference:++```bash+adom-desktop fusion_mcp_enable '{}'+```++which opens Preferences, expands General, selects API, ticks the box, clicks Apply then OK, and+verifies the port is listening before reporting success. See+[the MCP server guide](fusion-mcp-server.md).++## Doing this for other settings++The pattern generalizes:++1. `fusion_prefs_open {"section":"<section>"}`+2. Read the screenshot, find the control you want+3. `desktop_click {space:"image", shotId, x, y, hwnd}`+4. `fusion_prefs_close {"save":true}`+5. **Verify the effect**, do not trust the click. For MCP that means probing the port; for a+   rendering setting it means observing the change.++If you automate a section often, add its fractional coordinates to `_PREF_SECTIONS` in `server.py`+so `fusion_prefs_open` can jump straight there.++## Never surprise the user++Opening Preferences takes the foreground on a machine someone is working on. The bridge puts a+caption up first saying what it is doing and why, sized to how long the sentence takes to read, and+never leaves it up longer than about three seconds. Background UIA is always preferred; foreground+is a last resort for controls Qt does not expose.
docs/img/prefs-1-open.pngadded
⋯ 1 unchanged line ⋯
docs/img/prefs-2-general-expanded.pngadded
⋯ 1 unchanged line ⋯
docs/img/prefs-3-api-mcp.pngadded
⋯ 1 unchanged line ⋯
docs/mechanical-bom.mdadded+107
@@ -0,0 +1,107 @@+# Mechanical BOM and physical properties++Two verbs that turn a Fusion **mechanical assembly** into a purchasing bill of materials, and read+per-part volume and mass for costing.++These exist because `fusion_export_bom` is **electronics-only**. Point it at a Design-workspace+assembly and it tells you `No board open (current workspace: Design)`. There was no mechanical+equivalent, so anyone who wanted a parts list had to hand-write an occurrence walk inside+`fusion_run_modeling_script`.++Contributed by **Oliver**, who built [BOM Forge](https://wiki.adom.inc/adom/bom-forge) on this+bridge, hit both gaps, and sent a PR with the fix.++## The counting problem these solve++Walk `allOccurrences` naively and you count every screw in the model. But when you buy a+"bracket **with fasteners**" kit, the screws arrive in that box. Counting them separately means+ordering hardware you already have.++Measured on a real assembly:++| part | flat walk | kit-aware | error |+|---|---|---|---|+| T Nut Drop In 2020 M5 | 28 | **14** | 2.0x over |+| T Nut Eco 4040 M8 | 44 | **18** | 2.4x over |+| BHCS M8x16 | 46 | **20** | 2.3x over |+| BHCS M5x8 (fully bundled) | counted | **0** | should not be ordered at all |++The kit-aware counts match Fusion's own **Manage -> BOM**.++## `fusion_assembly_bom`++Structured, kit-aware BOM for the active assembly. Recurses organizational subassemblies, but+stops at a physical part or a purchased kit.++```bash+adom-desktop fusion_assembly_bom '{}'+adom-desktop fusion_assembly_bom '{"treatAsUnit":["with fasteners","bearing unit"],+                                   "outputPath":"C:/tmp/bom.csv"}'+```++| arg | meaning |+|---|---|+| `treatAsUnit` | name substrings counted as ONE unit and not exploded. Default `["with fasteners"]`, case-insensitive substring match |+| `exclude` | top-level component-name prefixes to skip entirely |+| `includePhysicalProperties` | adds `volume_cm3` / `mass_kg` per line (slow, see below) |+| `outputPath` | also write a CSV to this Windows path |++Returns `parts[]` of `{componentName, partNumber, description, material, quantity, bodies}`, plus+`partCount` and `totalInstances`.++**The leaf rule**, which is the whole trick: a component is a leaf when its name matches+`treatAsUnit` **or** it has `bRepBodies.count > 0`. Anything else is organizational and gets+recursed through.++## `fusion_physical_properties`++Per-component volume, mass, density, area and center of mass, read directly rather than through a+modeling script.++```bash+adom-desktop fusion_physical_properties '{}'+adom-desktop fusion_physical_properties '{"names":["Bracket A","Idler"],"accuracy":"high"}'+```++| arg | meaning |+|---|---|+| `names` | component names to measure. Default: all |+| `accuracy` | `low` \| `medium` \| `high` \| `veryhigh`. Default `low` |++Returns `properties` keyed by component name, each `{volume_cm3, mass_kg, density, area_cm2,+center_of_mass}`.++## Performance, and why the timeout is 300s++`getPhysicalProperties` runs **per component on Fusion's main thread**. On a large assembly this is+the slow part, not the walk. Two consequences:++- `includePhysicalProperties` defaults to **false** on the BOM. Leave it off unless you need it.+- When you only need a few parts, call `fusion_physical_properties` with an explicit `names` list+  instead of pulling properties through the whole BOM.++Both verbs carry a 300s add-in timeout for this reason. If a call returns a relay timeout at ~60s,+the work is still running on the bridge; poll rather than treating it as failure.++## Worked example: a costed BOM++```bash+# 1. structured parts list, CSV for purchasing+adom-desktop fusion_assembly_bom '{"outputPath":"C:/tmp/bom.csv"}'++# 2. volumes only for the printed parts, to price filament+adom-desktop fusion_physical_properties '{"names":["Spool Holder","Guide Block"]}'+```++That is the shape BOM Forge uses: the BOM for quantities, targeted physical properties for the+printed parts, then pricing applied downstream.++## Limits worth knowing++- The BOM aggregates to a **flat parts list with quantities**, not a hierarchy with a `Level`+  column. That matches Manage -> BOM, which is what purchasing needs. If you want the hierarchy,+  open an issue and it can be added without breaking the response shape.+- `treatAsUnit` is a case-insensitive **substring** match on the component name. Kit naming+  conventions beyond "with fasteners" must be passed explicitly.+- A component with bodies **and** children is treated as a leaf, so its children are not counted.+  That is intentional: a physical part is a leaf.
docs/schematics.mdadded+87
@@ -0,0 +1,87 @@+# Schematics++Opening, viewing and authoring Fusion Electronics schematics, including bringing legacy EAGLE+designs in so their parts instantiate.++## Open the PROJECT, never the schematic++This is the mistake that costs the most time, so it leads:++```+PROJECT        EcadDesignProductType   <- open THIS+  |- schematic   SchematicProductType+  |- board/.brd  BoardProductType+  |- 3D PCB      DesignProductType+```++Opening the schematic child directly gives you an isolated view with no board relationship, and you+cannot pull the board back out of it. Same-named copies in *other* projects are usually 3D-model+derivatives (`DesignProductType`), not the project. The open verbs reprimand you through their+`_hint` when the file you opened is not an `EcadDesignProductType`.++```bash+adom-desktop fusion_aps_open '{"query":"BQ25792"}'    # resolves + opens the project+adom-desktop fusion_show_schematic '{}'               # switch to the schematic editor+```++## `fusion_show_schematic`++Switches the active electronics design into the schematic editor. Pairs with+[`fusion_show_2d_board`](board-layout-2d.md) and `fusion_show_3d_board`, which are the same idea for+the other two views.++Use `fusion_get_app_state` to confirm where you landed: it reports `activeDocument`,+`activeWorkspace` and `isElectronics`.++## Importing a legacy EAGLE schematic++`fusion_new_electronics_from_eagle` is the only reliable way to author a Fusion board from EAGLE+source, and the reason is worth understanding:++- `Document.newDesignFromLocal` opens the schematic editor but does **not instantiate the parts**.+  You get an empty-looking design.+- The Fusion-native `.fsch` / `.fbrd` container is opaque binary you cannot author offline.++So this verb fires Fusion's own `ImportSCHAndBRDCmd` and drives both native Open dialogs (the `.sch`+then the `.brd`) **in the background** with UIA by accessible name, no foreground steal.++```bash+adom-desktop fusion_new_electronics_from_eagle '{+  "schPath":"C:/Users/me/adom-lib/MyBoard.sch",+  "brdPath":"C:/Users/me/adom-lib/MyBoard.brd"+}'+```++Stage both files onto Windows first with `send_files`. `brdPath` defaults to the sibling `.brd`.++**Paths with spaces work.** They used to fail silently; the open verbs now quote the path, fall back+to the Windows 8.3 short name, and verify `activeDocument` changed rather than reporting a+no-op as success.++## Driving the schematic editor++Fusion Electronics is EAGLE underneath, so it takes EAGLE commands:++```bash+adom-desktop fusion_electron_run '{"command":"GRID MM 1;"}'+adom-desktop fusion_execute_text_command '{"command":"Commands.Start ..."}'+```++`fusion_execute_text_command` is **space-delimited**, which trips people up.++For clean captures there are view controls that do not disturb the user:+`fusion_electron_zoom`, `fusion_electron_pan`, `fusion_electron_select`.++## Getting a picture out++`fusion_take_screenshot` grabs the Fusion window. For a document-level image of the schematic,+switch to it with `fusion_show_schematic` first, then capture.++## Gotchas++- `isElectronics: false` in `fusion_get_app_state` means you are in the mechanical Design workspace.+  Electronics verbs will refuse; open the project first.+- If an open appears to do nothing, check `fusion_dismiss_blocking_dialogs`. A recovery prompt or an+  update nag can sit in front of the document and swallow the action.+- The schematic and board are **separate documents in one project**. Saving one does not save the+  other.
docs/sign-in-and-updates.mdadded+113
@@ -0,0 +1,113 @@+# Sign-in, sessions and automatic updates++Everything about getting Fusion running, signed into the right Autodesk account, and kept current,+without interrupting the person using the machine.++## Lifecycle++```bash+adom-desktop fusion_readiness '{}'   # installed? running? ready? (never launches)+adom-desktop fusion_start '{}'       # launch + wait for the add-in+adom-desktop fusion_stop  '{}'       # graceful close+adom-desktop fusion_kill  '{}'       # force-kill a wedged session+```++`fusion_readiness` is the verb to poll. It live-detects on **every** call rather than trusting a+bridge-start snapshot, because that snapshot is wrong in both directions: it reported+`installed: false` on a machine where Fusion had since been installed, and `ready: true` on one+where Fusion had been removed.++**Never ask the user to restart Fusion or enable the add-in.** That is the bridge's job.++## Signing in through the right browser profile++Fusion fires its OAuth at the **OS-default browser**. If your Autodesk seat lives in a different+Chrome profile (work vs personal), Fusion signs into the wrong account or stalls.++```bash+adom-desktop fusion_signin '{"profile":"chrome:[email protected]"}'+```++What it does: reads the full authorize URL (client_id, PKCE, state, request_id) out of the browser's+**History DB**, then re-opens it in the profile you named. The URL completes back to the running+Fusion through the `idmgr/callback` plus `autodesk://` protocol handoff, matched by `request_id`, so+the browser no longer has to be the OS default.++The verb **clicks Fusion's Sign In button for you**, in the background. Never tell the user to click+it. It is a UIA-invokable control (`...QTSignInDialog.backgroundWidget.signin_button`), so no+foreground steal, no cursor move.++### SSO beats passwords++If the target profile already holds a Google/Apple/Microsoft identity, the whole login completes+with **no password and no 2FA**. Two rules make that work:++1. Click **"Continue with Google"** (or Apple/Microsoft) **before** typing any email. Entering an+   email commits you to the password path.+2. It must be a **fresh** flow. Re-navigating a `flowId` that already advanced dead-ends on the+   password screen with no way back.++### The ~2-minute clock++Fusion's sign-in request **expires in about two minutes**. A captured URL older than ~150s returns+`stage: "stale"`; restart Fusion for a fresh request rather than completing a dead one, which yields+"Sign-in request expired" and no handoff.++### When a human is required++If Autodesk demands a password with no saved credential, that is a genuine wall. The bridge+**toasts the user** from code (you do not have to ask it to) and hands the calling AI a mechanical+way to proceed. For an emailed 6-digit code:++```bash+adom-desktop fusion_signin_2fa '{"code":"123456"}'+```++which types it via background UIA. If `adom-google` is available, read the code out of Gmail and+submit it rather than asking the user for it.++Passwords and 2FA secrets are never typed by the bridge on the user's behalf beyond this explicit+verb.++## One login should cover APS too++Fusion sign-in and [APS](aps-cloud-search.md) are both Autodesk logins. They cannot share a token+(Autodesk DPAPI-encrypts Fusion's store with app entropy, deliberately), but they **can share the+warm browser SSO session**. So APS consent runs right after a Fusion sign-in while that session is+warm, and `fusion_readiness` reports APS state on every launch. The user should authenticate once.++## Automatic updates++Fusion ships updates constantly and nags with an "Update Now / Update Later" panel and a countdown.++The bridge clicks it **for you, in the background**, so you never see it. Fusion downloads and swaps+to the new build on the next launch.++```bash+adom-desktop fusion_set_auto_update '{}'                  # read current setting+adom-desktop fusion_set_auto_update '{"enabled":false}'   # opt out, STICKY+```++On by default for everyone. Opting out persists to `~/.adom/fusion-bridge/prefs.json` and is+honoured permanently; once off, the bridge never touches your update prompts again.++Honest limit: the nag only enters the UIA tree while its "View Job Status" panel is **open**, and+there is no API to enumerate a pending update while it is closed. So the bridge sweeps on a schedule+and catches the nag whenever Fusion shows it, rather than pre-empting it.++## Never steal the foreground++The bridge runs on a machine someone is working on. Background UIA (`desktop_ui_click`,+`desktop_ui_set`) is always tried first: no focus steal, no cursor move. A foreground click is a last+resort for controls with no UIA node, and when one is unavoidable the bridge puts a short on-screen+caption up first saying **why**. Windows it opens for OAuth are tracked and closed automatically once+the sign-in completes, so the desktop is not left littered.++## Is there a CLI for Fusion auth?++Effectively no, and this was investigated on a real machine.+`AdskIdentityManager.exe` takes flags, but they are IPC plumbing (`--process_name`,+`--server_name`), not auth commands: it is a long-running Identity SDK broker and Fusion is its+client over that named channel. There is no `--login`/`--user`/`--token`, and tokens live in a+DPAPI-encrypted store, so there is no headless way to initiate or inject a login. Driving the GUI in+the background, as above, is the answer.
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-desktop-fusion-bridge",-  "version": "1.7.14",+  "version": "1.8.0",   "type": "app",   "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Desktop loads the bridge runtime itself from the release zip.",   "dependencies": {
page.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-desktop-fusion-bridge",-  "version": "1.7.14",+  "version": "1.8.0",   "type": "app",   "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Desktop loads the bridge runtime itself from the release zip.",   "dependencies": {
skills/fusion-mcp-and-preferences/SKILL.mdadded+116
@@ -0,0 +1,116 @@+---+name: fusion-mcp-and-preferences+description: >-+  Use Autodesk's Fusion MCP server (the Autodesk + Anthropic text-to-CAD connector) from an Adom+  cloud AI, and change ANY Fusion preference including ones the Python API does not expose. The MCP+  server binds loopback on the user's machine so a cloud container cannot reach it; the fusion+  bridge proxies it. Covers turning it on for the user (no API exists for the toggle, so the bridge+  drives the Preferences dialog), the MCP session handshake, the four Autodesk tools, and when to+  prefer a native fusion_* verb instead. Trigger words - fusion mcp, fusion mcp server, autodesk+  mcp, claude connector for fusion, text to cad, 127.0.0.1:27182, enable fusion mcp, fusion+  preferences, change a fusion setting, fusion prefs dialog, api preferences, developer tools+  fusion, fusion setting not in the api.+---++# Fusion MCP server + preferences++Two capabilities that turned out to be the same trick: Fusion exposes far less to its Python API+than to its UI, and the UI is drivable.++## The MCP server++Autodesk + Anthropic ship a **local** MCP server at `http://127.0.0.1:27182/mcp` exposing Fusion's+own text-to-CAD surface.++**It binds loopback on the user's machine.** An Adom AI runs in a cloud container and cannot reach+it. The fusion bridge runs on that machine, so it proxies:++```bash+adom-desktop fusion_mcp_status '{}'          # up? serverInfo + live tool list+adom-desktop fusion_mcp_enable '{}'          # turn it ON for the user+adom-desktop fusion_mcp_tools  '{}'          # tools WITH input schemas+adom-desktop fusion_mcp_call   '{"tool":"fusion_mcp_electronics_read",+                                 "arguments":{"entity_type":"electronics.Element"}}'+adom-desktop fusion_mcp_resources '{}'       # electronics entity schemas+```++### Do not ask the user to enable it++It is OFF by default and there is **no API for the toggle**. `fusion_mcp_enable` does it for them:+opens Preferences, expands General, selects API, ticks the box, Apply, OK, then verifies the port+is listening. Tell them it is handled. Only fall back to instructions if the verb reports failure.++### The trap that costs a debugging cycle++**The setting is discarded unless Apply is clicked.** Restart or kill Fusion with the dialog open+and it reverts, while the checkbox looked correct. If the port is closed but the user says they+enabled it, this is almost certainly why. Re-run `fusion_mcp_enable`.++### The handshake is handled++Streamable-HTTP: a bare POST returns `400 Missing MCP-Session-Id`. You must `initialize`, capture+the `MCP-Session-Id` **response header**, send `notifications/initialized`, then pass that header.+The bridge does this and re-establishes dropped sessions, so `fusion_mcp_call` just works.++### Autodesk's four tools++| tool | for |+|---|---|+| `fusion_mcp_read` | geometric properties / data from the active model |+| `fusion_mcp_update` | update the active model |+| `fusion_mcp_execute` | execute operations |+| `fusion_mcp_electronics_read` | Electronics design data by `entity_type` |++`entity_type` covers the EAGLE object model: `electronics.Board`, `.Element`, `.Contact`,+`.ContactRef`, `.Device`, `.DeviceSet`, `.Gate`, `.Bus`, `.Error` (ERC/DRC), `.Frame`, `.Grid`,+`.Attribute`. `fusion_mcp_resources` returns the schemas.++MCP acts on the **active document**. Open one first (`fusion_aps_open`).++### MCP vs native verbs++**Prefer a native `fusion_*` verb when one exists.** They are tested, they return hints that teach+the next step, they handle the awkward parts (kit-aware BOM counting, the 2-minute sign-in clock,+GLB optimization), and they work without a Fusion subscription or MCP enabled.++**Reach for MCP** for Autodesk's text-to-CAD surface and the structured Electronics object model,+which is richer for querying a board than anything hand-rolled.++Requirements: Fusion subscription, Fusion running, loopback only.++## Any preference, via the UI++The API exposes a thin slice (`generalPreferences`: theme/orbit/units; `apiPreferences`:+`debuggingPort`, `isDeveloperToolsEnabled`). Everything else is UI-only.++```bash+adom-desktop fusion_prefs_open  '{"section":"api"}'   # opens + navigates + screenshots+# look at the screenshot, then click what you see:+adom-desktop desktop_click '{"space":"image","shotId":"<from response>","x":690,"y":476,"hwnd":<hwnd>}'+adom-desktop fusion_prefs_close '{"save":true}'       # Apply + OK+```++Sections: `general`, `api`, `design`, `manufacture`, `electronics`, `render`, `drawing`,+`material`, `graphics`, `network`, `preview features`.++### Why it returns an image instead of control names++`Commands.Start PreferencesCommand` opens the dialog, and the **top-level** section tree IS in the+UIA tree. But the children under General (API, Design, Manufacture, Electronics, Render, Drawing,+Simulation) are rendered **lazily by Qt** and never appear there, even after `desktop_ui_expand`+reports success. So child navigation is an image-space click, which takes the foreground and is+announced with a caption.++### Rules++1. **Always** finish with `fusion_prefs_close {save:true}`. Nothing persists otherwise.+2. **Verify the effect**, do not trust the click. For MCP, probe the port. For a render setting,+   observe the change.+3. Prefer `fusion_set_preference` for anything in the API-exposed slice; it is background and does+   not touch the user's screen.+4. Never leave the dialog open. A modal Preferences window blocks other Fusion verbs.++## Full write-ups++- [docs/fusion-mcp-server.md](../../docs/fusion-mcp-server.md)+- [docs/fusion-preferences.md](../../docs/fusion-preferences.md) (with screenshots of all three steps)