Move Redis/RabbitMQ/Container/Kafka to optional plugins - #140
DianaSensei wants to merge 34 commits into
Conversation
These three tools were already Tier-B sidecar plugins, so they're a natural
fit for the existing install-from-URL mechanism (docs/plugin-sdk/05-external-install.md)
instead of being compiled into every build for users who don't need a
Redis/RabbitMQ/Docker client.
- Remove src/plugins/{redis-client,rabbit-client,container-manager}, their
src/components/tools/* sources, sidecar bins, and the RabbitMQ Rust
integration test/docker harness (moved to the plugin repo).
- Extend the external-install SDK bridge (window.__DEVTOOL_VENDOR__.platform)
and RemotePluginManifest (installer.ts + artifact_installer.rs) so a
URL-installed plugin can actually use sdk.storage/sdk.service/etc. and
declare a Tier-B service descriptor — neither was possible before, so this
migration would have shipped non-functional plugins otherwise.
- Split service_host.rs's allowlist into ALLOWED_SERVICES (trust boundary,
unchanged) vs BUNDLED_SERVICES (what actually ships in this build), so the
three sidecar names stay installable without being force-bundled.
- Add a Tailwind classname safelist for the moved tools' UI so their utility
classes aren't purged from the compiled CSS before anyone reinstalls them.
- Update registry/test fixtures, MCP background bridge, tool groupings, and
docs accordingly.
devtool-mcp-server.rs used to hardcode every tool's name/description/ inputSchema in one ~900-line function, compiled in at build time — which meant a tool installed later (Redis/RabbitMQ/Container Manager from developer-desktop-util-plugin) could never appear in an MCP client's tool list no matter what its own bridge did. Each tool now bundles its own MCP schema and registers it with the platform at runtime instead. - mcp_bridge.rs: add a tool registry (keyed by registering plugin id, so a re-mount replaces rather than duplicates) plus `GET /tools`, and two new Tauri commands (`mcp_register_tools`/`mcp_unregister_tools`) any bridge calls on mount/unmount. - devtool-mcp-server.rs: delete the ~900-line hardcoded tool_definitions() and the ToolRouter it fed. `list_tools()` now fetches the current registered set from `GET /tools` on every call (not once at startup), so a plugin installed after this process started still shows up without restarting it. get_scripting_reference is the one tool still answered locally (static reference content, no running app required). - Every existing tool now bundles and registers its own catalogue: apiclient/mcpTools.ts, mockserver/mcpTools.ts, mcpUtilityTools.ts (codec/jwt/json, re-registered whenever a per-tool toggle flips since a disabled one shouldn't even be listed), mcpMetaTools.ts (the platform's own devtool_mcp_* management tools, registered unconditionally under a synthetic "devtool-platform" id since it isn't a plugin). - Same for the three plugins that moved to developer-desktop-util-plugin (redis/rabbit/container) — their manifests' `commands` widened from the exact-match 'mcp_respond' to the 'mcp_' prefix (already how api-client/ mock-server declare it) so they can also call the two new commands. Also fixed two real pre-existing bugs this surfaced: `cargo test` didn't compile (a test fixture was missing the `service` field added to RemotePluginManifest in the previous migration — never caught before because this sandbox's rustc couldn't build a transitive dependency at all until upgraded just now), and an unrelated dead-code warning on the service_host.rs test-only constant.
Kafka Explorer was the last broker/messaging tool still compiled into this repo, kept native because its logic lived directly in kafka.rs instead of a JSONL sidecar. Now that devtool-svc-kafka exists as a Tier-B sidecar in developer-desktop-util-plugin (mirroring devtool-svc-redis/-rabbit/-container), it moves out the same way: - Delete kafka.rs, the compiled-in kafka-explorer plugin/UI, docker-compose files, and testing/kafka/. - service_host.rs allowlists devtool-svc-kafka (still trusted to run, same as before) without bundling it. - Drop the rskafka dependency from src-tauri/Cargo.toml. - Update tests, live-connection docs, and tool guides that referenced the compiled-in tool; fix the two suites (motion.test.tsx's git-ls-files scan, toolGroups.test.ts's pinned nav-entry count) that only broke once kafka-explorer actually left TOOL_DEFS. - Extend the Tailwind classnames safelist so Kafka Explorer's utility classes survive JIT purging once installed from the plugin repo. - Update CLAUDE.md and the optional-broker-plugins decision doc to describe the now fully plugin-driven MCP registration (each tool bundles and self-registers its own MCP schema) instead of a hardcoded tool list in devtool-mcp-server.rs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2
…arch-lmhjvh Platform/plugin architecture: SDK, Tier B sidecars, external plugin install
…ns (#122) Redis/RabbitMQ/Container Manager/Kafka Explorer can already be updated independently of the app (Settings → Extensions already had per-row "Check for update"/"Update" buttons, re-installing from the same source URL). This closes the remaining gap for "the app itself can update plugins independently": devtool now knows about available updates without the user opening Settings first. - src/contexts/ExtensionUpdateContext.tsx (new): checks every installed plugin/service against its own source URL once per app launch, in the background. Best-effort — a dead URL only drops that one item, never blocks the rest or throws. - Settings nav shows a pill badge with the update count next to "Plugins"; the gear icon in the main sidebar gets the same dot it already shows for the app's own self-update. - SettingsExtensionInstaller pre-fills each row's "Update" button from this auto-check, instead of requiring a manual "Check for update" click first (that button still works, for a version that changed after the startup check). - artifact_installer.rs: installing over an existing plugin/service (a different version) now deletes the OLD version's directory once the new one is written — it was previously left orphaned on disk forever (only a full uninstall ever cleaned `plugins/<id>/`). For services, the running sidecar is now stopped *before* writing/cleaning, not after, so the old binary isn't still open when its directory is removed. Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 Co-authored-by: Claude <noreply@anthropic.com>
…visory (#123) * Auto-check for plugin/service updates, badge, and clean up old versions Redis/RabbitMQ/Container Manager/Kafka Explorer can already be updated independently of the app (Settings → Extensions already had per-row "Check for update"/"Update" buttons, re-installing from the same source URL). This closes the remaining gap for "the app itself can update plugins independently": devtool now knows about available updates without the user opening Settings first. - src/contexts/ExtensionUpdateContext.tsx (new): checks every installed plugin/service against its own source URL once per app launch, in the background. Best-effort — a dead URL only drops that one item, never blocks the rest or throws. - Settings nav shows a pill badge with the update count next to "Plugins"; the gear icon in the main sidebar gets the same dot it already shows for the app's own self-update. - SettingsExtensionInstaller pre-fills each row's "Update" button from this auto-check, instead of requiring a manual "Check for update" click first (that button still works, for a version that changed after the startup check). - artifact_installer.rs: installing over an existing plugin/service (a different version) now deletes the OLD version's directory once the new one is written — it was previously left orphaned on disk forever (only a full uninstall ever cleaned `plugins/<id>/`). For services, the running sidecar is now stopped *before* writing/cleaning, not after, so the old binary isn't still open when its directory is removed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 * Fix Coverage and Security CI: missing sidecar placeholders, rustls advisory Both were pre-existing failures on main, unrelated to recent plugin- architecture work (every Coverage run for many builds back — including plain dependabot bumps — has been red the same way): - coverage.yml only ran `npm run build` before `cargo llvm-cov`, but tauri-build's build.rs checks that every `bundle.externalBin` resource (tauri.conf.json) exists on disk even for a plain test build. `beforeBuildCommand` normally produces the real devtool-mcp-server/devtool-svc-echo binaries via `npm run build:mcp-sidecar`/`build:service-sidecars`, but coverage.yml never runs those (and doesn't need real binaries, only to compile their crate) — every run failed at the very first build-script invocation with "resource path ... doesn't exist". Fixed by touching empty, correctly-named placeholder files before the coverage build, same workaround already used for local `cargo check`/`test`. - security.yml's cargo-audit job found a real RustSec advisory against rustls 0.23.43 (TLS 1.3 handshake messages incorrectly accepted across encryption level boundaries) — bumped to 0.23.45 via `cargo update -p rustls`, within the existing semver range. Not touched: pullfrog.yml's unpinned `actions/checkout@v6` and `pullfrog/pullfrog@v0` also fail the "Verify SECURITY.md claims" job, but that file is explicitly marked "DO NOT EDIT EXCEPT WHERE INDICATED" (bot-managed) — flagging for the repo owner instead of hand-editing a tool-owned file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 --------- Co-authored-by: Claude <noreply@anthropic.com>
Dependabot split these into two separate PRs (#114, #116), but @vitest/coverage-v8 peer-depends on an exact matching vitest version — bumping either alone breaks `npm ci` with an ERESOLVE conflict. Bumped both together; full suite (1313 tests) and `vitest run --coverage` both pass unchanged. Supersedes #114 and #116. Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 Co-authored-by: Claude <noreply@anthropic.com>
…#127) * Backend cleanup: fix data-loss race, non-atomic writes, per-request Engine rebuild Found by an internal audit of src-tauri/. Each fix is independent and low-risk; grouped in one commit since they're all small. - secrets_vault.rs: add a write_lock serializing the read-modify-write cycle on secrets.enc. Two concurrent secret_vault_set/_delete calls (plausible from JS via Promise.all) could both read the same entries then have the later write clobber the earlier one — silent data loss for 2FA/JWT secrets. Mirrors the Mutex pattern artifact_installer.rs already uses for its own index.json. - artifact_installer.rs: - write_index now writes atomically (tmp + rename), matching the pattern already used for service binaries. A crash mid-write previously could corrupt index.json for every installed plugin/service at once. - Plugin bundle writes (stage_install_plugin) are now atomic too, for the same reason. - artifact_installer_list now holds the same InstalledIndex mutex install/uninstall already take, so a list() call can't observe a mid-operation state. - Downloads (plugin bundle / service binary) are now capped at 512MB and checked incrementally while streaming, not just via a Content-Length header a compromised/misconfigured host could lie about or omit — previously an unbounded body could OOM the app before the checksum check ever ran. - mockserver.rs: the rhai Engine (6 limits configured on construction) was rebuilt on every single script-mode request instead of once. Cached in a shared `ENGINE: OnceLock<Engine>` (needs rhai's "sync" feature for Send + Sync); only the per-call Scope is still built fresh, which is where actual request state lives. - mcp_bridge.rs: token comparison in check_auth is now constant-time (defense-in-depth on a trust-boundary check, even though real-world risk is low — loopback-only, random UUIDv4 token, no remote timing attacker). - service_host.rs / mcp_bridge.rs: documented (didn't merge) the two independently-defined but identical CALL_TIMEOUT constants, so a future change to one doesn't silently drift from the other. Added tests for the atomic write_index, the download size cap (both the Content-Length-header and incremental-streaming rejection paths), and constant_time_eq. Rust test count: 100 -> 105 (+9 integration tests unchanged). Full suite passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 * Frontend cleanup: memoize context values, fix unmemoized filter/sort in PortsView Found by an internal audit of src/. - FeatureContext.tsx: the provider value was a fresh object literal (with 6 non-useCallback'd functions) on every render. FeatureProvider sits near the top of the provider tree and is read by every sidebar row and route guard, so any state change here — toggling one tool, reordering, favoriting — re-rendered every consumer regardless of whether their own slice of features/favorites actually changed. Wrapped the functions in useCallback and the provider value in useMemo, matching the pattern AppConfigContext.tsx already used. - UpdateContext.tsx: same shape of bug (unmemoized provider value), smaller blast radius (header badge + update dialog). All the individual fields were already stable (useCallback'd functions, primitive state), so this only needed wrapping the final value in useMemo. - NetworkTools.tsx (PortsView): filtering/grouping/sorting (scanned, favRows, groups, sortedGroups, sortedSockets) recomputed inline in the render body on every render, including ones triggered by unrelated state like typing in the "add favorite port" input. Wrapped each stage in useMemo keyed on its actual inputs. Full test suite (1333 tests) and build passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 * Test suite: dedupe the 3x-repeated provider-render helper in one file Found by an internal audit of the test suite (115 files, 1333 tests). SettingsExtensionInstaller.test.tsx repeated the same resetModules() + dynamic-import + render(<LocaleProvider><ExtensionUpdateProvider>...) block 3 times in one file, each needing a fresh module epoch so it couldn't just call the existing renderInTauri() helper. Replaced all three with one parametrized renderInstaller({ tauri, beforeRender }) helper — beforeRender runs after the fresh imports resolve but before render(), which is what let the pendingInstall test enqueue into the same module epoch the component will read from. Also empirically tried the vitest.config.ts `pool: 'vmThreads'` optimization the audit flagged as the single biggest lever for the suite's ~65s/40s jsdom-recreation overhead — reverted immediately: it breaks WebCrypto (aesGcm.ts's SubtleCrypto calls) in 6 test files under vitest's vm-module environment. Not adopting it; noting this so it isn't re-suggested without re-testing. `isolate: false` (the other option raised) wasn't tried at all — the audit's own reasoning for why it's unsafe (shared module-level singletons, window.__TAURI_INTERNALS__ mutation across files in the same worker) is sound on inspection alone. Full suite (1333 tests) still passing; installer test file re-run 3x to confirm the refactor didn't introduce flakiness. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 * Address pullfrog review: atomic vault write, lock-ordering gap, dep placement - secrets_vault.rs: write_entries now writes secrets.enc atomically (tmp + rename), same pattern as artifact_installer.rs's write_index. This was the one file the prior commit's "fix data-loss races" pass missed — a crash mid-write left a truncated blob holding 2FA seeds and bearer tokens that read_entries could only report as unrecoverable. - secrets_vault.rs: write_lock is now acquired BEFORE key_for() in every command, not after. key_for() can create the encryption key on first use; under the exact concurrency scenario write_lock exists to serialize (two near-simultaneous first-run writes), the old ordering let each call resolve the key independently before the lock closed around the read-modify-write, so they could end up encrypting under different keys and leaving secrets.enc inconsistent with the cached/ keychain key. Same class of bug at an earlier step. - Cargo.toml: futures-util (added for a test-only mock server) moved to [dev-dependencies] — it's not used by any production code path, unlike axum/uuid which are (mockserver.rs/service_host.rs/ mcp_bridge.rs run real axum servers, not just tests). - FeatureContext.tsx: corrected a comment overstating what the memoization fixes. A real features/favorites change still re-renders every consumer (React context has no per-field subscription); what the memo actually fixes is FeatureProvider re-rendering (with a brand-new value object) whenever something unrelated re-renders its parent, with no real state change at all. Rust tests: 105 passing (unchanged count; these are behavior fixes to already-covered code paths, not new pure functions to unit test in isolation — the lock-ordering/atomicity guarantees need an AppHandle to exercise end-to-end, which this module's test harness deliberately avoids, same tradeoff already documented for its crypto-only tests). Full frontend suite (1333 tests) and build still passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 --------- Co-authored-by: Claude <noreply@anthropic.com>
#128) Documents the four tools' migration to developer-desktop-util-plugin, that connection data isn't carried over automatically, and links to the plugins page to reinstall.
… main (#129) main is strictly ahead of the canary-v1.1.0-arch1 build; the Platform/Plugin architecture is confirmed stable, so the parallel test channel is no longer needed.
Direct git tag pushes are blocked for this session's credentials; mirrors developer-desktop-util-plugin's release.yml fallback.
windows-latest defaults to pwsh, not bash; the "Resolve release tag" step from #130 needed an explicit shell: bash to actually export RELEASE_TAG on Windows.
…orm succeeds (#132) build (matrix) never touches tags/releases; publish (needs: build) only runs — and creates the tag/release — once every platform has succeeded. Merged to main to test-trigger a real release run; pre-merge main backed up at backup/main-before-build-then-tag.
… format Real v0.9.1-test1 test run revealed the bug a flat readdirSync couldn't catch: Tauri's own bundle output nests one subdirectory per installer format (bundle/dmg/*.dmg, bundle/macos/*.app.tar.gz, bundle/appimage/*, bundle/deb/*, bundle/nsis/*), not a flat directory of files. The script only looked at immediate children of downloaded/bundle-<platform>/, which are those format directory NAMES, not files — so classify() never matched anything and it threw 'No signed updater artifacts found', after the tag and GitHub Release had already been created (this job's steps run in sequence — see the follow-up commit for fixing that ordering too). Switched to a recursive walk (walkFiles) so the format subdirectory depth doesn't matter. Also discovered along the way: plain `tauri build` only signs the three genuine updater-capable formats (app.tar.gz/AppImage/nsis) — unlike tauri-action's v0.9.0 output, deb has no .sig here. That's handled gracefully already (no .sig sibling = skipped, same as dmg), just documented in a comment so it's not mistaken for a new bug later. Re-verified via dry run against the exact nested structure observed in the real (failed) v0.9.1-test1 run's logs.
Fallout from the same v0.9.1-test1 test run: the tag+release were created (published, not draft) as the FIRST step of the publish job, with the updater-manifest/checksum/SBOM/attestation steps still to come. When the manifest step failed right after, the result was a real published release with installers but no latest.json — visible to the in-app updater, exactly the kind of broken release this whole redesign was meant to prevent, just moved one level down (from 'a platform failed' to 'a post-tag step failed'). Fix: create the release as --draft, and only flip it to published in a final step that runs after every other step in this job has already succeeded. A failure anywhere before that leaves an unfinished draft instead of a broken live release.
v0.9.1-test2's run hit this immediately after #133 made the release a draft until every publish step succeeds: GitHub doesn't create the actual git tag ref until a release is published, and GET /repos/{owner}/{repo}/releases/tags/{tag} requires that ref to already exist — so it 404s for a draft, even though the draft release object itself exists and has all the uploaded assets. Switched to GET /repos/{owner}/{repo}/releases (includes drafts) and filter by tag_name in JS instead of asking the API to resolve a tag ref that doesn't exist yet. Verified with a dry run against a fake multi-release response (an unrelated real published release plus the in-progress draft) to confirm it picks out the right one and the signatures still match v0.9.0's real latest.json exactly.
…Buffer v0.9.1-test3's run failed with ENOBUFS: this repo already has 90+ releases, each with a full asset array, and the unfiltered `gh api repos/.../releases --paginate` response exceeded execFileSync's default 1MB maxBuffer before JSON.parse ever ran. Filter with --jq instead of buffering everything into Node and filtering in JS — it runs per-page under --paginate, so only the one matching release (if any) ever reaches stdout. Also raises maxBuffer as a backstop. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2
release.yml, build-cache.yml, and coverage.yml each repeated the same five steps (setup-node, install rust, rust-cache, apt install for Tauri's Linux deps, npm ci) with identical bodies — three places to keep pinned action SHAs and the apt package list in sync. Pulled them into .github/actions/setup-tauri-env, parameterized by rust-target/components and the cache shared-key/save-if each caller already varied. Also adds --prefer-offline --no-audit --no-fund to every `npm ci` call (tests.yml included) — skips npm's registry audit/funding network calls on every run, using only what's already in the npm cache. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2
package-lock.json's only hasInstallScript entry is fsevents, an optional macOS-only file watcher npm run dev pulls in — irrelevant to CI, which only builds/lints/tests. Verified npm ci --ignore-scripts still installs cleanly and tsc --noEmit still passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2
Redo of dependabot PR #115 (which went stale after #123 merged sidecar-placeholder/rustls fixes). rmcp 3.x renamed several public types used by the MCP stdio server: Content -> ContentBlock, ServerInfo -> ServerConfig, and ServerHandler::call_tool now returns CallToolResponse instead of CallToolResult (CallToolResult still implements Into<CallToolResponse>). Updated devtool-mcp-server.rs accordingly; verified with cargo check --all-targets and cargo test (109 tests passing). Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2 Co-authored-by: Claude <noreply@anthropic.com>
… updates (#120) Bumps the minor-and-patch group with 2 updates in the /src-tauri directory: [uuid](https://github.com/uuid-rs/uuid) and [rhai](https://github.com/rhaiscript/rhai). Updates `uuid` from 1.26.0 to 1.26.1 - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](uuid-rs/uuid@v1.26.0...v1.26.1) Updates `rhai` from 1.26.0 to 1.26.1 - [Release notes](https://github.com/rhaiscript/rhai/releases) - [Changelog](https://github.com/rhaiscript/rhai/blob/main/CHANGELOG.md) - [Commits](rhaiscript/rhai@v1.26.0...v1.26.1) --- updated-dependencies: - dependency-name: rhai dependency-version: 1.26.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: uuid dependency-version: 1.26.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… updates (#121) Bumps the minor-and-patch group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [js-yaml](https://github.com/nodeca/js-yaml) | `5.4.1` | `5.4.2` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.41.0` | `1.45.0` | | [react](https://github.com/react/react/tree/HEAD/packages/react) | `19.2.8` | `19.3.0` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.2.18` | `19.3.0` | | [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom) | `19.2.8` | `19.3.0` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.2.7` | `19.3.0` | | [tailwind-merge](https://github.com/dcastil/tailwind-merge/tree/HEAD/packages/tailwind-merge) | `3.6.0` | `3.7.0` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.1` | `26.5.1` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.2.2` | `8.3.0` | Updates `js-yaml` from 5.4.1 to 5.4.2 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](nodeca/js-yaml@5.4.1...5.4.2) Updates `lucide-react` from 1.41.0 to 1.45.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.45.0/packages/lucide-react) Updates `react` from 19.2.8 to 19.3.0 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.3.0/packages/react) Updates `@types/react` from 19.2.18 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 19.2.8 to 19.3.0 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.3.0/packages/react-dom) Updates `@types/react-dom` from 19.2.7 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `tailwind-merge` from 3.6.0 to 3.7.0 - [Release notes](https://github.com/dcastil/tailwind-merge/releases) - [Commits](https://github.com/dcastil/tailwind-merge/commits/tailwind-merge@3.7.0/packages/tailwind-merge) Updates `@types/node` from 26.4.1 to 26.5.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/react` from 19.2.18 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `@types/react-dom` from 19.2.7 to 19.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `vite` from 8.2.2 to 8.3.0 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/create-vite@8.3.0/packages/vite) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.5.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@types/react" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@types/react" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@types/react-dom" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@types/react-dom" dependency-version: 19.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: js-yaml dependency-version: 5.4.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: lucide-react dependency-version: 1.45.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: react dependency-version: 19.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: react-dom dependency-version: 19.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: tailwind-merge dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: vite dependency-version: 8.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from 4.37.9 to 4.38.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@cdf488f...b96794f) --- updated-dependencies: - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.38.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](actions/checkout@v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.87.5 to 2.87.12. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](taiki-e/install-action@5bf6ce0...3f74d7c) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.87.12 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…oded The user reported the in-app updater showing "Invalid encoding in minisign data" on v0.9.1-test4. Downloaded that release's real DevTool.app.tar.gz.sig asset and decoded it: the .sig file's raw content already IS base64 of the minisign block (Tauri's own signer writes it that way) — the script was base64-encoding it a second time, producing a signature field that needs TWO decodes instead of one. Verified against v0.9.0 (the last release actually built by tauri-action, confirmed working for real users): its signature field decodes to the readable minisign block in exactly ONE step. Fixed by reading the .sig file as UTF-8 text directly instead of re-encoding it to base64. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2
…base64-signature Fix build-updater-manifest.mjs: signature field was double base64-encoded
…ing) The "Verify SECURITY.md claims" job in security.yml has been failing on every run for days: SECURITY.md documents that every action in .github/workflows is pinned to an immutable commit SHA, but pullfrog.yml (added by the Pullfrog integration) used floating tags — actions/checkout@v7 and pullfrog/pullfrog@v0 — either of which could be repointed by its owner to run different code inside a workflow with contents:read + id-token:write. Pinned both to their current commit SHAs: actions/checkout's v7.0.1 SHA (already used elsewhere in this repo), and pullfrog/pullfrog's v0 tag SHA (verified independently via `git ls-remote --tags`, not just the GitHub web UI, since this repo is outside this session's GitHub tool scope). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2
Pin pullfrog.yml's actions to commit SHAs
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
|
|
Closing — Generated by Claude Code Generated by Claude Code |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## arch/platform-plugin #140 +/- ##
=========================================================
+ Coverage 29.49% 64.74% +35.24%
=========================================================
Files 12 13 +1
Lines 5465 3344 -2121
=========================================================
+ Hits 1612 2165 +553
+ Misses 3853 1179 -2674
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Important
Two things to fix before this lands: the .deb updater entry is dropped from latest.json (silently breaking auto-update for deb-installed users), and enabling Background MCP bridge now disables MCP for the four moved plugins even while their own route is open.
Reviewed changes
- Four tools extracted to optional plugins — removes all frontend/backend/plugin code for redis-client, rabbit-client, container-manager, kafka-explorer (plus
testing/fixtures and the canary workflow); source moves toDianaSensei/developer-desktop-util-plugin. - Dynamic MCP tool registry —
mcp_bridge.rsstores registered tool defs keyed by plugin id and servesGET /tools;devtool-mcp-server.rsdrops its compiled-in catalogue and fetches the registered set perlist_tools(). - Per-tool catalogue extraction —
mcpMetaTools.ts,mcpUtilityTools.ts,apiclient/mcpTools.ts,mockserver/mcpTools.ts; each bridge registers itself on mount. - Tier-B install-from-URL for plugins —
RemotePluginManifest.service/RemoteServiceDescriptor,ALLOWED_SERVICESgainsdevtool-svc-kafka, new test-onlyBUNDLED_SERVICES,externalBinnarrowed to mcp-server + echo. - Deep link install flow —
tauri-plugin-deep-link+ single-instance,deepLink.ts/pendingInstall.ts,SettingsExtensionInstallerqueue drain,Settingsopens a section vialocation.state. - Release pipeline rebuild —
release.ymlsplit intobuild+publish, updater manifest reconstructed byscripts/build-updater-manifest.mjs, sharedsetup-tauri-envaction, canary retired. - Hardening — tmp+rename atomic index/bundle writes, old-version cleanup,
download_cappedsize cap, constant-time token compare, context memoization,rmcp1→3.3.
Findings are anchored inline. Verified locally: npx tsc --noEmit passes and the changed test files (deepLink, pendingInstall, Settings.routing, platform/services) pass.
Important
Pullfrog covered this run's model usage. DeepSeek Flash is fast and cheap — expect lighter work than a frontier model. This model allowance is temporary and separate from your Pullfrog plan. Connect a model-provider subscription or API key →
DeepSeek Flash (model usage covered by Pullfrog) | 𝕏
| ${{ steps.bundle-dir.outputs.dir }}/**/*.app.tar.gz.sig | ||
| ${{ steps.bundle-dir.outputs.dir }}/**/*.AppImage | ||
| ${{ steps.bundle-dir.outputs.dir }}/**/*.AppImage.sig | ||
| ${{ steps.bundle-dir.outputs.dir }}/**/*.deb |
There was a problem hiding this comment.
The upload list covers .app.tar.gz.sig, .AppImage.sig, and .exe.sig but not .deb.sig. build-updater-manifest.mjs resolves a .sig sibling for every classified artifact and silently continues when it is missing, so the linux-x86_64-deb entry never makes it into latest.json (and .deb.sig never reaches the release). A deb-installed client then falls back to the bare linux-x86_64 key — which the AppImage owns — and install_deb rejects the bytes with InvalidUpdaterFormat.
Technical details
# Dropped deb updater entry
## Affected sites
- `.github/workflows/release.yml:141` — comment claims "plus each one's .sig", but `.deb.sig` is absent from the list.
- `.github/workflows/release.yml:153` — only `**/*.deb` is uploaded.
- `scripts/build-updater-manifest.mjs:156-162` — a missing `.sig` sibling makes the `.deb` `continue`, so `platforms["linux-x86_64-deb"]` is never set (the format rule at `:69` / `:168` expects it).
- `src-tauri/tauri.conf.json:44-50` — `deb` is a configured `bundle.targets` entry, so `tauri build` does sign it when `createUpdaterArtifacts` + the signing key are present.
## Required outcome
- `.deb.sig` is uploaded from the build job, lands in `downloaded/bundle-ubuntu-22.04/`, and yields a `linux-x86_64-deb` entry in `latest.json` (bare `linux-x86_64` stays with the AppImage).
## Suggested approach
- Add the `${{ steps.bundle-dir.outputs.dir }}/**/*.deb.sig` line after `**/*.deb`.
- Optional: make `build-updater-manifest.mjs` throw (rather than silently skip) when a non-primary format the script claims to emit has no `.sig`, so this class of omission fails the release instead of shipping a partially-broken manifest.
## Open questions for the human
- Was dropping the deb updater entry intentional (e.g. deb installs are not expected to auto-update), or an oversight in the build/publish split?| ${{ steps.bundle-dir.outputs.dir }}/**/*.deb | |
| ${{ steps.bundle-dir.outputs.dir }}/**/*.deb | |
| ${{ steps.bundle-dir.outputs.dir }}/**/*.deb.sig |
| // have a background bridge here: an externally-installed plugin only mounts | ||
| // when its own route is visited, so there's no compile-time runtime context | ||
| // to share while it's off-screen. Their MCP tool definitions still exist in | ||
| // devtool-mcp-server.rs (bundled with those plugins, registered dynamically | ||
| // when installed); calls to them simply go unanswered unless the plugin's | ||
| // own route is open. |
There was a problem hiding this comment.
This comment says calls go unanswered "unless the plugin's own route is open", but with Background MCP bridge enabled they go unanswered even on-route: the four external plugins gate their bridge on usePluginMcpBridgeActive(sdk), which still returns ... && !backgroundEnabled (src/platform/services.ts:53-58). Since this file no longer mounts their bridges at the app root, background mode now turns their MCP access off instead of lifting the on-screen requirement.
| // have a background bridge here: an externally-installed plugin only mounts | ||
| // when its own route is visited, so there's no compile-time runtime context | ||
| // to share while it's off-screen. Their MCP tool definitions still exist in | ||
| // devtool-mcp-server.rs (bundled with those plugins, registered dynamically | ||
| // when installed); calls to them simply go unanswered unless the plugin's | ||
| // own route is open. |
There was a problem hiding this comment.
Background mode must not suppress a plugin's on-screen bridge once the app-root fallback is gone.
Technical details
# Background MCP bridge disables external-plugin MCP
## Affected sites
- `src/components/McpBackgroundBridge.tsx:37-46` — the four `useRedisMcpBridge`/`useKafkaMcpBridge`/`useRabbitMcpBridge`/`useContainerMcpBridge` mounts were removed; only API Client and Mock Server remain.
- `src/platform/services.ts:53-58` — `usePluginMcpBridgeActive` returns `isMcpTool && toolEnabled && !backgroundEnabled`; the `!backgroundEnabled` term assumed the app-root bridge would answer in its place, which is no longer true for plugins.
- External plugins — verified on `developer-desktop-util-plugin` branches `app/<id>/main`: each UI calls `usePluginMcpBridgeActive(sdk)` (e.g. `plugins/redis-client/ui/RedisClient.tsx:40,45`) and each `ui/mcpBridge.ts` early-returns on `!enabled` before `mcp_register_tools`/`mcp:call` (e.g. redis `ui/mcpBridge.ts:136,145,149`). The SDK shim `shared/platform.ts` forwards to `window.__DEVTOOL_VENDOR__.platform.usePluginMcpBridgeActive` (`src/main.tsx:47,65`), so this is the app's own expression.
- `src/lib/i18n.ts:294-296` (`settings.mcp.backgroundDescription`) — still promises Redis Client and Kafka Explorer answer in background mode.
- `src/components/mcpMetaTools.ts` — `devtool_mcp_set_background` ("every enabled tool's MCP tools answer regardless of which tool is on screen") and `devtool_mcp_status` still describe the old behavior; only the prose in `devtool-mcp-server.rs` was updated.
## Required outcome
- With background mode ON, an external plugin whose route is mounted still registers its tools and answers `mcp:call`; and `list_tools`/the MCP meta descriptions stop asserting a guarantee the app no longer provides.
## Suggested approach
- Drop the `!backgroundEnabled` term for externally-installed plugins (the app-root bridge no longer compensates for them), or keep a root-level mount for them. Then update `settings.mcp.backgroundDescription` and the `devtool_mcp_*` descriptions to match.
- Add a regression test: `src/platform/services.test.tsx` currently only asserts `usePluginMcpBridgeActive(apiClient)` with background off, so a plugin id with background on is uncovered.| **If you used any of these tools before upgrading:** | ||
|
|
||
| - After updating, they will no longer appear in the sidebar — this is expected, not a bug. | ||
| - Install the ones you need from **Settings → Extensions**, or from the plugins page: |
There was a problem hiding this comment.
The Settings section renders settings.plugins.title (src/lib/i18n.ts:201), which is Plugins, not "Extensions" — the upgrade instructions send users to a nav label that doesn't exist. Same wording appears in the PR description.
| - Install the ones you need from **Settings → Extensions**, or from the plugins page: | |
| - Install the ones you need from **Settings → Plugins**, or from the plugins page: |
| │ ├── human/ # Human contributor guides | ||
| │ └── design/DESIGN-SYSTEM.md | ||
| ├── testing/rabbitmq/ # RabbitMQ integration test harness (Python + Docker) | ||
| ├── testing/kafka/ # Kafka integration test harness (Python + Docker) |
There was a problem hiding this comment.
This now points at testing/kafka/, but the whole testing/ directory is deleted by this PR (no testing/ at head), so the repo tree describes a path that no longer exists.
| # testing/rabbitmq (and its spring-rpc fixture) moved to | ||
| # developer-desktop-util-plugin along with the RabbitMQ plugin — see | ||
| # docs/decisions/architecture/platform-plugin-architecture.md. Configure | ||
| # dependabot there instead. |
There was a problem hiding this comment.
The comment above says to configure Dependabot in the plugin repo, but the pip entry just below still targets /testing/kafka — a directory this PR deletes. Dependabot will fail to find that manifest on each run (and the RabbitMQ maven entry was removed, so this is the one leftover).



Summary
Moves Redis Client, RabbitMQ Client, Container Manager, and Kafka Explorer from built-in tools to optional installable plugins, completing the platform/plugin architecture migration (Phase 2). These tools now run as separate Tier-B sidecar processes managed by the plugin system, with their frontend code removed from the main app bundle.
Key Changes
Removed from main app:
src/components/tools/{redis,rabbit,container,kafka}/)src-tauri/src/{redis_tool,rabbit,container_tool,kafka}.rs)src/plugins/{redis-client,rabbit-client,container-manager,kafka-explorer}/).github/workflows/release-canary.yml)Moved to plugins (via developer-desktop-util-plugin):
src-tauri/src/bin/devtool-svc-{redis,rabbit,container}.rs)mcp_register_toolsmcpTools.tsper tool)Platform/MCP infrastructure updates:
devtool-mcp-server.rsnow fetches tool catalogue dynamically fromGET /toolsbridge endpoint instead of using compiled-in listmcp_bridge.rsextended to support tool registration and dynamic tool discoveryBuild & CI/CD:
.github/actions/setup-tauri-env/)Documentation:
docs/decisions/architecture/optional-broker-plugins.mdexplaining the migrationImplementation Details
service-data/directoryhttps://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2