Skip to content

feat(mcp): multi-server lifecycle — enable/disable + error visibility (#3196)#3339

Merged
senamakel merged 15 commits into
tinyhumansai:mainfrom
CodeGhost21:issue/3196-add-support-for-multiple-mcp-servers
Jun 4, 2026
Merged

feat(mcp): multi-server lifecycle — enable/disable + error visibility (#3196)#3339
senamakel merged 15 commits into
tinyhumansai:mainfrom
CodeGhost21:issue/3196-add-support-for-multiple-mcp-servers

Conversation

@CodeGhost21
Copy link
Copy Markdown
Contributor

@CodeGhost21 CodeGhost21 commented Jun 4, 2026

Summary

  • Adds an enabled lifecycle to installed MCP servers (additive SQLite migration; defaults true so existing rows keep auto-connecting).
  • Surfaces per-server connection failures via a new process-global LAST_ERRORS map and a ServerStatus::Disabled variant; all_status priority is Disabled > Connected > Error > Disconnected.
  • New openhuman.mcp_clients_set_enabled RPC + boot-time skip for disabled servers; mcp_clients_connect returns a clear error when called on a disabled install.
  • Frontend gains a real Enable/Disable toggle in the MCP server detail, a quieter disabled badge style, and setEnabled on mcpClientsApi. Connect is hidden when a server is disabled.
  • Multi-server regression tests cover name-collision safety, per-server routing, failure isolation, and disabled enforcement; i18n added across all 14 locales.

Problem

The MCP server model already supported multiple installs (UUID per InstalledServer, namespaced tool IDs), but three operational gaps prevented multi-server workflows from being usable:

  1. No lifecycle for enable/disable — the only way to silence a server was to uninstall it, which lost env values.
  2. ServerStatus::Error existed in the enum but no code path populated it; failed connects rendered as plain disconnected.
  3. No regression coverage for the two real multi-server risks: tool-name collision and failure isolation.

Issue #3196 calls these out directly in the acceptance criteria.

Solution

Rust core (src/openhuman/mcp_registry/)

  • types.rsInstalledServer.enabled: bool with #[serde(default = "default_enabled")]. New ServerStatus::Disabled variant.
  • store.rs — additive ALTER TABLE mcp_servers ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1 guarded by the same PRAGMA-driven existence check used for transport / deployment_url. CRUD helpers update_enabled + update_enabled_conn.
  • connections.rs — process-global LAST_ERRORS: OnceLock<RwLock<HashMap<String, String>>> populated by a wrapper around connect (connect_inner is the renamed original body). all_status is rewritten with the new priority logic and reads the errors snapshot for the Error arm.
  • boot.rsspawn_installed_servers skips !enabled rows with an info log; healthy peers still connect even if a sibling fails.
  • ops.rsmcp_clients_set_enabled persists the flip, on false calls disconnect + clear_last_error + publishes DomainEvent::McpServerDisconnected { reason: "disabled" }. mcp_clients_connect rejects disabled servers with a clear error.
  • schemas.rsset_enabled schema + handler wired into the controller registry (no core/dispatch.rs branch added).

Tests

  • tests/mcp_registry_e2e.rs — 10 hermetic tests against the test-mcp-stub binary covering enabled round-trip, LAST_ERRORS record/clear, status priority, boot skip, and set_enabled semantics.
  • tests/mcp_registry_multi_server.rs — new file with 4 regression scenarios: name-collision safety, routing by server_id, failure isolation (bad subprocess doesn't block a healthy peer), and disabled enforcement.
  • tests/json_rpc_e2e.rs — smoke check for the new set_enabled RPC.

Frontend (app/src/)

  • components/channels/mcp/types.tsenabled: boolean + 'disabled' status.
  • services/api/mcpClientsApi.tssetEnabled({ server_id, enabled }) method.
  • components/channels/mcp/McpStatusBadge.tsxdisabled style (muted gray + italic) using mcp.status.disabled.
  • components/channels/mcp/InstalledServerDetail.tsx — Enable/Disable button using existing runBusy wrapper, clears local tool list on disable, fires onEnabledChange? callback. Connect/Disconnect hidden when !server.enabled.
  • components/channels/mcp/McpServersTab.tsx — passes onEnabledChange handler that re-fetches installed list + status, mirroring handleUninstalled.
  • Fixtures updated where Record<ServerStatus, …> and InstalledServer literals would otherwise have failed exhaustiveness checks.

i18nmcp.detail.enable, mcp.detail.disable, mcp.status.disabled added with real translations across en, ar, bn, de, es, fr, hi, id, it, ko, pl, pt, ru, zh-CN. pnpm i18n:check reports 0 missing / 0 extra.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — changed lines (Vitest + cargo-llvm-cov merged via diff-cover) meet the gate enforced by .github/workflows/pr-ci.yml. Run pnpm test:coverage and pnpm test:rust locally; PRs below 80% on changed lines will not merge.
  • Coverage matrix updated — added/removed/renamed feature rows in docs/TEST-COVERAGE-MATRIX.md reflect this change (or N/A: behaviour-only change) — N/A: extending an existing capability (MCP server lifecycle) already represented in the matrix; no new feature row needed.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — N/A: extension of existing MCP feature surface.
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md) — N/A: feature is within existing MCP server settings flow; no new release-cut surface.
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Desktop: New Enable/Disable affordance in Settings → MCP Servers → server detail. Disabling a server immediately disconnects its subprocess and hides its tools from the agent without losing env values; re-enabling does not auto-connect (enabled is a persistent setting, connected is a live state).
  • Migration: Additive SQLite ALTER TABLE runs at first boot post-upgrade. Legacy rows back-fill to enabled = 1. Idempotent — init_schema can be re-entered without error.
  • Performance: LAST_ERRORS is a process-global tokio::sync::RwLock<HashMap>; reads on all_status are O(n) over installed servers, one snapshot clone per call (matches CONNECTIONS).
  • Security: No new external network calls. Disabled servers cannot be connected via mcp_clients_connect — the guard runs after store::get_server, returning a clear error.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: issue/3196-add-support-for-multiple-mcp-servers
  • Commit SHA: 8afb03d0f

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck
  • Focused tests: cargo test --test mcp_registry_e2e (10 pass), cargo test --test mcp_registry_multi_server (4 pass), pnpm test src/components/channels/mcp/ (236 pass), pnpm test src/services/api/mcpClientsApi.test.ts (23 pass)
  • Rust fmt/check (if changed): cargo fmt --manifest-path Cargo.toml && cargo check --manifest-path Cargo.toml — clean
  • Tauri fmt/check (if changed): cargo check --manifest-path app/src-tauri/Cargo.toml — clean (no changes in app/src-tauri/)

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: MCP server installs now have a persisted enabled flag controlling boot auto-connect and tool exposure. New RPC openhuman.mcp_clients_set_enabled and matching UI toggle.
  • User-visible effect: Settings → MCP Servers gains an Enable/Disable button per server; failed connects render as a distinct "error" badge with the failure message visible in status; disabled servers render as a muted italic "disabled" badge.

Parity Contract

  • Legacy behavior preserved: SQLite schema migration is additive; pre-existing rows default to enabled = 1 so the upgrade is invisible to users with installed servers. connect/disconnect/all_status keep their existing return shapes — the ConnStatus.last_error field was already present and is now actually populated.
  • Guard/fallback/dispatch parity checks: mcp_clients_connect runs the disabled guard after the existing store::get_server lookup, so the "not found" error still wins over the "disabled" error. disconnect clears the LAST_ERRORS entry in addition to its existing connection-map removal.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): None
  • Canonical PR: This PR
  • Resolution: N/A

Summary by CodeRabbit

  • New Features

    • UI toggle to enable/disable individual MCP servers; disabling clears the tool list and closes the playground
    • Disabled servers are labeled "Disabled", excluded from aggregated tools and auto-start
  • Bug Fixes

    • Disabled servers reject connection attempts; toggling failures surface inline error messages
    • Disabling a running server disconnects it and updates status; re-enabling does not auto-start
  • Localization

    • Added translations for enable/disable controls and "Disabled" status
  • Tests

    • Extensive unit/integration coverage for enable/disable flows and status reporting

…humansai#3196)

Defaults to true so legacy rows keep auto-connecting. New column
enabled INTEGER NOT NULL DEFAULT 1 added via the same PRAGMA-driven
additive migration used for transport/deployment_url.
…nyhumansai#3196)

One-liner adding assert!(s.enabled, ...) in the existing serde-default
test so a future refactor that breaks #[serde(default)] is caught here
rather than transitively.
…inyhumansai#3196)

Wraps connect so each successful run clears the prior error and each
failure stores its message keyed by server_id. Exposed via
connections::last_error_for so the next status poll can surface
err state instead of generic 'disconnected'.
…tinyhumansai#3196)

Status priority: Disabled > Connected > Error > Disconnected.
Reads LAST_ERRORS so failed-connect servers surface as Error with
last_error populated instead of indistinguishable from idle.
A disabled install is left in the store with its env values intact,
but its subprocess is never spawned and its tools never enter the
agent's tool surface. One failed server still doesn't block others
(existing per-iteration error swallow plus LAST_ERRORS recording).
Give server C a bogus command so the post-boot last_error remains
None only if boot actually skipped the connect attempt. Without the
boot.rs guard the bogus command would record an error, failing the
test.
…isabled (tinyhumansai#3196)

set_enabled persists the flip and auto-disconnects on false. connect
returns a clear error when called on a disabled server. Re-enabling
does not auto-connect; the user re-issues connect explicitly so
'enabled' stays a persistent setting and 'connected' stays a
live-session state.
Four scenarios against the hermetic test-mcp-stub binary:
- Name collision: two servers exposing 'echo' both reach the surface
- Routing: call_tool dispatches by server_id even with shared names
- Failure isolation: bad subprocess doesn't block a healthy peer
- Disabled enforcement: disabled server contributes no tools and
  refuses explicit connect
…humansai#3196)

InstalledServer.enabled threaded through. ServerStatus gains a
'disabled' variant. McpStatusBadge renders it with a quieter
italic style and a dedicated i18n key so it reads distinctly from
'disconnected'.
)

Adds an Enable/Disable button next to Connect/Disconnect. Disabling
calls mcp_clients_set_enabled(enabled=false), clears local tool
state, and notifies the parent so the installed list + status
refresh. Connect is hidden for disabled servers.
Adds the disabled-status badge translation. mcp.detail.enable and
mcp.detail.disable were already added in the toggle commit; this
closes the last missing key referenced by McpStatusBadge.
@CodeGhost21 CodeGhost21 requested a review from a team June 4, 2026 09:07
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 808379c5-b17d-4808-b26e-85e7a9da9292

📥 Commits

Reviewing files that changed from the base of the PR and between 116fca4 and 8ad64b7.

📒 Files selected for processing (2)
  • app/src/components/channels/mcp/InstalledServerDetail.test.tsx
  • app/src/components/channels/mcp/McpServersTab.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/components/channels/mcp/InstalledServerDetail.test.tsx

📝 Walkthrough

Walkthrough

This PR adds an enabled flag and disabled status for MCP servers, persists and migrates it, exposes a set_enabled RPC and client, updates connection/boot/status behavior to respect disabled servers, wires an enable/disable toggle in the UI, adds i18n keys, and adds unit/integration/E2E tests.

Changes

MCP Server Enable/Disable Feature

Layer / File(s) Summary
Types and schema definitions
app/src/components/channels/mcp/types.ts, src/openhuman/mcp_registry/types.rs
Frontend and Rust types add enabled: boolean on InstalledServer and a Disabled server status with serde defaulting to true for legacy payloads.
Database persistence and migrations
src/openhuman/mcp_registry/store.rs
Adds enabled INTEGER NOT NULL DEFAULT 1 to schema; persist/read enabled in INSERT/SELECT and map rows with fallback for older DBs; adds update_enabled helpers and migration/round-trip tests.
Connection registry and status tracking
src/openhuman/mcp_registry/connections.rs, src/openhuman/mcp_registry/boot.rs
Tracks per-server last connect errors, clears on success/disconnect, reports status prioritizing disabledconnectederrordisconnected, and makes boot skip disabled servers.
RPC API schemas and handlers
src/openhuman/mcp_registry/schemas.rs, src/openhuman/mcp_registry/mod.rs
Registers mcp_clients.set_enabled controller and handler (handle_set_enabled) delegating to ops.
RPC operation handlers
src/openhuman/mcp_registry/ops.rs, src/openhuman/mcp_registry/setup_ops.rs
Install path sets enabled=true; mcp_clients_connect rejects disabled servers; mcp_clients_set_enabled persists flag, disconnects/clears last error and emits disconnect event when disabling; update_env avoids reconnect for disabled servers.
Frontend API client
app/src/services/api/mcpClientsApi.ts
Adds SetEnabledResult type and mcpClientsApi.setEnabled(server_id, enabled) calling openhuman.mcp_clients_set_enabled.
Detail component enable/disable toggle
app/src/components/channels/mcp/InstalledServerDetail.tsx
Adds onEnabledChange prop, handleSetEnabled that calls API and clears tools/playground when disabling, and shows Enable/Disable button while gating Connect/Disconnect on server.enabled.
Status badge and list UI updates
app/src/components/channels/mcp/McpStatusBadge.tsx, app/src/components/channels/mcp/InstalledServerList.tsx
Badge metadata includes disabled entry with i18n key and italic styling; list adds CSS and tooltip mappings for disabled indicator.
Frontend integration and state management
app/src/components/channels/mcp/McpServersTab.tsx
Wires handleEnabledChange to refresh installed servers and polled connection statuses after toggles.
Frontend component tests and fixtures
app/src/components/channels/mcp/*test.tsx
Adds toggle tests for InstalledServerDetail; updates test fixtures to include enabled: true across tests; adds McpStatusBadge disabled test.
Frontend API client tests
app/src/services/api/mcpClientsApi.test.ts
Adds tests verifying setEnabled RPC parameters and response handling for both enabling and disabling.
Multilingual UI translations
app/src/lib/i18n/*.ts
Adds mcp.detail.enable, mcp.detail.disable, and mcp.status.disabled across locales.
Backend integration and E2E tests
tests/*.rs
Adds/extends tests verifying last-error lifecycle, disabled status priority, boot skipping disabled servers, disconnect-on-disable, connect rejection when disabled, update_env behavior for disabled, multi-server routing/isolation, and JSON-RPC smoke coverage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tinyhumansai/openhuman#2409: Introduced the initial MCP UI and core infrastructure that this PR extends with enable/disable functionality.

Suggested reviewers

  • senamakel
  • sanil-23
  • graycyrus

Poem

🐰 I toggle true, then toggle false with care,

The server sleeps or wakes from its lair,
DB, RPC, and UI all nod in time,
Errors tracked, and tests sing in rhyme,
A rabbit hops—enabled, disabled—everywhere.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main feature: enabling/disabling multiple MCP servers and improving error visibility—core parts of the changeset.
Linked Issues check ✅ Passed All acceptance criteria from #3196 are met: multiple servers configurable with enable/disable, connection state visible including disabled status, tools discovered safely across servers, agent can use tools across servers, failure isolation via LAST_ERRORS and status prioritization, comprehensive test coverage (10 e2e + 4 multi-server scenarios), and diff coverage >80%.
Out of Scope Changes check ✅ Passed All changes are directly scoped to supporting multiple MCP servers: database schema, API handlers, UI components, translations, and test coverage. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. working A PR that is being worked on by the team. labels Jun 4, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/components/channels/mcp/McpStatusBadge.test.tsx (1)

22-29: ⚡ Quick win

Assert the disabled label text (behavior), not only the CSS class.

This test currently validates implementation detail (className) but not the user-visible disabled label, so it can miss i18n/status regressions.

Suggested test adjustment
-  it('renders the disabled status badge with the mcp.status.disabled i18n key', () => {
+  it('renders the disabled status badge label and style', () => {
     render(<McpStatusBadge status="disabled" />);
-    // The i18n key 'mcp.status.disabled' will be added in Task 9; until then
-    // the runtime falls back to the key string itself.
     const badge = screen.getByRole('status');
-    // The badge renders without crashing and carries the italic style.
+    expect(badge).toHaveTextContent('Disabled');
     expect(badge.className).toContain('italic');
   });

As per coding guidelines, “Prefer behavior over implementation in unit tests.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/channels/mcp/McpStatusBadge.test.tsx` around lines 22 -
29, The test for McpStatusBadge currently asserts only the CSS implementation
(badge.className contains 'italic'); update it to assert the user-visible label
as well by checking the rendered badge (screen.getByRole('status') or the same
element) contains the expected disabled text (the i18n fallback string
'mcp.status.disabled' until Task 9) in addition to the existing italic class
assertion so the test validates behavior not just implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/mcp_registry/ops.rs`:
- Around line 272-277: The connect guard that prevents connecting disabled
servers in mcp_clients_connect is missing from the mcp_clients_update_env
reconnection path: before calling connections::connect inside
mcp_clients_update_env (and other reconnect paths around lines 304-350), check
the server.enabled flag and return an Err with the same message used by
mcp_clients_connect when the server is disabled; this enforces the
disabled-state at the ops level (reference mcp_clients_update_env,
connections::connect, server.enabled, and mcp_clients_set_enabled).

---

Nitpick comments:
In `@app/src/components/channels/mcp/McpStatusBadge.test.tsx`:
- Around line 22-29: The test for McpStatusBadge currently asserts only the CSS
implementation (badge.className contains 'italic'); update it to assert the
user-visible label as well by checking the rendered badge
(screen.getByRole('status') or the same element) contains the expected disabled
text (the i18n fallback string 'mcp.status.disabled' until Task 9) in addition
to the existing italic class assertion so the test validates behavior not just
implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 68260bc7-7d03-49f1-a865-e3c57959679d

📥 Commits

Reviewing files that changed from the base of the PR and between 87a91ae and 8afb03d.

📒 Files selected for processing (39)
  • app/src/components/channels/mcp/InstalledServerDetail.enabledToggle.test.tsx
  • app/src/components/channels/mcp/InstalledServerDetail.test.tsx
  • app/src/components/channels/mcp/InstalledServerDetail.tsx
  • app/src/components/channels/mcp/InstalledServerList.test.tsx
  • app/src/components/channels/mcp/InstalledServerList.tsx
  • app/src/components/channels/mcp/McpInventoryManifest.test.ts
  • app/src/components/channels/mcp/McpInventoryPanel.test.tsx
  • app/src/components/channels/mcp/McpServersTab.tsx
  • app/src/components/channels/mcp/McpStatusBadge.test.tsx
  • app/src/components/channels/mcp/McpStatusBadge.tsx
  • app/src/components/channels/mcp/types.ts
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • app/src/services/api/mcpClientsApi.test.ts
  • app/src/services/api/mcpClientsApi.ts
  • src/openhuman/mcp_registry/boot.rs
  • src/openhuman/mcp_registry/connections.rs
  • src/openhuman/mcp_registry/mod.rs
  • src/openhuman/mcp_registry/ops.rs
  • src/openhuman/mcp_registry/schemas.rs
  • src/openhuman/mcp_registry/setup_ops.rs
  • src/openhuman/mcp_registry/store.rs
  • src/openhuman/mcp_registry/types.rs
  • tests/json_rpc_e2e.rs
  • tests/mcp_registry_e2e.rs
  • tests/mcp_registry_multi_server.rs
  • tests/tool_registry_approval_raw_coverage_e2e.rs

Comment thread src/openhuman/mcp_registry/ops.rs
…sh schema counts (tinyhumansai#3196)

- update_env on a disabled server now persists the new env values but
  skips the reconnect, returning status="disabled". Closes the gap
  CodeRabbit flagged where the existing connect-guard on
  mcp_clients_connect was not mirrored in the update_env reconnection
  path.
- Bumps the schemas_tests assertions from 19 to 20 (mcp_clients 13 → 14)
  to account for the new set_enabled controller — was the lib-test
  failure in CI.
- McpStatusBadge test now asserts the visible 'Disabled' label in
  addition to the italic class, per CodeRabbit nitpick.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 4, 2026
…tinyhumansai#3196)

Adds two short Vitest cases to push diff-cover above the 80% gate:
- McpServersTab: clicks Disable in the detail pane and verifies
  mcpClientsApi.setEnabled is called and the parent re-fetches the
  installed list + status (covers handleEnabledChange + the
  onEnabledChange prop wiring).
- InstalledServerDetail: renders with status='connecting' and asserts
  the Connecting button is shown and disabled (covers the
  connecting-label branch in the Connect/Disconnect group).

Also adds setEnabled + a mock + an enabled:true to the McpServersTab
test harness so the toggle render path matches the real component.
@senamakel senamakel merged commit d710252 into tinyhumansai:main Jun 4, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for multiple MCP servers

2 participants