Skip to content

WIP: Dynamic MCP toggle meta tools + rename/edit for Spaces/FS/Workspaces - #154

Closed
crimsonsunset wants to merge 48 commits into
mcpmux:feat/workspace-root-routingfrom
crimsonsunset:feat/dynamic-mcp-toggle-meta-tools
Closed

WIP: Dynamic MCP toggle meta tools + rename/edit for Spaces/FS/Workspaces#154
crimsonsunset wants to merge 48 commits into
mcpmux:feat/workspace-root-routingfrom
crimsonsunset:feat/dynamic-mcp-toggle-meta-tools

Conversation

@crimsonsunset

@crimsonsunset crimsonsunset commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Two related slices of UX work on top of PR #151's mcpmux_* namespace and workspace-root routing:

1. Dynamic MCP toggle meta tools (session/workspace enable-disable)

  • SessionOverrideRegistry — in-memory per-session enabled/disabled server sets, composed additively over workspace bindings in FeatureService
  • New meta tools: mcpmux_list_servers, mcpmux_enable_server, mcpmux_disable_server (session default; workspace scope persists via binding writes)
  • Gateway: tools/list_changed on override changes; resolver honors session overrides even when binding resolution denies
  • Desktop UI: Workspaces -> live folder inspector -> Active session overrides panel; Settings -> session-scope approval toggle

2. Rename / edit affordances for Spaces, Feature Sets, and Workspace bindings

  • Spaces: card -> side panel with editable name/icon/description, save + delete (default protected)
  • Feature Sets: General Information section in FeatureSetPanel is now editable (name/description/icon); Starter sets are renamable
  • Workspace bindings: optional friendly label separate from folder path, surfaced on cards / inspector / search
  • Dashboard: stat cards (Servers / FeatureSets / Clients / Workspace) are now buttons routing to their detail pages

Stacks on feat/workspace-root-routing (#151).

What changed

Backend (Rust)

  • SessionOverrideRegistry + composition into FeatureService::get_*_for_grants
  • mcpmux_* server enable/disable meta tools wired through the gateway
  • Per-peer tools/list_changed on override changes
  • New update_space Tauri command + SpaceService::update
  • New label: Option<String> on WorkspaceBinding (migration 016_workspace_binding_label.sql); CRUD updated
  • update_feature_set no longer guards on is_builtin so Starter sets can be renamed

Desktop UI (React)

  • SpacePanel slide-out (mirrors FeatureSetPanel pattern)
  • FeatureSetPanel General Information now editable with separate Save
  • WorkspacesPage BindingForm gains a label input; cards + inspector + search use it
  • WorkspacesPage Active session overrides panel + Settings approval toggle
  • App.tsx dashboard stat cards converted to keyboard-accessible nav buttons

Tests

  • Rust unit tests added for normalize_label, WorkspaceBinding label round-trip, and SpaceService::update
  • ~25 new integration tests in tests/rust/tests/integration/meta_tools.rs

WIP status

  • Manual smoke test of Workspaces overrides panel + Settings approval toggle
  • Confirm gateway/client reconnect behavior after session toggles in a real Cursor session
  • E2E coverage for the new rename/edit panels (currently Rust-only for the rename slice)

How was it tested

  • pnpm validate passes
  • pnpm test:ts -- 174 passed
  • pnpm test:rust -- 592+ passed (includes new meta-tools integration tests + label/space-service unit tests)
  • Local production build installed and launched from /Applications/McpMux.app
  • pnpm dev watch mode verified with rename + dashboard nav changes
  • Full pnpm test (E2E not run on this branch yet)
  • pnpm build exits clean (updater signing requires TAURI_SIGNING_PRIVATE_KEY; app bundle builds successfully)

Checklist

  • My code follows the project's code style
  • I have signed off my commits (git commit -s)
  • I have updated documentation if needed

…lippy

Cherry-picks the two changes from upstream PR mcpmux#152 (mcpmux#152)
since `feat/workspace-root-routing` (PR mcpmux#151, this branch's base) reworked
`dcr.rs` extensively and a direct merge would conflict.

- `validate_redirect_uris` now skips invalid URIs with a warn log and only
  fails registration when zero valid URIs remain. Unblocks Cursor 3.4.20,
  which sends a mixed-validity list (`cursor://`, `https://www.cursor.com`,
  `http://localhost`) in a single DCR request.
- `PrefixCacheService` uses `sort_by_key` for the `created_at` sort so
  `clippy::unnecessary_sort_by` doesn't fail `cargo clippy -D warnings`.

Adds two tests covering the mixed-validity case and the all-invalid case.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
5-phase plan layered on top of PR mcpmux#151's `mcpmux_*` meta-tool surface:
adds `mcpmux_list_servers`, `mcpmux_enable_server`, `mcpmux_disable_server`
with a session-scoped default (in-memory `SessionOverrideRegistry`) and a
workspace-scope opt-in that reuses the existing `WorkspaceBinding` write
path. Resolver stays untouched; composition lives in `FeatureService`.

Closes the gap between PR mcpmux#151's persistent bindings and the ephemeral
"LLM-driven, minimum-context-default" workflow from the jsg-tech-check
homelab plan.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Document how to replace /Applications/McpMux.app with a local build
while preserving user data in Application Support and the keychain.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Phase 1 of dynamic MCP toggling: per-session enable/disable sets wired
through FeatureService list paths, session GC, and composition tests.
Also fixes a DashMap deadlock in SessionRootsRegistry::record_resolution.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Server-level manifest with binding/session status per installed server.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
…e 3)

mcpmux_enable_server and mcpmux_disable_server mutate SessionOverrideRegistry,
audit as session_override when auto-allowed, and notify the calling session.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
…ase 4)

