diff --git a/clients/web/src/components/views/InspectorView/InspectorView.test.tsx b/clients/web/src/components/views/InspectorView/InspectorView.test.tsx
index 4b1f999a..18e1be16 100644
--- a/clients/web/src/components/views/InspectorView/InspectorView.test.tsx
+++ b/clients/web/src/components/views/InspectorView/InspectorView.test.tsx
@@ -920,6 +920,76 @@ describe("InspectorView", () => {
).toBeInTheDocument();
});
+ it("dims the other server cards while a connection is live", () => {
+ const betaServer: ServerEntry = {
+ id: "beta",
+ name: "Beta",
+ config: { type: "stdio", command: "echo" },
+ connection: { status: "disconnected" },
+ };
+ renderWithMantine(
+ ,
+ );
+ // The non-active card is inert while alpha holds a live session, so the
+ // user can't start a second connection mid-session.
+ const betaCard = screen.getByText("Beta").closest(".mantine-Card-root");
+ expect(betaCard?.getAttribute("aria-disabled")).toBe("true");
+ });
+
+ it("re-enables the other server cards when the active connection goes to error (#1521)", () => {
+ const betaServer: ServerEntry = {
+ id: "beta",
+ name: "Beta",
+ config: { type: "stdio", command: "echo" },
+ connection: { status: "disconnected" },
+ };
+ // Live session on alpha → beta starts out dimmed/inert.
+ const { rerender } = renderWithMantine(
+ ,
+ );
+ expect(
+ screen
+ .getByText("Beta")
+ .closest(".mantine-Card-root")
+ ?.getAttribute("aria-disabled"),
+ ).toBe("true");
+
+ // alpha's connection errors. App does NOT clear `activeServer` here — a
+ // terminal `error` fires no InspectorClient `disconnect` event — so the
+ // id still points at alpha. The other cards must re-enable anyway; only a
+ // *live* session should dim them.
+ rerender(
+ ,
+ );
+ expect(
+ screen
+ .getByText("Beta")
+ .closest(".mantine-Card-root")
+ ?.getAttribute("aria-disabled"),
+ ).toBeNull();
+ });
+
describe("listChanged indicator wiring (#1402)", () => {
// The indicator only mounts on the active screen, so each case connects,
// navigates to the target tab, and asserts the "List updated" affordance.
diff --git a/clients/web/src/components/views/InspectorView/InspectorView.tsx b/clients/web/src/components/views/InspectorView/InspectorView.tsx
index 3ed89b6b..98195c54 100644
--- a/clients/web/src/components/views/InspectorView/InspectorView.tsx
+++ b/clients/web/src/components/views/InspectorView/InspectorView.tsx
@@ -18,6 +18,7 @@ import type {
MessageEntry,
ServerEntry,
} from "@inspector/core/mcp/types.js";
+import { isTerminalStatus } from "@inspector/core/mcp/types.js";
import { isAppTool } from "@inspector/core/mcp/apps.js";
import { ViewHeader } from "../../groups/ViewHeader/ViewHeader";
import { ServerListScreen } from "../../screens/ServerListScreen/ServerListScreen";
@@ -576,6 +577,24 @@ export function InspectorView({
[serversInput, activeServer, connectionStatus, initializeResult],
);
+ // The other server cards are dimmed/`inert` only while a connection is
+ // actively live (connecting or connected), so the user can't kick off a
+ // second connection mid-session. Once the active session settles into a
+ // terminal state — `disconnected` or `error` — the rest must re-enable
+ // (#1521). A connect-time handshake failure (and a mid-session transport
+ // `onerror`) resolves to `error` *without* firing the InspectorClient
+ // `disconnect` event that App uses to clear `activeServerId`, so the id
+ // lingers; gating the dim source on liveness here is what un-dims the
+ // others. The errored card itself still shows its real status via the
+ // merged `servers` list above (keyed off the real `activeServer`), so
+ // passing `undefined` here lifts only the *other* cards' dimming, not the
+ // error indicator on the active one. `isTerminalStatus` (the #1490 teardown
+ // convention) is the single source of truth for "session is over" so this
+ // gate can't silently desync from a future status addition.
+ const dimCardsAgainst = isTerminalStatus(connectionStatus)
+ ? undefined
+ : activeServer;
+
return (
// padding={0}: each screen fills `calc(100dvh - header)` and supplies its
// own `xl` padding, so Main must contribute only the fixed-header offset.
@@ -612,7 +631,7 @@ export function InspectorView({