diff --git a/.claude/skills/write-insomnia-test/reference.md b/.claude/skills/write-insomnia-test/reference.md index aadf8d2c17..6fa28ad8cb 100644 --- a/.claude/skills/write-insomnia-test/reference.md +++ b/.claude/skills/write-insomnia-test/reference.md @@ -17,7 +17,7 @@ Every test injects only the `user` fixture (`async ({ user }) => {...}`) — nev | WebSocket request | `webSocketRequestFlow` | `flows/web-socket-request.flow.ts` | `pages/web-socket-request.page.ts` | `models/websocket-request.ts` | | Socket.IO request | `socketIoRequestFlow` | `flows/socket-io-request.flow.ts` | `pages/socket-io-request.page.ts` | `models/socket-io-request.ts` | | Event Stream (SSE) request | `eventStreamRequestFlow` | `flows/event-stream-request.flow.ts` | `pages/event-stream-request.page.ts` | `models/event-stream-request.ts` | -| MCP client | `mcpClientFlow` | `flows/mcp-client.flow.ts` | `pages/mcp-client.page.ts` | `models/mcp-client.ts` | +| MCP client | `mcpClientFlow` | `flows/mcp-client.flow.ts` | `pages/mcp-client.page.ts` | `models/mcp-client.ts`; `enums/mcp-capability.ts` (`McpPrimitiveType`, `McpListAction`) | | Mock Server (self-hosted only, `tests/mock-server/`) | `mockServerFlow` | `flows/mock-server.flow.ts` | `pages/mock-server.page.ts` | `models/mock-server.ts` (`MockServer`, positional ctor), `models/mock-route.ts` (`MockRoute` literal, `MockRouteHistoryEntry`); `enums/mock-body-type.ts` (`MockBodyType`) | | Environment | `environmentFlow` | `flows/environment.flow.ts` | `pages/environment.page.ts` | `models/environment.ts` | | Cookie | `cookieFlow` | `flows/cookie.flow.ts` | `pages/cookie.page.ts` | `models/cookie.ts` | @@ -557,11 +557,11 @@ getListError(client: McpClient, type: McpCapabilityType): Promise // collapse/expand the capability item refresh(client: McpClient, type: McpCapabilityType): Promise // actions menu -> Refresh (re-runs the list request, re-derives entries + error) loadMore(client: McpClient, type: McpCapabilityType): Promise // actions menu -> Load More (only offered while the last page carried a nextCursor) -getListAction(client: McpClient, type: McpCapabilityType): Promise // "Refresh" or "Load More" — what the actions menu currently offers +getListAction(client: McpClient, type: McpCapabilityType): Promise // McpListAction.Refresh or .LoadMore — what the actions menu currently offers filterCapabilities(client: McpClient, text: string): Promise // opens the client, types into the sidebar capability filter (narrows primitives by name) — wraps McpClientPage.setCapabilityFilter ``` -`McpClientPage` (`pageManager.mcpClientPage`) additionally exposes plain-value readers for the same sidebar, none of which have a Flow wrapper: `getPrimitives(type: McpPrimitiveType): Promise` (row names of one kind; `McpPrimitiveType` adds `"resourceTemplates"`; **does not wait**, so an empty list is a valid answer — poll it with `expect.poll`, unlike `getTools()` which waits for >0), `getCapabilityErrorCount(type): Promise` (0 or 1 — use `expect.poll(...).toBe(0|1)` for "panel hidden/shown"), `hasCapability(type): Promise`, `hasCaret(type): Promise` (no caret on an empty capability item), `getCapabilityBounds(type)` / `getCapabilityErrorBounds(type)` (`{ y, height }`, for "panel does not overlap the next row"), `isCapabilityErrorScrollable(type): Promise`. +`McpClientPage` (`pageManager.mcpClientPage`) additionally exposes plain-value readers for the same sidebar, none of which have a Flow wrapper: `getPrimitives(type: McpPrimitiveType): Promise` (row names of one kind; `McpPrimitiveType` (`enums/mcp-capability.ts`) adds `ResourceTemplates`; every `type` argument here and in the Flow methods above takes the enum (`McpPrimitiveType.Tools`), never a `"tools"` string literal, and list-action assertions use `McpListAction.LoadMore`/`.Refresh`; **does not wait**, so an empty list is a valid answer — poll it with `expect.poll`, unlike `getTools()` which waits for >0), `getCapabilityErrorCount(type): Promise` (0 or 1 — use `expect.poll(...).toBe(0|1)` for "panel hidden/shown"), `hasCapability(type): Promise`, `hasCaret(type): Promise` (no caret on an empty capability item), `getCapabilityBounds(type)` / `getCapabilityErrorBounds(type)` (`{ y, height }`, for "panel does not overlap the next row"), `isCapabilityErrorScrollable(type): Promise`. **Scripting what the MCP server answers (`misc/mcp-list-server.ts`).** `misc/mcp-server.ts`'s regular `/mcp` endpoint only serves valid tools. For malformed / partial / error / paginated list responses, every test gets its own endpoint: `const url = await scriptMcpListServer(key, config)` (key unique per test, e.g. `faker.string.alphanumeric(12)`; returns the URL to give `new McpClient(name, url)`). `config` maps a list method (`"tools/list" | "resources/list" | "resources/templates/list" | "prompts/list"`) to `{ result: }`, `{ error: { code, message } }` (a JSON-RPC error), or `{ pages: { "": , "": } }`. Methods left out answer with a valid empty list. `config` also takes the flag `serverRequestReusingToolsListId: true`, which makes the `tools/list` reply an SSE stream: the result, then a server-initiated `roots/list` request reusing the `tools/list` request's JSON-RPC id (for testing id-collision handling). The `McpListMethod` type names the four list methods. Requests without an `id` **or without a `method`** get a bare 202. Call `scriptMcpListServer` again with the **same key** to switch behavior mid-test (e.g. between two Refresh clicks). Real examples: `tests/mcp-client/error-response/` (one spec per INS-3647 test-plan case; `helpers.ts` there holds `connectTo(user, config)` = script + create client + connect, plus `validTool` / `wholeFailureTitle` / `skippedTitle`). @@ -1579,6 +1579,8 @@ ContextMenuItem: Collection | CollaboratorRole: Owner | Admin | Member; // enums/collaborator-role.ts — InvitePage's per-row role-menu selector, OrganizationFlow.invite()'s Collaborator.role field DeleteMode: Local | Full; // enums/delete-mode.ts SyncStatus: Synced | Unsynced; // enums/sync-status.ts +McpPrimitiveType: Tools | Resources | Prompts | ResourceTemplates; // enums/mcp-capability.ts — values "tools"/"resources"/"prompts"/"resourceTemplates"; `McpCapabilityType` (same file, a type alias) = every member except ResourceTemplates +McpListAction: Refresh | LoadMore; // enums/mcp-capability.ts — "Refresh" / "Load More", the list action a capability item's menu offers ExportFormat: InsomniaV5 | Har; // enums/export-format.ts ApiSpecExportFormat: Yaml | Json; // enums/spec-format.ts or export-format.ts — see ExportPage ImportSource: File | Url | Curl | Clipboard | Mcp; diff --git a/packages/insomnia-test/enums/mcp-capability.ts b/packages/insomnia-test/enums/mcp-capability.ts new file mode 100644 index 0000000000..2e937ce9d1 --- /dev/null +++ b/packages/insomnia-test/enums/mcp-capability.ts @@ -0,0 +1,16 @@ +/** The primitive kinds a sidebar row can belong to (the prefix of its `data-key`). */ +export enum McpPrimitiveType { + Tools = "tools", + Resources = "resources", + Prompts = "prompts", + ResourceTemplates = "resourceTemplates", +} + +/** The capability items of the MCP sidebar; Resource Templates are listed under `resources`. */ +export type McpCapabilityType = Exclude; + +/** The list action a capability item's menu offers. */ +export enum McpListAction { + Refresh = "Refresh", + LoadMore = "Load More", +} diff --git a/packages/insomnia-test/flows/mcp-client.flow.ts b/packages/insomnia-test/flows/mcp-client.flow.ts index 401bace0b6..1c1e06a768 100644 --- a/packages/insomnia-test/flows/mcp-client.flow.ts +++ b/packages/insomnia-test/flows/mcp-client.flow.ts @@ -1,11 +1,12 @@ import { expect } from "@playwright/test"; +import { type McpCapabilityType, McpListAction } from "../enums/mcp-capability"; import { DEFAULT_TIMEOUT } from "../misc/constants"; import { McpClient } from "../models/mcp-client"; import type { Project } from "../models/project"; import type { ResponseView } from "../models/response"; import type { OAuth2Tokens } from "../pages/auth-tab.page"; -import type { McpCapabilityError, McpCapabilityType } from "../pages/mcp-client.page"; +import type { McpCapabilityError } from "../pages/mcp-client.page"; import type { PageManager } from "../pages/page-manager"; import { BaseFlow } from "./base.flow"; import type { FlowContext } from "./flow-context"; @@ -223,7 +224,7 @@ export class McpClientFlow extends BaseFlow { */ async refresh(client: McpClient, type: McpCapabilityType): Promise { await this.open(client); - await this.pageManager.mcpClientPage.clickCapabilityAction(type, "Refresh"); + await this.pageManager.mcpClientPage.clickCapabilityAction(type, McpListAction.Refresh); } /** @@ -235,7 +236,7 @@ export class McpClientFlow extends BaseFlow { */ async loadMore(client: McpClient, type: McpCapabilityType): Promise { await this.open(client); - await this.pageManager.mcpClientPage.clickCapabilityAction(type, "Load More"); + await this.pageManager.mcpClientPage.clickCapabilityAction(type, McpListAction.LoadMore); } /** @@ -245,7 +246,7 @@ export class McpClientFlow extends BaseFlow { * @param type - Which capability item's actions menu to read * @returns `Refresh`, or `Load More` while a `nextCursor` is pending */ - async getListAction(client: McpClient, type: McpCapabilityType): Promise { + async getListAction(client: McpClient, type: McpCapabilityType): Promise { await this.open(client); return this.pageManager.mcpClientPage.getCapabilityActionName(type); } diff --git a/packages/insomnia-test/pages/mcp-client.page.ts b/packages/insomnia-test/pages/mcp-client.page.ts index 2e9b36618a..57add998e2 100644 --- a/packages/insomnia-test/pages/mcp-client.page.ts +++ b/packages/insomnia-test/pages/mcp-client.page.ts @@ -1,13 +1,10 @@ import { expect, type Locator } from '@playwright/test'; +import type { McpListAction} from '../enums/mcp-capability'; +import { type McpCapabilityType, type McpPrimitiveType } from '../enums/mcp-capability'; import { ACTION_TIMEOUT, DEFAULT_TIMEOUT } from '../misc/constants'; import { RequestPage } from './request.page'; -/** The capability items of the MCP sidebar; Resource Templates are listed under `resources`. */ -export type McpCapabilityType = 'tools' | 'resources' | 'prompts'; -/** The primitive kinds a sidebar row can belong to (the prefix of its `data-key`). */ -export type McpPrimitiveType = McpCapabilityType | 'resourceTemplates'; - /** The error panel embedded in a capability item's row. */ export interface McpCapabilityError { title: string; @@ -245,7 +242,7 @@ export class McpClientPage extends RequestPage { * @param type - Which capability item's menu to open * @param action - `Refresh`, or `Load More` once the server returned a `nextCursor` */ - async clickCapabilityAction(type: McpCapabilityType, action: 'Refresh' | 'Load More'): Promise { + async clickCapabilityAction(type: McpCapabilityType, action: McpListAction): Promise { const capabilityItem = this.getCapabilityItem(type); await capabilityItem.hover({ position: { x: 60, y: 10 } }); await capabilityItem.locator(`[data-testid="Dropdown-${type}"]`).click(); @@ -258,12 +255,12 @@ export class McpClientPage extends RequestPage { * Opens the menu to read it and closes it again. * @param type - Which capability item's menu to read */ - async getCapabilityActionName(type: McpCapabilityType): Promise { + async getCapabilityActionName(type: McpCapabilityType): Promise { const capabilityItem = this.getCapabilityItem(type); await capabilityItem.hover({ position: { x: 60, y: 10 } }); await capabilityItem.locator(`[data-testid="Dropdown-${type}"]`).click(); const item = this.page.locator(this.ACTIONS_MENU).locator('[role^="menuitem"]').first(); - const name = (await item.innerText()).trim(); + const name = (await item.innerText()).trim() as McpListAction; await this.page.keyboard.press('Escape'); await expect(item).toBeHidden(); return name; diff --git a/packages/insomnia-test/tests/mcp-client/error-response/duplicate-invalid-entries.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/duplicate-invalid-entries.spec.ts index 98796fba37..7341c3fcec 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/duplicate-invalid-entries.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/duplicate-invalid-entries.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -19,6 +20,6 @@ test('Verify Invalid Entries With the Same Name and Issue Are All Listed', async const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await mcpClientFlow.getListError(client, 'tools'); + const error = await mcpClientFlow.getListError(client, McpPrimitiveType.Tools); expect(error.entries.map(entry => entry.label)).toEqual(['dup', 'dup']); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/entry-label-and-long-error.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/entry-label-and-long-error.spec.ts index cbb7810e12..ea531741b5 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/entry-label-and-long-error.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/entry-label-and-long-error.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -20,7 +21,7 @@ test('Verify an Entry Without a Usable Name Is Labeled by Its Position and Shows const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await mcpClientFlow.getListError(client, 'tools'); + const error = await mcpClientFlow.getListError(client, McpPrimitiveType.Tools); expect(error.entries.map(entry => entry.label)).toEqual(['index 1', 'index 2', 'index 3', 'index 4']); expect(error.entries[3].reason).toMatch(/\(And \d+ more\)$/); }); @@ -37,11 +38,11 @@ test('Verify a Long Error Panel Scrolls Inside Its Own Area Without Overlapping const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await mcpClientFlow.getListError(client, 'tools'); + const error = await mcpClientFlow.getListError(client, McpPrimitiveType.Tools); expect(error.entries).toHaveLength(12); - expect(await mcpClientPage.isCapabilityErrorScrollable('tools')).toBe(true); + expect(await mcpClientPage.isCapabilityErrorScrollable(McpPrimitiveType.Tools)).toBe(true); - const panelBox = await mcpClientPage.getCapabilityErrorBounds('tools'); - const nextCapabilityBox = await mcpClientPage.getCapabilityBounds('prompts'); + const panelBox = await mcpClientPage.getCapabilityErrorBounds(McpPrimitiveType.Tools); + const nextCapabilityBox = await mcpClientPage.getCapabilityBounds(McpPrimitiveType.Prompts); expect(panelBox.y + panelBox.height).toBeLessThanOrEqual(nextCapabilityBox.y + 1); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/error-panel-expand-collapse.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/error-panel-expand-collapse.spec.ts index 8f12e38b3b..ccadbc997f 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/error-panel-expand-collapse.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/error-panel-expand-collapse.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -19,18 +20,18 @@ test('Verify the Error Panel of a Partially Invalid List Follows Expand and Coll const project = await workspaceFlow.create(new Project(faker.string.alphanumeric(10), ProjectType.Local)); const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - await mcpClientFlow.getListError(client, 'tools'); - await expect.poll(() => mcpClientPage.hasCapability('prompts')).toBe(true); + await mcpClientFlow.getListError(client, McpPrimitiveType.Tools); + await expect.poll(() => mcpClientPage.hasCapability(McpPrimitiveType.Prompts)).toBe(true); - const panelBox = await mcpClientPage.getCapabilityErrorBounds('tools'); - const nextCapabilityBox = await mcpClientPage.getCapabilityBounds('prompts'); + const panelBox = await mcpClientPage.getCapabilityErrorBounds(McpPrimitiveType.Tools); + const nextCapabilityBox = await mcpClientPage.getCapabilityBounds(McpPrimitiveType.Prompts); expect(panelBox.y + panelBox.height).toBeLessThanOrEqual(nextCapabilityBox.y + 1); - await mcpClientFlow.toggleCapability(client, 'tools'); - await expect.poll(() => mcpClientPage.getCapabilityErrorCount('tools')).toBe(0); + await mcpClientFlow.toggleCapability(client, McpPrimitiveType.Tools); + await expect.poll(() => mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Tools)).toBe(0); - await mcpClientFlow.toggleCapability(client, 'tools'); - await expect.poll(() => mcpClientPage.getCapabilityErrorCount('tools')).toBe(1); + await mcpClientFlow.toggleCapability(client, McpPrimitiveType.Tools); + await expect.poll(() => mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Tools)).toBe(1); }); test('Verify the Error Panel of an Empty List Stays Visible Without a Collapse Caret', async ({ user }) => { @@ -42,9 +43,9 @@ test('Verify the Error Panel of an Empty List Stays Visible Without a Collapse C const project = await workspaceFlow.create(new Project(faker.string.alphanumeric(10), ProjectType.Local)); const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - await mcpClientFlow.getListError(client, 'tools'); + await mcpClientFlow.getListError(client, McpPrimitiveType.Tools); - expect(await mcpClientPage.hasCaret('tools')).toBe(false); - await mcpClientFlow.toggleCapability(client, 'tools'); - await expect.poll(() => mcpClientPage.getCapabilityErrorCount('tools')).toBe(1); + expect(await mcpClientPage.hasCaret(McpPrimitiveType.Tools)).toBe(false); + await mcpClientFlow.toggleCapability(client, McpPrimitiveType.Tools); + await expect.poll(() => mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Tools)).toBe(1); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error-with-json-message.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error-with-json-message.spec.ts index 2a701229aa..bb8395d779 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error-with-json-message.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error-with-json-message.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -15,7 +16,7 @@ test('Verify a JSON-RPC Error Whose Message Is a JSON String Is Shown Readably', const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await user.flowManager.mcpClientFlow.getListError(client, 'tools'); + const error = await user.flowManager.mcpClientFlow.getListError(client, McpPrimitiveType.Tools); expect(error.title).toContain('boom'); expect(error.title).not.toContain('[object Object]'); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error.spec.ts index 3a8a227384..10dc0f9dd7 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/jsonrpc-error.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -21,7 +22,7 @@ for (const rpcError of jsonRpcErrors) { const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await user.flowManager.mcpClientFlow.getListError(client, 'tools'); + const error = await user.flowManager.mcpClientFlow.getListError(client, McpPrimitiveType.Tools); expect(error.title).toContain(rpcError.message); expect(error.entries).toEqual([]); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-entry-reports-error.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-entry-reports-error.spec.ts index 18ac749ef4..823918cd45 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-entry-reports-error.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-entry-reports-error.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -32,13 +33,13 @@ test('Verify Load More Whose Next Page Has an Invalid Entry Lists the Skipped En const project = await workspaceFlow.create(new Project(faker.string.alphanumeric(10), ProjectType.Local)); const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - await expect.poll(() => mcpClientPage.getPrimitives('resources'), { timeout: DEFAULT_TIMEOUT }).toEqual(['item-1']); + await expect.poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Resources), { timeout: DEFAULT_TIMEOUT }).toEqual(['item-1']); - await mcpClientFlow.loadMore(client, 'resources'); + await mcpClientFlow.loadMore(client, McpPrimitiveType.Resources); await expect - .poll(() => mcpClientPage.getPrimitives('resources'), { timeout: DEFAULT_TIMEOUT }) + .poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Resources), { timeout: DEFAULT_TIMEOUT }) .toEqual(['item-1', 'item-2']); - const error = await mcpClientFlow.getListError(client, 'resources'); + const error = await mcpClientFlow.getListError(client, McpPrimitiveType.Resources); expect(error.entries.map(entry => entry.label)).toEqual(['bad-resource']); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-page.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-page.spec.ts index 1448f5db59..2fb81926ac 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-page.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/load-more-bad-page.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpListAction, McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -8,19 +9,19 @@ import { Project } from '../../../models/project'; const loadMoreVariants = [ { - type: 'tools', + type: McpPrimitiveType.Tools, method: 'tools/list', entry: (name: string) => ({ name, inputSchema: { type: 'object' } }), badEntry: { name: 'bad-tool' }, }, { - type: 'prompts', + type: McpPrimitiveType.Prompts, method: 'prompts/list', entry: (name: string) => ({ name }), badEntry: { name: 'bad-prompt', arguments: 'x' }, }, { - type: 'resources', + type: McpPrimitiveType.Resources, method: 'resources/list', entry: (name: string) => ({ name, uri: `file:///${name}.txt` }), badEntry: { name: 'bad-resource' }, @@ -30,8 +31,8 @@ const loadMoreVariants = [ // Open app bugs: listTools() / listPrompts() do not forward the cursor, so Load More re-requests the first page. // Resources forwards it and passes. const knownBugs: Partial> = { - tools: 'INS-4060', - prompts: 'INS-4061', + [McpPrimitiveType.Tools]: 'INS-4060', + [McpPrimitiveType.Prompts]: 'INS-4061', }; for (const { type, method, entry, badEntry } of loadMoreVariants) { @@ -51,12 +52,12 @@ for (const { type, method, entry, badEntry } of loadMoreVariants) { const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); await expect.poll(() => mcpClientPage.getPrimitives(type), { timeout: DEFAULT_TIMEOUT }).toEqual(['item-1']); - expect(await mcpClientFlow.getListAction(client, type)).toBe('Load More'); + expect(await mcpClientFlow.getListAction(client, type)).toBe(McpListAction.LoadMore); await mcpClientFlow.loadMore(client, type); await expect .poll(() => mcpClientPage.getPrimitives(type), { timeout: DEFAULT_TIMEOUT }) .toEqual(['item-1', 'item-2']); - expect(await mcpClientFlow.getListAction(client, type)).toBe('Refresh'); + expect(await mcpClientFlow.getListAction(client, type)).toBe(McpListAction.Refresh); }); } diff --git a/packages/insomnia-test/tests/mcp-client/error-response/partial-response-invalid.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/partial-response-invalid.spec.ts index c596f98405..2f9edcbb44 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/partial-response-invalid.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/partial-response-invalid.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -31,14 +32,14 @@ for (const [variant, { tools, skipped }] of Object.entries(partialInvalidTools)) const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await mcpClientFlow.getListError(client, 'tools'); + const error = await mcpClientFlow.getListError(client, McpPrimitiveType.Tools); expect(error.title).toMatch(skippedTitle(2)); expect(error.entries.map(entry => entry.label)).toEqual(skipped); for (const entry of error.entries) { expect(entry.reason).not.toBe(''); } await expect - .poll(() => mcpClientPage.getPrimitives('tools'), { + .poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Tools), { timeout: DEFAULT_TIMEOUT, }) .toEqual(['valid-tool']); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/refresh-clears-error.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/refresh-clears-error.spec.ts index 79d2044a20..4bc3a710ce 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/refresh-clears-error.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/refresh-clears-error.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { type McpListConfig, scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -19,14 +20,14 @@ test('Verify Refresh Clears the Error Once the Server Returns Valid Data', async ); const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - await mcpClientFlow.getListError(client, 'tools'); + await mcpClientFlow.getListError(client, McpPrimitiveType.Tools); await scriptMcpListServer(key, valid); - await mcpClientFlow.refresh(client, 'tools'); - await expect.poll(() => mcpClientPage.getCapabilityErrorCount('tools')).toBe(0); - await expect.poll(() => mcpClientPage.getPrimitives('tools'), { timeout: DEFAULT_TIMEOUT }).toEqual(['valid-tool']); + await mcpClientFlow.refresh(client, McpPrimitiveType.Tools); + await expect.poll(() => mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Tools)).toBe(0); + await expect.poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Tools), { timeout: DEFAULT_TIMEOUT }).toEqual(['valid-tool']); await scriptMcpListServer(key, invalid); - await mcpClientFlow.refresh(client, 'tools'); - expect((await mcpClientFlow.getListError(client, 'tools')).title).toBe(wholeFailureTitle('tools/list')); + await mcpClientFlow.refresh(client, McpPrimitiveType.Tools); + expect((await mcpClientFlow.getListError(client, McpPrimitiveType.Tools)).title).toBe(wholeFailureTitle('tools/list')); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/refresh-resources-clears-templates-error.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/refresh-resources-clears-templates-error.spec.ts index c0dffe6d52..3a52c2e497 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/refresh-resources-clears-templates-error.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/refresh-resources-clears-templates-error.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -23,7 +24,7 @@ test('Verify Refresh on Resources Clears a Resource Templates Error Once the Ser const project = await workspaceFlow.create(new Project(faker.string.alphanumeric(10), ProjectType.Local)); const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - await mcpClientFlow.getListError(client, 'resources'); + await mcpClientFlow.getListError(client, McpPrimitiveType.Resources); await scriptMcpListServer(key, { 'resources/list': resources, @@ -31,7 +32,7 @@ test('Verify Refresh on Resources Clears a Resource Templates Error Once the Ser result: { resourceTemplates: [{ uriTemplate: 'file:///{path}', name: 'valid-template' }] }, }, }); - await mcpClientFlow.refresh(client, 'resources'); + await mcpClientFlow.refresh(client, McpPrimitiveType.Resources); - await expect.poll(() => mcpClientPage.getCapabilityErrorCount('resources'), { timeout: DEFAULT_TIMEOUT }).toBe(0); + await expect.poll(() => mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Resources), { timeout: DEFAULT_TIMEOUT }).toBe(0); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/resources-and-prompts-invalid.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/resources-and-prompts-invalid.spec.ts index 1ee7b51e50..7d599cea76 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/resources-and-prompts-invalid.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/resources-and-prompts-invalid.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -26,20 +27,20 @@ test('Verify Invalid resources/list and prompts/list Are Reported Like tools/lis const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const resourcesError = await mcpClientFlow.getListError(client, 'resources'); + const resourcesError = await mcpClientFlow.getListError(client, McpPrimitiveType.Resources); expect(resourcesError.title).toMatch(skippedTitle(2)); expect(resourcesError.entries.map(entry => entry.label)).toEqual(['no-uri-resource', 'numeric-uri-resource']); await expect - .poll(() => mcpClientPage.getPrimitives('resources'), { + .poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Resources), { timeout: DEFAULT_TIMEOUT, }) .toEqual(['valid-resource']); - expect(await mcpClientFlow.getListError(client, 'prompts')).toEqual({ + expect(await mcpClientFlow.getListError(client, McpPrimitiveType.Prompts)).toEqual({ title: wholeFailureTitle('prompts/list'), entries: [], }); - expect(await mcpClientPage.getPrimitives('prompts')).toEqual([]); + expect(await mcpClientPage.getPrimitives(McpPrimitiveType.Prompts)).toEqual([]); }); test('Verify a prompts/list Whose prompts Is an Object Is a Whole-Response Error', async ({ user }) => { @@ -51,7 +52,7 @@ test('Verify a prompts/list Whose prompts Is an Object Is a Whole-Response Error const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - expect(await user.flowManager.mcpClientFlow.getListError(client, 'prompts')).toEqual({ + expect(await user.flowManager.mcpClientFlow.getListError(client, McpPrimitiveType.Prompts)).toEqual({ title: wholeFailureTitle('prompts/list'), entries: [], }); @@ -70,11 +71,11 @@ test('Verify a Prompt with Bad arguments Is Skipped While Valid Prompts Are List const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await mcpClientFlow.getListError(client, 'prompts'); + const error = await mcpClientFlow.getListError(client, McpPrimitiveType.Prompts); expect(error.title).toMatch(skippedTitle(1)); expect(error.entries.map(entry => entry.label)).toEqual(['bad-args-prompt']); await expect - .poll(() => user.pageManager.mcpClientPage.getPrimitives('prompts'), { + .poll(() => user.pageManager.mcpClientPage.getPrimitives(McpPrimitiveType.Prompts), { timeout: DEFAULT_TIMEOUT, }) .toEqual(['valid-prompt']); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/server-request-reusing-list-request-id.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/server-request-reusing-list-request-id.spec.ts index 4e5809b3d8..2df17676a7 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/server-request-reusing-list-request-id.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/server-request-reusing-list-request-id.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -21,6 +22,6 @@ test('Verify a Server-Initiated Request Reusing the tools/list Request Id Does N const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - await expect.poll(() => mcpClientPage.getPrimitives('tools'), { timeout: DEFAULT_TIMEOUT }).toEqual(['valid-tool']); - expect(await mcpClientPage.getCapabilityErrorCount('tools')).toBe(0); + await expect.poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Tools), { timeout: DEFAULT_TIMEOUT }).toEqual(['valid-tool']); + expect(await mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Tools)).toBe(0); }); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/shared-resources-capability-error.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/shared-resources-capability-error.spec.ts index decd11c0bf..9205f1cf6b 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/shared-resources-capability-error.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/shared-resources-capability-error.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { DEFAULT_TIMEOUT, expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -22,13 +23,13 @@ test('Verify Resources and Resource Templates Errors Share One Capability Item P const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - const error = await mcpClientFlow.getListError(client, 'resources'); - await expect.poll(() => mcpClientPage.getCapabilityErrorCount('resources')).toBe(1); + const error = await mcpClientFlow.getListError(client, McpPrimitiveType.Resources); + await expect.poll(() => mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Resources)).toBe(1); expect(error.title).toContain(wholeFailureTitle('resources/list')); expect(error.title).toContain('skipped because they do not match the MCP schema'); expect(error.entries.map(entry => entry.label)).toEqual(['no-uri-template']); await expect - .poll(() => mcpClientPage.getPrimitives('resourceTemplates'), { + .poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.ResourceTemplates), { timeout: DEFAULT_TIMEOUT, }) .toEqual(['valid-template']); diff --git a/packages/insomnia-test/tests/mcp-client/error-response/whole-response-invalid.spec.ts b/packages/insomnia-test/tests/mcp-client/error-response/whole-response-invalid.spec.ts index cea02d9f4c..16145d9fcf 100644 --- a/packages/insomnia-test/tests/mcp-client/error-response/whole-response-invalid.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/error-response/whole-response-invalid.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; +import { McpPrimitiveType } from '../../../enums/mcp-capability'; import { ProjectType } from '../../../enums/project-types'; import { expect, test } from '../../../misc/fixtures'; import { scriptMcpListServer } from '../../../misc/mcp-list-server'; @@ -21,11 +22,11 @@ for (const [variant, result] of Object.entries(wholeInvalidTools)) { const client = await mcpClientFlow.create(project, new McpClient(faker.string.alphanumeric(10), url)); await mcpClientFlow.connect(client); - expect(await mcpClientFlow.getListError(client, 'tools')).toEqual({ + expect(await mcpClientFlow.getListError(client, McpPrimitiveType.Tools)).toEqual({ title: wholeFailureTitle('tools/list'), entries: [], }); - expect(await user.pageManager.mcpClientPage.getPrimitives('tools')).toEqual([]); + expect(await user.pageManager.mcpClientPage.getPrimitives(McpPrimitiveType.Tools)).toEqual([]); }); } @@ -39,8 +40,8 @@ test('Verify a tools/list Result That Is Not an Object Shows an Error and Lists // The MCP SDK rejects a non-object `result` before the pane's own // validation runs, so the title is the SDK's ZodError, not the agreed // "does not match the MCP schema" wording. - const error = await user.flowManager.mcpClientFlow.getListError(client, 'tools'); + const error = await user.flowManager.mcpClientFlow.getListError(client, McpPrimitiveType.Tools); expect(error.title).not.toBe(''); expect(error.entries).toEqual([]); - expect(await user.pageManager.mcpClientPage.getPrimitives('tools')).toEqual([]); + expect(await user.pageManager.mcpClientPage.getPrimitives(McpPrimitiveType.Tools)).toEqual([]); }); diff --git a/packages/insomnia-test/tests/mcp-client/happy-path.spec.ts b/packages/insomnia-test/tests/mcp-client/happy-path.spec.ts index 4aeadb5534..8f01504161 100644 --- a/packages/insomnia-test/tests/mcp-client/happy-path.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/happy-path.spec.ts @@ -1,5 +1,6 @@ import { faker } from "@faker-js/faker"; +import { McpPrimitiveType } from "../../enums/mcp-capability"; import { ProjectType } from "../../enums/project-types"; import { DEFAULT_TIMEOUT,expect, MCP_SERVER, test } from "../../misc/fixtures"; import { McpClient } from "../../models/mcp-client"; @@ -38,7 +39,7 @@ test("Verify Create MCP Client", async ({ user }) => { ); await mcpClientFlow.connect(mcpClient); const tools = await mcpClientFlow.getTools(mcpClient); - expect(await user.pageManager.mcpClientPage.getCapabilityErrorCount("tools")).toBe(0); + expect(await user.pageManager.mcpClientPage.getCapabilityErrorCount(McpPrimitiveType.Tools)).toBe(0); expect(tools).toEqual( expect.arrayContaining([ "read_wiki_structure", diff --git a/packages/insomnia-test/tests/mcp-client/load-more-survives-switching-request.spec.ts b/packages/insomnia-test/tests/mcp-client/load-more-survives-switching-request.spec.ts index 984308a38f..96698f3909 100644 --- a/packages/insomnia-test/tests/mcp-client/load-more-survives-switching-request.spec.ts +++ b/packages/insomnia-test/tests/mcp-client/load-more-survives-switching-request.spec.ts @@ -1,5 +1,6 @@ import { faker } from "@faker-js/faker"; +import { McpPrimitiveType } from "../../enums/mcp-capability"; import { ProjectType } from "../../enums/project-types"; import { DEFAULT_TIMEOUT, expect, MCP_SERVER, test } from "../../misc/fixtures"; import { scriptMcpListServer } from "../../misc/mcp-list-server"; @@ -39,9 +40,9 @@ test("Verify Loaded Pages Are Kept After Switching to Another MCP Client and Bac new McpClient(faker.string.alphanumeric(10), url), ); await mcpClientFlow.connect(client); - await mcpClientFlow.loadMore(client, "resources"); + await mcpClientFlow.loadMore(client, McpPrimitiveType.Resources); await expect - .poll(() => mcpClientPage.getPrimitives("resources"), { + .poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Resources), { timeout: DEFAULT_TIMEOUT, }) .toEqual(["item-1", "item-2"]); @@ -50,10 +51,10 @@ test("Verify Loaded Pages Are Kept After Switching to Another MCP Client and Bac project, new McpClient(faker.string.alphanumeric(10), MCP_SERVER), ); - await mcpClientFlow.getListAction(client, "resources"); + await mcpClientFlow.getListAction(client, McpPrimitiveType.Resources); await expect - .poll(() => mcpClientPage.getPrimitives("resources"), { + .poll(() => mcpClientPage.getPrimitives(McpPrimitiveType.Resources), { timeout: DEFAULT_TIMEOUT, }) .toEqual(["item-1", "item-2"]);