diff --git a/client/src/App.tsx b/client/src/App.tsx
index 59d15ba06..484c8ab14 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -584,6 +584,23 @@ const App = () => {
saveInspectorConfig(CONFIG_LOCAL_STORAGE_KEY, config);
}, [config]);
+ useEffect(() => {
+ if (connectionStatus === "disconnected" || connectionStatus === "error") {
+ setToolResult(null);
+ setPromptContent("");
+ setResourceContent("");
+ setResourceContentMap({});
+ setLogLevel("info");
+ setTools([]);
+ setSelectedTool(null);
+ setPrompts([]);
+ setSelectedPrompt(null);
+ setResources([]);
+ setResourceTemplates([]);
+ setSelectedResource(null);
+ }
+ }, [connectionStatus]);
+
const onOAuthConnect = useCallback(
(serverUrl: string) => {
setSseUrl(serverUrl);
diff --git a/client/src/__tests__/App.disconnect.test.tsx b/client/src/__tests__/App.disconnect.test.tsx
new file mode 100644
index 000000000..64ef672d5
--- /dev/null
+++ b/client/src/__tests__/App.disconnect.test.tsx
@@ -0,0 +1,222 @@
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import "@testing-library/jest-dom";
+import App from "../App";
+import { useConnection } from "../lib/hooks/useConnection";
+import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
+
+jest.mock("@modelcontextprotocol/sdk/client/auth.js", () => ({
+ auth: jest.fn(),
+}));
+
+jest.mock("../lib/oauth-state-machine", () => ({
+ OAuthStateMachine: jest.fn(),
+}));
+
+jest.mock("../lib/auth", () => ({
+ InspectorOAuthClientProvider: jest.fn().mockImplementation(() => ({
+ tokens: jest.fn().mockResolvedValue(null),
+ clear: jest.fn(),
+ })),
+ DebugInspectorOAuthClientProvider: jest.fn(),
+}));
+
+jest.mock("../utils/configUtils", () => ({
+ ...jest.requireActual("../utils/configUtils"),
+ getMCPProxyAddress: jest.fn(() => "http://localhost:6277"),
+ getMCPProxyAuthToken: jest.fn(() => ({
+ token: "",
+ header: "X-MCP-Proxy-Auth",
+ })),
+ getInitialTransportType: jest.fn(() => "stdio"),
+ getInitialSseUrl: jest.fn(() => "http://localhost:3001/sse"),
+ getInitialCommand: jest.fn(() => "mcp-server-everything"),
+ getInitialArgs: jest.fn(() => ""),
+ initializeInspectorConfig: jest.fn(() => ({})),
+ saveInspectorConfig: jest.fn(),
+}));
+
+jest.mock("../lib/hooks/useDraggablePane", () => ({
+ useDraggablePane: () => ({
+ height: 300,
+ handleDragStart: jest.fn(),
+ }),
+ useDraggableSidebar: () => ({
+ width: 320,
+ isDragging: false,
+ handleDragStart: jest.fn(),
+ }),
+}));
+
+jest.mock("../components/Sidebar", () => ({
+ __esModule: true,
+ default: () =>
Sidebar
,
+}));
+
+jest.mock("../components/ResourcesTab", () => ({
+ __esModule: true,
+ default: () => ResourcesTab
,
+}));
+
+jest.mock("../components/PromptsTab", () => ({
+ __esModule: true,
+ default: () => PromptsTab
,
+}));
+
+jest.mock("../components/TasksTab", () => ({
+ __esModule: true,
+ default: () => TasksTab
,
+}));
+
+jest.mock("../components/ConsoleTab", () => ({
+ __esModule: true,
+ default: () => ConsoleTab
,
+}));
+
+jest.mock("../components/PingTab", () => ({
+ __esModule: true,
+ default: () => PingTab
,
+}));
+
+jest.mock("../components/SamplingTab", () => ({
+ __esModule: true,
+ default: () => SamplingTab
,
+}));
+
+jest.mock("../components/RootsTab", () => ({
+ __esModule: true,
+ default: () => RootsTab
,
+}));
+
+jest.mock("../components/ElicitationTab", () => ({
+ __esModule: true,
+ default: () => ElicitationTab
,
+}));
+
+jest.mock("../components/MetadataTab", () => ({
+ __esModule: true,
+ default: () => MetadataTab
,
+}));
+
+jest.mock("../components/AuthDebugger", () => ({
+ __esModule: true,
+ default: () => AuthDebugger
,
+}));
+
+jest.mock("../components/HistoryAndNotifications", () => ({
+ __esModule: true,
+ default: () => HistoryAndNotifications
,
+}));
+
+jest.mock("../components/AppsTab", () => ({
+ __esModule: true,
+ default: () => AppsTab
,
+}));
+
+jest.mock("../components/ToolsTab", () => ({
+ __esModule: true,
+ default: ({
+ callTool,
+ toolResult,
+ }: {
+ callTool: (
+ name: string,
+ params: Record,
+ ) => Promise;
+ toolResult: unknown;
+ }) => (
+
+
+ {toolResult && (
+
{JSON.stringify(toolResult)}
+ )}
+
+ ),
+}));
+
+global.fetch = jest.fn().mockResolvedValue({ json: () => Promise.resolve({}) });
+
+jest.mock("../lib/hooks/useConnection", () => ({
+ useConnection: jest.fn(),
+}));
+
+const mockClient = {
+ request: jest.fn(),
+ notification: jest.fn(),
+ close: jest.fn(),
+} as unknown as Client;
+
+const makeRequest = jest.fn(async (request: { method: string }) => {
+ if (request.method === "tools/list") {
+ return {
+ tools: [
+ { name: "echo", inputSchema: { type: "object", properties: {} } },
+ ],
+ nextCursor: undefined,
+ };
+ }
+ if (request.method === "tools/call") {
+ return { content: [{ type: "text", text: "echo result" }] };
+ }
+ throw new Error(`Unexpected method: ${request.method}`);
+});
+
+const connectedState = {
+ connectionStatus: "connected" as const,
+ serverCapabilities: { tools: { listChanged: true } },
+ serverImplementation: null,
+ mcpClient: mockClient,
+ requestHistory: [],
+ clearRequestHistory: jest.fn(),
+ makeRequest,
+ cancelTask: jest.fn(),
+ listTasks: jest.fn(),
+ sendNotification: jest.fn(),
+ handleCompletion: jest.fn(),
+ completionsSupported: false,
+ connect: jest.fn(),
+ disconnect: jest.fn(),
+} as ReturnType;
+
+const disconnectedState = {
+ ...connectedState,
+ connectionStatus: "disconnected" as const,
+ serverCapabilities: null,
+ mcpClient: null,
+};
+
+describe("App - session state cleared on disconnect", () => {
+ const mockUseConnection = jest.mocked(useConnection);
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ window.location.hash = "#tools";
+ });
+
+ it("clears tool result panel when connection is disconnected", async () => {
+ mockUseConnection.mockReturnValue(connectedState);
+
+ const { rerender } = render();
+
+ // Trigger a tool call to populate toolResult
+ fireEvent.click(screen.getByRole("button", { name: /run tool/i }));
+
+ // Verify the result is shown
+ await waitFor(() => {
+ expect(screen.getByTestId("tool-result")).toBeInTheDocument();
+ });
+
+ // Simulate disconnect
+ mockUseConnection.mockReturnValue(disconnectedState);
+ rerender();
+
+ // Result panel should be cleared
+ await waitFor(() => {
+ expect(screen.queryByTestId("tool-result")).not.toBeInTheDocument();
+ });
+ });
+});