Persist server-all FeatureSets on workspace bindings with approval;
session list_changed only fires for session scope.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Expose session overrides in the Workspaces inspector with clear controls,
wire list/clear Tauri commands, and add a settings gate for session-scope
enable/disable approval.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Document the mcpmux_* manifest workflow in README, reconcile the planning
doc for validation/PR gate, and fix stale DCR and notification dedup tests.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
…dings

Expose update_space and editable panels for Spaces and Feature Sets.
Add optional workspace binding labels with migration 016 so folders
can have friendly display names separate from their paths.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Add Rust unit tests for workspace binding labels, label normalization,
and SpaceService::update.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Restructure to cover both flows: pnpm dev for live HMR + Rust
auto-recompile, and build + swap into /Applications for an installed
app. Add Keychain prompt explainer and shared-data table.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
@crimsonsunset crimsonsunset changed the title WIP: Dynamic MCP toggle meta tools (session/workspace enable/disable) WIP: Dynamic MCP toggle meta tools + rename/edit for Spaces/FS/Workspaces May 19, 2026
RoutingService::call_tool used resolve_feature_sets without session
context, so session-enabled servers passed list_servers but failed at
call time. Pass session_id and use get_tools_for_grants instead.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Tools with output schemas (e.g. Google Workspace MCP) return structuredContent
alongside text content. The gateway was dropping this field when proxying
CallToolResult, causing clients to reject otherwise successful tool calls.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Documents UI-assisted multi-account installs (Option 2) with optional
first-class instances deferred to a later phase.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- Made normalize_server_id/normalize_alias public — clone_server must reuse the same ID rules as user-space imports
- Registered server_clone in commands/mod.rs — required for lib.rs command wiring
- Added suggest_clone_suffix/is_clone_id_available on ServerAppService — straightforward helpers for Phase 2 UI collision checks

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- Post-clone configure uses ServersPage inline config modal — ConfigEditorModal is for space JSON, not per-server credentials
- cloned_from typed via local ServerViewModelWithClone extension — kept Phase 2 scope off types/registry.ts
- Clone badge replaces Manual badge when clonedFrom is set — lineage is more useful than generic Manual label

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- Added InstalledServerRepository to MetaToolContext so mcpmux_list_servers
  can resolve cloned_from without a core type change (field already on InstalledServer)
- Omitted cloned_from from non-clone rows rather than emitting null
- Skipped jsg-tech-check migration doc (out of repo per orchestrator)
- Updated planning doc status/checkboxes to match Phases 1–3 completion

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- PrefixCache reads alias from cached_definition before registry lookup so clone suffix aliases resolve at connect time
- Uninstall-source UX: three-action modal (cancel / source-only / uninstall all) via UninstallSourceWithClonesDialog
- list_clone_dependents filters list_for_space by cloned_from — no new repo method needed

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- Commit fmt-only diffs from pnpm validate cargo fmt --all

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Replace Electron-style -webkit-app-region with Tauri 2 data-tauri-drag-region
and enable acceptFirstMouse for drag on unfocused macOS windows.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Also reconcile May 23 closeout notes in meta-tools and account-clones
planning docs.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Add My Servers header controls to expand or collapse connected rows,
and show Disable alongside Retry when a server is stuck in error state.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
…itives

Add DropdownMenu, ChipButton, SearchField, and HoverTooltip to @mcpmux/ui.
Refactor My Servers with transport/status filters, tool-aware search,
row-click expand, inline gateway status, and Add Server discover/custom menu.

Signed-off-by: Joe Sangiorgio <jsangio1@gmail.com>
Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Show installed/connected/disabled/error counts beside My Servers, move filter
clear-all outside the popover, and flip HoverTooltip placement from viewport space.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Lets users distinguish multiple installs of the same MCP (e.g. two Google
Calendars) with a friendly label that survives user-config sync, without
changing server_id, alias, or tool prefixes.

