diff --git a/client/e2e/resource-refresh.spec.ts b/client/e2e/resource-refresh.spec.ts new file mode 100644 index 000000000..c422e5ddc --- /dev/null +++ b/client/e2e/resource-refresh.spec.ts @@ -0,0 +1,62 @@ +import { test, expect } from "@playwright/test"; + +const AUTH_TOKEN = process.env.MCP_PROXY_AUTH_TOKEN ?? ""; +const APP_URL = AUTH_TOKEN + ? `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=${AUTH_TOKEN}` + : "http://localhost:6274/"; + +test.describe("Resource Refresh Button", () => { + test.beforeEach(async ({ page }) => { + await page.goto(APP_URL); + + // Set up STDIO transport with server-everything + const transportSelect = page.getByLabel("Transport Type"); + await expect(transportSelect).toBeVisible(); + await expect(transportSelect).toContainText("STDIO"); + + const commandInput = page.locator("#command-input"); + await commandInput.fill("npx"); + + const argsInput = page.locator("#arguments-input"); + await argsInput.fill("-y @modelcontextprotocol/server-everything@latest"); + + // Connect + await page.getByRole("button", { name: "Connect" }).click(); + + // Wait for connection to be established + await expect(page.getByRole("button", { name: "Disconnect" })).toBeVisible({ + timeout: 30000, + }); + }); + + test("should re-fetch resource when refresh button is clicked", async ({ + page, + }) => { + // Navigate to Resources tab + await page.getByRole("tab", { name: "Resources" }).click(); + + // List resources + await page.getByRole("button", { name: "List Resources" }).click(); + + // Wait for a resource to appear and click it + const firstResource = page.getByText("architecture.md").first(); + await expect(firstResource).toBeVisible({ timeout: 10000 }); + await firstResource.click(); + + // Wait for resource content to load + await page.waitForTimeout(2000); + + // Count current resources/read entries in history + const historyEntries = page.locator("text=resources/read"); + const initialCount = await historyEntries.count(); + expect(initialCount).toBeGreaterThanOrEqual(1); + + // Click the Refresh button + await page.getByRole("button", { name: "Refresh" }).click(); + + // Verify a new resources/read request appeared in history + await expect(historyEntries).toHaveCount(initialCount + 1, { + timeout: 10000, + }); + }); +}); diff --git a/client/src/App.tsx b/client/src/App.tsx index 12e9a7bd0..2e81b9e5d 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -893,8 +893,13 @@ const App = () => { setPromptContent(JSON.stringify(response, null, 2)); }; - const readResource = async (uri: string) => { - if (fetchingResources.has(uri) || resourceContentMap[uri]) { + const readResource = async (uri: string, force: boolean = false) => { + if (fetchingResources.has(uri)) { + return; + } + + if (!force && resourceContentMap[uri]) { + setResourceContent(resourceContentMap[uri]); return; } @@ -1471,9 +1476,9 @@ const App = () => { setResourceTemplates([]); setNextResourceTemplateCursor(undefined); }} - readResource={(uri) => { + readResource={(uri, force) => { clearError("resources"); - readResource(uri); + readResource(uri, force); }} selectedResource={selectedResource} setSelectedResource={(resource) => { diff --git a/client/src/components/ResourcesTab.tsx b/client/src/components/ResourcesTab.tsx index 36e5cec8f..9f2774217 100644 --- a/client/src/components/ResourcesTab.tsx +++ b/client/src/components/ResourcesTab.tsx @@ -46,7 +46,7 @@ const ResourcesTab = ({ clearResources: () => void; listResourceTemplates: () => void; clearResourceTemplates: () => void; - readResource: (uri: string) => void; + readResource: (uri: string, force?: boolean) => void; selectedResource: Resource | null; setSelectedResource: (resource: Resource | null) => void; handleCompletion: ( @@ -229,7 +229,7 @@ const ResourcesTab = ({