From 509ca909bb00768754008ebae9ce2b80d9e52414 Mon Sep 17 00:00:00 2001
From: evalstate <1936278+evalstate@users.noreply.github.com>
Date: Wed, 11 Jun 2025 12:01:36 +0100
Subject: [PATCH 1/7] make prompt and tool listing display consistent
---
client/src/components/PromptsTab.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/client/src/components/PromptsTab.tsx b/client/src/components/PromptsTab.tsx
index 66c15d8f..466b3d52 100644
--- a/client/src/components/PromptsTab.tsx
+++ b/client/src/components/PromptsTab.tsx
@@ -98,12 +98,12 @@ const PromptsTab = ({
setPromptArgs({});
}}
renderItem={(prompt) => (
- <>
+
{prompt.name}
-
+
{prompt.description}
- >
+
)}
title="Prompts"
buttonText={nextCursor ? "List More Prompts" : "List Prompts"}
From 6034ea36dc405a48d7bc05337683afa165cb72dc Mon Sep 17 00:00:00 2001
From: Richard Michael
Date: Sat, 14 Jun 2025 22:31:30 -0700
Subject: [PATCH 2/7] Fix expansion state tracking in Server Notifications
---
client/src/components/History.tsx | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/client/src/components/History.tsx b/client/src/components/History.tsx
index 78394dea..34c035f5 100644
--- a/client/src/components/History.tsx
+++ b/client/src/components/History.tsx
@@ -110,15 +110,27 @@ const HistoryAndNotifications = ({
>
toggleNotificationExpansion(index)}
+ onClick={() =>
+ toggleNotificationExpansion(
+ serverNotifications.length - 1 - index,
+ )
+ }
>
{serverNotifications.length - index}.{" "}
{notification.method}
- {expandedNotifications[index] ? "▼" : "▶"}
+
+ {expandedNotifications[
+ serverNotifications.length - 1 - index
+ ]
+ ? "▼"
+ : "▶"}
+
- {expandedNotifications[index] && (
+ {expandedNotifications[
+ serverNotifications.length - 1 - index
+ ] && (
From 7eaa948e9800230237d2b701482a943183e53561 Mon Sep 17 00:00:00 2001
From: Richard Michael
Date: Sat, 14 Jun 2025 23:29:19 -0700
Subject: [PATCH 3/7] Add tests for HistoryAndNotifications component
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Tests cover basic rendering, numbering, expansion functionality, and
expansion state persistence when new items are added. Includes
regression test for notification expansion state tracking bug.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
.../src/components/__tests__/History.test.tsx | 216 ++++++++++++++++++
1 file changed, 216 insertions(+)
create mode 100644 client/src/components/__tests__/History.test.tsx
diff --git a/client/src/components/__tests__/History.test.tsx b/client/src/components/__tests__/History.test.tsx
new file mode 100644
index 00000000..19cae083
--- /dev/null
+++ b/client/src/components/__tests__/History.test.tsx
@@ -0,0 +1,216 @@
+import { render, screen, fireEvent } from "@testing-library/react";
+import "@testing-library/jest-dom";
+import { describe, it, expect, jest } from "@jest/globals";
+import HistoryAndNotifications from "../History";
+import { ServerNotification } from "@modelcontextprotocol/sdk/types.js";
+
+// Mock JsonView component
+jest.mock("../JsonView", () => {
+ return function JsonView({ data }: { data: string }) {
+ return {data}
;
+ };
+});
+
+describe("HistoryAndNotifications", () => {
+ const mockRequestHistory = [
+ {
+ request: JSON.stringify({ method: "test/method1", params: {} }),
+ response: JSON.stringify({ result: "success" }),
+ },
+ {
+ request: JSON.stringify({ method: "test/method2", params: {} }),
+ response: JSON.stringify({ result: "success" }),
+ },
+ ];
+
+ const mockNotifications: ServerNotification[] = [
+ {
+ method: "notification/test1",
+ params: { message: "First notification" },
+ },
+ {
+ method: "notification/test2",
+ params: { message: "Second notification" },
+ },
+ ];
+
+ it("renders history and notifications sections", () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByText("History")).toBeInTheDocument();
+ expect(screen.getByText("Server Notifications")).toBeInTheDocument();
+ });
+
+ it("displays request history items with correct numbering", () => {
+ render(
+ ,
+ );
+
+ // Items should be numbered in reverse order (newest first)
+ expect(screen.getByText("2. test/method2")).toBeInTheDocument();
+ expect(screen.getByText("1. test/method1")).toBeInTheDocument();
+ });
+
+ it("displays server notifications with correct numbering", () => {
+ render(
+ ,
+ );
+
+ // Items should be numbered in reverse order (newest first)
+ expect(screen.getByText("2. notification/test2")).toBeInTheDocument();
+ expect(screen.getByText("1. notification/test1")).toBeInTheDocument();
+ });
+
+ it("expands and collapses request items when clicked", () => {
+ render(
+ ,
+ );
+
+ const firstRequestHeader = screen.getByText("2. test/method2");
+
+ // Initially collapsed - should show ▶ arrows (there are multiple)
+ expect(screen.getAllByText("▶")).toHaveLength(2);
+ expect(screen.queryByText("Request:")).not.toBeInTheDocument();
+
+ // Click to expand
+ fireEvent.click(firstRequestHeader);
+
+ // Should now be expanded - one ▼ and one ▶
+ expect(screen.getByText("▼")).toBeInTheDocument();
+ expect(screen.getAllByText("▶")).toHaveLength(1);
+ expect(screen.getByText("Request:")).toBeInTheDocument();
+ expect(screen.getByText("Response:")).toBeInTheDocument();
+ });
+
+ it("expands and collapses notification items when clicked", () => {
+ render(
+ ,
+ );
+
+ const firstNotificationHeader = screen.getByText("2. notification/test2");
+
+ // Initially collapsed
+ expect(screen.getAllByText("▶")).toHaveLength(2);
+ expect(screen.queryByText("Details:")).not.toBeInTheDocument();
+
+ // Click to expand
+ fireEvent.click(firstNotificationHeader);
+
+ // Should now be expanded
+ expect(screen.getByText("▼")).toBeInTheDocument();
+ expect(screen.getAllByText("▶")).toHaveLength(1);
+ expect(screen.getByText("Details:")).toBeInTheDocument();
+ });
+
+ it("maintains expanded state when new notifications are added", () => {
+ const { rerender } = render(
+ ,
+ );
+
+ // Find and expand the older notification (should be "1. notification/test1")
+ const olderNotificationHeader = screen.getByText("1. notification/test1");
+ fireEvent.click(olderNotificationHeader);
+
+ // Verify it's expanded
+ expect(screen.getByText("Details:")).toBeInTheDocument();
+
+ // Add a new notification at the beginning (simulating real behavior)
+ const newNotifications: ServerNotification[] = [
+ {
+ method: "notification/new",
+ params: { message: "New notification" },
+ },
+ ...mockNotifications,
+ ];
+
+ // Re-render with new notifications
+ rerender(
+ ,
+ );
+
+ // The original notification should still be expanded
+ // It's now numbered as "2. notification/test1" due to the new item
+ expect(screen.getByText("3. notification/test2")).toBeInTheDocument();
+ expect(screen.getByText("2. notification/test1")).toBeInTheDocument();
+ expect(screen.getByText("1. notification/new")).toBeInTheDocument();
+
+ // The originally expanded notification should still show its details
+ expect(screen.getByText("Details:")).toBeInTheDocument();
+ });
+
+ it("maintains expanded state when new requests are added", () => {
+ const { rerender } = render(
+ ,
+ );
+
+ // Find and expand the older request (should be "1. test/method1")
+ const olderRequestHeader = screen.getByText("1. test/method1");
+ fireEvent.click(olderRequestHeader);
+
+ // Verify it's expanded
+ expect(screen.getByText("Request:")).toBeInTheDocument();
+ expect(screen.getByText("Response:")).toBeInTheDocument();
+
+ // Add a new request at the beginning
+ const newRequestHistory = [
+ {
+ request: JSON.stringify({ method: "test/new_method", params: {} }),
+ response: JSON.stringify({ result: "new success" }),
+ },
+ ...mockRequestHistory,
+ ];
+
+ // Re-render with new request history
+ rerender(
+ ,
+ );
+
+ // The original request should still be expanded
+ // It's now numbered as "2. test/method1" due to the new item
+ expect(screen.getByText("3. test/method2")).toBeInTheDocument();
+ expect(screen.getByText("2. test/method1")).toBeInTheDocument();
+ expect(screen.getByText("1. test/new_method")).toBeInTheDocument();
+
+ // The originally expanded request should still show its details
+ expect(screen.getByText("Request:")).toBeInTheDocument();
+ expect(screen.getByText("Response:")).toBeInTheDocument();
+ });
+
+ it("displays empty state messages when no data is available", () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByText("No history yet")).toBeInTheDocument();
+ expect(screen.getByText("No notifications yet")).toBeInTheDocument();
+ });
+});
From 78eaa177a49183cf1d95fe31055237aebf4aa196 Mon Sep 17 00:00:00 2001
From: Richard Michael
Date: Sat, 14 Jun 2025 23:34:41 -0700
Subject: [PATCH 4/7] Rename History file to HistoryAndNotifications
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Rename History.tsx to HistoryAndNotifications.tsx for consistency
with component name and update imports.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude
---
client/src/App.tsx | 2 +-
...istory.tsx => HistoryAndNotifications.tsx} | 0
...t.tsx => HistoryAndNotifications.test.tsx} | 88 +++++++++++--------
3 files changed, 50 insertions(+), 40 deletions(-)
rename client/src/components/{History.tsx => HistoryAndNotifications.tsx} (100%)
rename client/src/components/__tests__/{History.test.tsx => HistoryAndNotifications.test.tsx} (66%)
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 26eb44c5..3ceafcae 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -52,7 +52,7 @@ import { z } from "zod";
import "./App.css";
import AuthDebugger from "./components/AuthDebugger";
import ConsoleTab from "./components/ConsoleTab";
-import HistoryAndNotifications from "./components/History";
+import HistoryAndNotifications from "./components/HistoryAndNotifications";
import PingTab from "./components/PingTab";
import PromptsTab, { Prompt } from "./components/PromptsTab";
import ResourcesTab from "./components/ResourcesTab";
diff --git a/client/src/components/History.tsx b/client/src/components/HistoryAndNotifications.tsx
similarity index 100%
rename from client/src/components/History.tsx
rename to client/src/components/HistoryAndNotifications.tsx
diff --git a/client/src/components/__tests__/History.test.tsx b/client/src/components/__tests__/HistoryAndNotifications.test.tsx
similarity index 66%
rename from client/src/components/__tests__/History.test.tsx
rename to client/src/components/__tests__/HistoryAndNotifications.test.tsx
index 19cae083..42c58519 100644
--- a/client/src/components/__tests__/History.test.tsx
+++ b/client/src/components/__tests__/HistoryAndNotifications.test.tsx
@@ -1,7 +1,6 @@
import { render, screen, fireEvent } from "@testing-library/react";
-import "@testing-library/jest-dom";
import { describe, it, expect, jest } from "@jest/globals";
-import HistoryAndNotifications from "../History";
+import HistoryAndNotifications from "../HistoryAndNotifications";
import { ServerNotification } from "@modelcontextprotocol/sdk/types.js";
// Mock JsonView component
@@ -25,12 +24,19 @@ describe("HistoryAndNotifications", () => {
const mockNotifications: ServerNotification[] = [
{
- method: "notification/test1",
- params: { message: "First notification" },
+ method: "notifications/message",
+ params: {
+ level: "info" as const,
+ message: "First notification",
+ },
},
{
- method: "notification/test2",
- params: { message: "Second notification" },
+ method: "notifications/progress",
+ params: {
+ progressToken: "test-token",
+ progress: 50,
+ message: "Second notification",
+ },
},
];
@@ -42,8 +48,8 @@ describe("HistoryAndNotifications", () => {
/>,
);
- expect(screen.getByText("History")).toBeInTheDocument();
- expect(screen.getByText("Server Notifications")).toBeInTheDocument();
+ expect(screen.getByText("History")).toBeTruthy();
+ expect(screen.getByText("Server Notifications")).toBeTruthy();
});
it("displays request history items with correct numbering", () => {
@@ -55,8 +61,8 @@ describe("HistoryAndNotifications", () => {
);
// Items should be numbered in reverse order (newest first)
- expect(screen.getByText("2. test/method2")).toBeInTheDocument();
- expect(screen.getByText("1. test/method1")).toBeInTheDocument();
+ expect(screen.getByText("2. test/method2")).toBeTruthy();
+ expect(screen.getByText("1. test/method1")).toBeTruthy();
});
it("displays server notifications with correct numbering", () => {
@@ -68,8 +74,8 @@ describe("HistoryAndNotifications", () => {
);
// Items should be numbered in reverse order (newest first)
- expect(screen.getByText("2. notification/test2")).toBeInTheDocument();
- expect(screen.getByText("1. notification/test1")).toBeInTheDocument();
+ expect(screen.getByText("2. notifications/progress")).toBeTruthy();
+ expect(screen.getByText("1. notifications/message")).toBeTruthy();
});
it("expands and collapses request items when clicked", () => {
@@ -84,16 +90,16 @@ describe("HistoryAndNotifications", () => {
// Initially collapsed - should show ▶ arrows (there are multiple)
expect(screen.getAllByText("▶")).toHaveLength(2);
- expect(screen.queryByText("Request:")).not.toBeInTheDocument();
+ expect(screen.queryByText("Request:")).toBeNull();
// Click to expand
fireEvent.click(firstRequestHeader);
// Should now be expanded - one ▼ and one ▶
- expect(screen.getByText("▼")).toBeInTheDocument();
+ expect(screen.getByText("▼")).toBeTruthy();
expect(screen.getAllByText("▶")).toHaveLength(1);
- expect(screen.getByText("Request:")).toBeInTheDocument();
- expect(screen.getByText("Response:")).toBeInTheDocument();
+ expect(screen.getByText("Request:")).toBeTruthy();
+ expect(screen.getByText("Response:")).toBeTruthy();
});
it("expands and collapses notification items when clicked", () => {
@@ -104,19 +110,21 @@ describe("HistoryAndNotifications", () => {
/>,
);
- const firstNotificationHeader = screen.getByText("2. notification/test2");
+ const firstNotificationHeader = screen.getByText(
+ "2. notifications/progress",
+ );
// Initially collapsed
expect(screen.getAllByText("▶")).toHaveLength(2);
- expect(screen.queryByText("Details:")).not.toBeInTheDocument();
+ expect(screen.queryByText("Details:")).toBeNull();
// Click to expand
fireEvent.click(firstNotificationHeader);
// Should now be expanded
- expect(screen.getByText("▼")).toBeInTheDocument();
+ expect(screen.getByText("▼")).toBeTruthy();
expect(screen.getAllByText("▶")).toHaveLength(1);
- expect(screen.getByText("Details:")).toBeInTheDocument();
+ expect(screen.getByText("Details:")).toBeTruthy();
});
it("maintains expanded state when new notifications are added", () => {
@@ -127,18 +135,20 @@ describe("HistoryAndNotifications", () => {
/>,
);
- // Find and expand the older notification (should be "1. notification/test1")
- const olderNotificationHeader = screen.getByText("1. notification/test1");
+ // Find and expand the older notification (should be "1. notifications/message")
+ const olderNotificationHeader = screen.getByText(
+ "1. notifications/message",
+ );
fireEvent.click(olderNotificationHeader);
// Verify it's expanded
- expect(screen.getByText("Details:")).toBeInTheDocument();
+ expect(screen.getByText("Details:")).toBeTruthy();
// Add a new notification at the beginning (simulating real behavior)
const newNotifications: ServerNotification[] = [
{
- method: "notification/new",
- params: { message: "New notification" },
+ method: "notifications/resources/updated",
+ params: { uri: "file://test.txt" },
},
...mockNotifications,
];
@@ -152,13 +162,13 @@ describe("HistoryAndNotifications", () => {
);
// The original notification should still be expanded
- // It's now numbered as "2. notification/test1" due to the new item
- expect(screen.getByText("3. notification/test2")).toBeInTheDocument();
- expect(screen.getByText("2. notification/test1")).toBeInTheDocument();
- expect(screen.getByText("1. notification/new")).toBeInTheDocument();
+ // It's now numbered as "2. notifications/message" due to the new item
+ expect(screen.getByText("3. notifications/progress")).toBeTruthy();
+ expect(screen.getByText("2. notifications/message")).toBeTruthy();
+ expect(screen.getByText("1. notifications/resources/updated")).toBeTruthy();
// The originally expanded notification should still show its details
- expect(screen.getByText("Details:")).toBeInTheDocument();
+ expect(screen.getByText("Details:")).toBeTruthy();
});
it("maintains expanded state when new requests are added", () => {
@@ -174,8 +184,8 @@ describe("HistoryAndNotifications", () => {
fireEvent.click(olderRequestHeader);
// Verify it's expanded
- expect(screen.getByText("Request:")).toBeInTheDocument();
- expect(screen.getByText("Response:")).toBeInTheDocument();
+ expect(screen.getByText("Request:")).toBeTruthy();
+ expect(screen.getByText("Response:")).toBeTruthy();
// Add a new request at the beginning
const newRequestHistory = [
@@ -196,13 +206,13 @@ describe("HistoryAndNotifications", () => {
// The original request should still be expanded
// It's now numbered as "2. test/method1" due to the new item
- expect(screen.getByText("3. test/method2")).toBeInTheDocument();
- expect(screen.getByText("2. test/method1")).toBeInTheDocument();
- expect(screen.getByText("1. test/new_method")).toBeInTheDocument();
+ expect(screen.getByText("3. test/method2")).toBeTruthy();
+ expect(screen.getByText("2. test/method1")).toBeTruthy();
+ expect(screen.getByText("1. test/new_method")).toBeTruthy();
// The originally expanded request should still show its details
- expect(screen.getByText("Request:")).toBeInTheDocument();
- expect(screen.getByText("Response:")).toBeInTheDocument();
+ expect(screen.getByText("Request:")).toBeTruthy();
+ expect(screen.getByText("Response:")).toBeTruthy();
});
it("displays empty state messages when no data is available", () => {
@@ -210,7 +220,7 @@ describe("HistoryAndNotifications", () => {
,
);
- expect(screen.getByText("No history yet")).toBeInTheDocument();
- expect(screen.getByText("No notifications yet")).toBeInTheDocument();
+ expect(screen.getByText("No history yet")).toBeTruthy();
+ expect(screen.getByText("No notifications yet")).toBeTruthy();
});
});
From c0e9899cd4716e231aaadc34dc893ba9022c4217 Mon Sep 17 00:00:00 2001
From: Manabu McCloskey
Date: Mon, 23 Jun 2025 22:58:54 +0000
Subject: [PATCH 5/7] fix connection refused error message
Signed-off-by: Manabu McCloskey
---
server/src/mcpProxy.ts | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/server/src/mcpProxy.ts b/server/src/mcpProxy.ts
index 9dee9de4..d37e312f 100644
--- a/server/src/mcpProxy.ts
+++ b/server/src/mcpProxy.ts
@@ -6,11 +6,7 @@ function onClientError(error: Error) {
}
function onServerError(error: Error) {
- if (
- (error?.message &&
- error.message.includes("Error POSTing to endpoint (HTTP 404)")) ||
- (error?.cause && JSON.stringify(error.cause).includes("ECONNREFUSED"))
- ) {
+ if (error?.cause && JSON.stringify(error.cause).includes("ECONNREFUSED")) {
console.error("Connection refused. Is the MCP server running?");
} else {
console.error("Error from MCP server:", error);
From 5868c6bad6075e7c9dd49c3eb9b4ef946ceed53f Mon Sep 17 00:00:00 2001
From: Manabu McCloskey
Date: Thu, 3 Jul 2025 14:48:46 +0000
Subject: [PATCH 6/7] print explicit 404 message
Signed-off-by: Manabu McCloskey
---
server/src/mcpProxy.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/server/src/mcpProxy.ts b/server/src/mcpProxy.ts
index d37e312f..664f1711 100644
--- a/server/src/mcpProxy.ts
+++ b/server/src/mcpProxy.ts
@@ -8,6 +8,8 @@ function onClientError(error: Error) {
function onServerError(error: Error) {
if (error?.cause && JSON.stringify(error.cause).includes("ECONNREFUSED")) {
console.error("Connection refused. Is the MCP server running?");
+ } else if (error.message && error.message.includes("404")) {
+ console.error("Error accessing endpoint (HTTP 404)");
} else {
console.error("Error from MCP server:", error);
}
From d1e2498ad0e3fc0574eafd4288f6fe4572ae70e3 Mon Sep 17 00:00:00 2001
From: Richard Michael
Date: Thu, 3 Jul 2025 21:28:00 -0700
Subject: [PATCH 7/7] Add engines to `package-lock.json`
---
package-lock.json | 3 +++
1 file changed, 3 insertions(+)
diff --git a/package-lock.json b/package-lock.json
index ce3b7ba7..075b0a28 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -37,6 +37,9 @@
"prettier": "3.3.3",
"rimraf": "^6.0.1",
"typescript": "^5.4.2"
+ },
+ "engines": {
+ "node": ">=22.7.5"
}
},
"cli": {