- Migration 018 + InstalledServer.display_name_override; display_name()
  precedence: override -> server_name -> server_id tail.
- ServerAppService gains set_display_name_override and accepts an optional
  override on update_config and clone_server. New set_server_display_name
  Tauri command; save_server_inputs and clone_server extended.
- Frontend resolveInstalledDisplayName helper used in every merge path;
  Configure modal exposes a Display name field and ServerActionMenu always
  shows Configure (Settings when no inputs); CloneAccountModal adds an
  optional Display name input.
- mcpmux_list_servers reports the effective installed display name so
  agents see the user's label instead of the catalog name.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Auto-connect OAuth servers on gateway boot when mux already has tokens
(stdio OAuth always attempts connect; HTTP only prompts Connect when
unauthenticated). Bootstrap oauth_connected after credential-based connect.

Pin the My Servers search/actions header while scrolling the server list.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Planning doc for `mcpmux_pin_this_session` + `mcpmux_clear_session_pin`
- ephemeral session-scope tool-list pin with optional workspace persistence.
Revisits the deferred tool-level-granularity decision from
dynamic-mcp-toggle-meta-tools in light of the 240-tool context bloat
exposed by the Google Workspace clone case.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Define phased plan for serving the admin UI over HTTP behind CF Access,
separate from the MCP gateway tunnel.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Use a leftmost Enabled switch on server rows for clearer state and fewer action buttons.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Define search → schema → invoke as the primary agent path with a hard cut
away from exposing backend tools in tools/list. Defer session pin in favor
of this approach.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- routing_service as Option in MetaToolContext — existing meta_tools tests stay lightweight without PoolService wiring
- Updated service_container.rs and meta_tools.rs fixtures — mechanical compile wiring for build_default_registry signature change
- mcpmux_invoke_tool is_read (not write) — backend invoke is not a gateway state mutation and must not require approval
- get_advertised_tools_for_grants returns empty until Phase C surfaced flag — hard cut keeps tools/list meta-only now

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- DEFAULT_MAX_ROWS=50 and DEFAULT_MAX_BYTES=65536 — balances context savings with usable samples for smart default truncation
- pub mod invoke — external integration tests need public access to shaping helpers
- Hoist returned/total/truncated to parent object for nested list keys — keeps truncation metadata visible without extra nesting

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Autonomous decisions:
- Keep session-enable all-tools expansion only when no FeatureSet is bound — preserves Phase A bootstrap path while member filter is authoritative once bound
- Add migration 019 surfaced column on feature_set_members — matches existing ALTER TABLE pattern, default 0
- Surface toggle UI only on selected tools in FeatureSetPanel — surfaced without include is meaningless

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Rebind an existing workspace root via update instead of insert, avoiding
UNIQUE constraint failures when Starter or UI bindings already exist.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Single-session checklist with copy-paste prompts for Phases A–C validation.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Mark Phases A–C implementation status in the planning doc and record
manual QA results for runbook sections 0–3.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Remove default row/byte shaping when filter is omitted so backends
return payloads as-is. Explicit filter still bounds JSON arrays and
plain text with returned/total/truncated metadata.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Reconcile invoke planning docs with manual QA through section 5 and
opt-in filter truncation at 433e7bd.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Add InvokeToolBackend for pluggable routing, expand filter unit tests,
and prove mcpmux_invoke_tool applies filters via CannedInvokeBackend.
Reconcile Phase B planning docs after manual github_list_issues pass.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Probe workspace roots before call_tool routing so surfaced tools resolve
the same binding ACL as tools/list. Document checkbox vs Surface in the
FeatureSet editor and record Phase C manual QA pass.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Record test 11 end-to-end pass and mark the runbook ready to ship.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Align planning doc with Ship result, surfaced one-hop exception, and
completed Phase A–C checklist; refresh QA runbook red-flag wording.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
list_all_tools now exposes invokable vs server_available with counts;
get_tool_schema accepts array and JSON-encoded array forms and reports
missing names; invoke filter applies max_bytes to JSON arrays without max_rows.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Record post-85113e7 QA results for §3, §6, §9, and §10 as Ship.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Document crimsonsunset fork canonical branch (feat/meta-gateway-invoke),
upstream PR policy (mcpmux#152/mcpmux#154 keep, mcpmux#155 megapr closed), and umbrella
warm-pool/session-readiness planning.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Delete docs/planning/* and run-from-source-macos.md now archived in
jsg-tech-check so PR mcpmux#154 stays free of homelab-specific content.

Signed-off-by: crimsonsunset <jsangio1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant