@frontmcp/testing 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/auth/mock-api-server.d.ts +99 -0
- package/src/auth/mock-api-server.js +200 -0
- package/src/auth/mock-api-server.js.map +1 -0
- package/src/auth/mock-oauth-server.d.ts +85 -0
- package/src/auth/mock-oauth-server.js +253 -0
- package/src/auth/mock-oauth-server.js.map +1 -0
- package/src/client/mcp-test-client.builder.d.ts +43 -1
- package/src/client/mcp-test-client.builder.js +52 -0
- package/src/client/mcp-test-client.builder.js.map +1 -1
- package/src/client/mcp-test-client.js +22 -14
- package/src/client/mcp-test-client.js.map +1 -1
- package/src/client/mcp-test-client.types.d.ts +67 -6
- package/src/client/mcp-test-client.types.js +9 -0
- package/src/client/mcp-test-client.types.js.map +1 -1
- package/src/example-tools/index.d.ts +19 -0
- package/src/example-tools/index.js +40 -0
- package/src/example-tools/index.js.map +1 -0
- package/src/example-tools/tool-configs.d.ts +170 -0
- package/src/example-tools/tool-configs.js +222 -0
- package/src/example-tools/tool-configs.js.map +1 -0
- package/src/expect.d.ts +6 -5
- package/src/expect.js.map +1 -1
- package/src/fixtures/fixture-types.d.ts +19 -0
- package/src/fixtures/fixture-types.js.map +1 -1
- package/src/fixtures/test-fixture.d.ts +3 -1
- package/src/fixtures/test-fixture.js +35 -4
- package/src/fixtures/test-fixture.js.map +1 -1
- package/src/index.d.ts +7 -0
- package/src/index.js +40 -1
- package/src/index.js.map +1 -1
- package/src/matchers/matcher-types.js.map +1 -1
- package/src/matchers/mcp-matchers.d.ts +7 -0
- package/src/matchers/mcp-matchers.js +8 -4
- package/src/matchers/mcp-matchers.js.map +1 -1
- package/src/platform/index.d.ts +28 -0
- package/src/platform/index.js +47 -0
- package/src/platform/index.js.map +1 -0
- package/src/platform/platform-client-info.d.ts +97 -0
- package/src/platform/platform-client-info.js +155 -0
- package/src/platform/platform-client-info.js.map +1 -0
- package/src/platform/platform-types.d.ts +72 -0
- package/src/platform/platform-types.js +110 -0
- package/src/platform/platform-types.js.map +1 -0
- package/src/server/test-server.d.ts +4 -0
- package/src/server/test-server.js +58 -3
- package/src/server/test-server.js.map +1 -1
- package/src/transport/streamable-http.transport.js +6 -0
- package/src/transport/streamable-http.transport.js.map +1 -1
- package/src/transport/transport.interface.d.ts +3 -0
- package/src/transport/transport.interface.js.map +1 -1
- package/src/ui/ui-assertions.d.ts +59 -0
- package/src/ui/ui-assertions.js +152 -0
- package/src/ui/ui-assertions.js.map +1 -1
- package/src/ui/ui-matchers.d.ts +8 -0
- package/src/ui/ui-matchers.js +218 -0
- package/src/ui/ui-matchers.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-matchers.js","sourceRoot":"","sources":["../../../src/matchers/mcp-matchers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file mcp-matchers.ts\n * @description Custom Jest matchers for MCP testing\n *\n * @example\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test('tools work', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n *\n * const result = await mcp.tools.call('my-tool', {});\n * expect(result).toBeSuccessful();\n * expect(result).toHaveTextContent();\n * });\n * ```\n */\n\nimport type { MatcherFunction } from 'expect';\nimport type { Tool, Resource, ResourceTemplate, Prompt } from '@modelcontextprotocol/sdk/types.js';\nimport type { ToolResultWrapper, ResourceContentWrapper, PromptResultWrapper } from '../client/mcp-test-client.types';\nimport { uiMatchers } from '../ui/ui-matchers';\n\n// ═══════════════════════════════════════════════════════════════════\n// HELPER TYPES\n// ═══════════════════════════════════════════════════════════════════\n\ntype ResultWrapper = ToolResultWrapper | ResourceContentWrapper;\n\n// ═══════════════════════════════════════════════════════════════════\n// TOOL MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if tools array contains a tool with the given name\n */\nconst toContainTool: MatcherFunction<[toolName: string]> = function (received, toolName) {\n const tools = received as Tool[];\n\n if (!Array.isArray(tools)) {\n return {\n pass: false,\n message: () => `Expected an array of tools, but received ${typeof received}`,\n };\n }\n\n const pass = tools.some((t) => t.name === toolName);\n const availableTools = tools.map((t) => t.name).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected tools not to contain \"${toolName}\"`\n : `Expected tools to contain \"${toolName}\", but got: [${availableTools}]`,\n };\n};\n\n/**\n * Check if result is successful (not an error)\n */\nconst toBeSuccessful: MatcherFunction<[]> = function (received) {\n const result = received as ResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('isSuccess' in result)) {\n return {\n pass: false,\n message: () => `Expected a result wrapper object with isSuccess property`,\n };\n }\n\n const pass = result.isSuccess;\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected result not to be successful'\n : `Expected result to be successful, but got error: ${result.error?.message ?? 'unknown error'}`,\n };\n};\n\n/**\n * Check if result is an error, optionally with a specific error code\n */\nconst toBeError: MatcherFunction<[expectedCode?: number]> = function (received, expectedCode) {\n const result = received as ResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('isError' in result)) {\n return {\n pass: false,\n message: () => `Expected a result wrapper object with isError property`,\n };\n }\n\n let pass = result.isError;\n\n if (pass && expectedCode !== undefined) {\n pass = result.error?.code === expectedCode;\n }\n\n return {\n pass,\n message: () => {\n if (!result.isError) {\n return 'Expected result to be an error, but it was successful';\n }\n if (expectedCode !== undefined && result.error?.code !== expectedCode) {\n return `Expected error code ${expectedCode}, but got ${result.error?.code}`;\n }\n return 'Expected result not to be an error';\n },\n };\n};\n\n/**\n * Check if tool result has text content, optionally containing specific text\n */\nconst toHaveTextContent: MatcherFunction<[expectedText?: string]> = function (received, expectedText) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasTextContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasTextContent method`,\n };\n }\n\n const hasText = result.hasTextContent();\n const text = result.text();\n let pass = hasText;\n\n if (pass && expectedText !== undefined) {\n pass = text?.includes(expectedText) ?? false;\n }\n\n return {\n pass,\n message: () => {\n if (!hasText) {\n return 'Expected result to have text content';\n }\n if (expectedText !== undefined && !text?.includes(expectedText)) {\n return `Expected text to contain \"${expectedText}\", but got: \"${text}\"`;\n }\n return 'Expected result not to have text content';\n },\n };\n};\n\n/**\n * Check if tool result has image content\n */\nconst toHaveImageContent: MatcherFunction<[]> = function (received) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasImageContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasImageContent method`,\n };\n }\n\n const pass = result.hasImageContent();\n\n return {\n pass,\n message: () => (pass ? 'Expected result not to have image content' : 'Expected result to have image content'),\n };\n};\n\n/**\n * Check if tool result has resource content\n */\nconst toHaveResourceContent: MatcherFunction<[]> = function (received) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasResourceContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasResourceContent method`,\n };\n }\n\n const pass = result.hasResourceContent();\n\n return {\n pass,\n message: () => (pass ? 'Expected result not to have resource content' : 'Expected result to have resource content'),\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// RESOURCE MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if resources array contains a resource with the given URI\n */\nconst toContainResource: MatcherFunction<[uri: string]> = function (received, uri) {\n const resources = received as Resource[];\n\n if (!Array.isArray(resources)) {\n return {\n pass: false,\n message: () => `Expected an array of resources, but received ${typeof received}`,\n };\n }\n\n const pass = resources.some((r) => r.uri === uri);\n const availableUris = resources.map((r) => r.uri).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected resources not to contain \"${uri}\"`\n : `Expected resources to contain \"${uri}\", but got: [${availableUris}]`,\n };\n};\n\n/**\n * Check if resource templates array contains a template with the given URI template\n */\nconst toContainResourceTemplate: MatcherFunction<[uriTemplate: string]> = function (received, uriTemplate) {\n const templates = received as ResourceTemplate[];\n\n if (!Array.isArray(templates)) {\n return {\n pass: false,\n message: () => `Expected an array of resource templates, but received ${typeof received}`,\n };\n }\n\n const pass = templates.some((t) => t.uriTemplate === uriTemplate);\n const availableTemplates = templates.map((t) => t.uriTemplate).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected templates not to contain \"${uriTemplate}\"`\n : `Expected templates to contain \"${uriTemplate}\", but got: [${availableTemplates}]`,\n };\n};\n\n/**\n * Check if resource content has a specific MIME type\n */\nconst toHaveMimeType: MatcherFunction<[mimeType: string]> = function (received, mimeType) {\n const result = received as ResourceContentWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasMimeType' in result)) {\n return {\n pass: false,\n message: () => `Expected a ResourceContentWrapper object with hasMimeType method`,\n };\n }\n\n const pass = result.hasMimeType(mimeType);\n const actualMimeType = result.mimeType();\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected content not to have MIME type \"${mimeType}\"`\n : `Expected MIME type \"${mimeType}\", but got \"${actualMimeType}\"`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// PROMPT MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if prompts array contains a prompt with the given name\n */\nconst toContainPrompt: MatcherFunction<[name: string]> = function (received, name) {\n const prompts = received as Prompt[];\n\n if (!Array.isArray(prompts)) {\n return {\n pass: false,\n message: () => `Expected an array of prompts, but received ${typeof received}`,\n };\n }\n\n const pass = prompts.some((p) => p.name === name);\n const availablePrompts = prompts.map((p) => p.name).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected prompts not to contain \"${name}\"`\n : `Expected prompts to contain \"${name}\", but got: [${availablePrompts}]`,\n };\n};\n\n/**\n * Check if prompt result has a specific number of messages\n */\nconst toHaveMessages: MatcherFunction<[count: number]> = function (received, count) {\n const result = received as PromptResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('messages' in result)) {\n return {\n pass: false,\n message: () => `Expected a PromptResultWrapper object with messages property`,\n };\n }\n\n const actualCount = result.messages?.length ?? 0;\n const pass = actualCount === count;\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected prompt not to have ${count} messages`\n : `Expected prompt to have ${count} messages, but got ${actualCount}`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// PROTOCOL MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if response is valid JSON-RPC 2.0\n * A valid JSON-RPC 2.0 response must have:\n * - jsonrpc: \"2.0\"\n * - id (matching the request, can be null for notifications)\n * - Either result OR error (but not both)\n */\nconst toBeValidJsonRpc: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const hasJsonRpc = response['jsonrpc'] === '2.0';\n const hasId = 'id' in response;\n const hasResult = 'result' in response;\n const hasError = 'error' in response;\n const hasExactlyOneResultOrError = (hasResult || hasError) && !(hasResult && hasError);\n\n const pass = hasJsonRpc && hasId && hasExactlyOneResultOrError;\n\n return {\n pass,\n message: () => {\n if (pass) {\n return 'Expected response not to be valid JSON-RPC';\n }\n const issues: string[] = [];\n if (!hasJsonRpc) issues.push('missing or invalid \"jsonrpc\": \"2.0\"');\n if (!hasId) issues.push('missing \"id\" field');\n if (!hasExactlyOneResultOrError) {\n if (!hasResult && !hasError) issues.push('missing \"result\" or \"error\"');\n else issues.push('cannot have both \"result\" and \"error\"');\n }\n return `Expected valid JSON-RPC 2.0 response: ${issues.join(', ')}`;\n },\n };\n};\n\n/**\n * Check if JSON-RPC response has a result\n */\nconst toHaveResult: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const pass = 'result' in response;\n\n return {\n pass,\n message: () => (pass ? 'Expected response not to have result' : 'Expected response to have result'),\n };\n};\n\n/**\n * Check if JSON-RPC response has an error\n */\nconst toHaveError: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const pass = 'error' in response;\n\n return {\n pass,\n message: () => (pass ? 'Expected response not to have error' : 'Expected response to have error'),\n };\n};\n\n/**\n * Check if JSON-RPC response has a specific error code\n */\nconst toHaveErrorCode: MatcherFunction<[code: number]> = function (received, code) {\n const response = received as { error?: { code: number } };\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const actualCode = response.error?.code;\n const pass = actualCode === code;\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected response not to have error code ${code}`\n : `Expected error code ${code}, but got ${actualCode ?? 'no error'}`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * All MCP matchers as an object for expect.extend()\n */\nexport const mcpMatchers = {\n // Tool matchers\n toContainTool,\n toBeSuccessful,\n toBeError,\n toHaveTextContent,\n toHaveImageContent,\n toHaveResourceContent,\n\n // Resource matchers\n toContainResource,\n toContainResourceTemplate,\n toHaveMimeType,\n\n // Prompt matchers\n toContainPrompt,\n toHaveMessages,\n\n // Protocol matchers\n toBeValidJsonRpc,\n toHaveResult,\n toHaveError,\n toHaveErrorCode,\n\n // UI matchers (for testing tool UI responses)\n ...uiMatchers,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-matchers.js","sourceRoot":"","sources":["../../../src/matchers/mcp-matchers.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAKH,mDAA+C;AAQ/C,sEAAsE;AACtE,gBAAgB;AAChB,sEAAsE;AAEtE;;GAEG;AACH,MAAM,aAAa,GAAwC,UAAU,QAAQ,EAAE,QAAQ;IACrF,MAAM,KAAK,GAAG,QAAkB,CAAC;IAEjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,4CAA4C,OAAO,QAAQ,EAAE;SAC7E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IACpD,MAAM,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE3D,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,kCAAkC,QAAQ,GAAG;YAC/C,CAAC,CAAC,8BAA8B,QAAQ,gBAAgB,cAAc,GAAG;KAC9E,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAwB,UAAU,QAAQ;IAC5D,MAAM,MAAM,GAAG,QAAyB,CAAC;IAEzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,WAAW,IAAI,MAAM,CAAC,EAAE,CAAC;QAC9E,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,0DAA0D;SAC1E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;IAE9B,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,sCAAsC;YACxC,CAAC,CAAC,oDAAoD,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe,EAAE;KACrG,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,SAAS,GAA6C,UAAU,QAAQ,EAAE,YAAY;IAC1F,MAAM,MAAM,GAAG,QAAyB,CAAC;IAEzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,EAAE,CAAC;QAC5E,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,wDAAwD;SACxE,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC;IAE1B,IAAI,IAAI,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QACvC,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,KAAK,YAAY,CAAC;IAC7C,CAAC;IAED,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,uDAAuD,CAAC;YACjE,CAAC;YACD,IAAI,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,KAAK,YAAY,EAAE,CAAC;gBACtE,OAAO,uBAAuB,YAAY,aAAa,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;YAC9E,CAAC;YACD,OAAO,oCAAoC,CAAC;QAC9C,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,iBAAiB,GAA6C,UAAU,QAAQ,EAAE,YAAY;IAClG,MAAM,MAAM,GAAG,QAAsD,CAAC;IAEtE,0DAA0D;IAC1D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,EAAE,CAAC;QACzE,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,gFAAgF;SAChG,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAE3B,qHAAqH;IACrH,MAAM,OAAO,GAAG,gBAAgB,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC;IAE1F,IAAI,IAAI,GAAG,OAAO,CAAC;IAEnB,IAAI,IAAI,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QACvC,IAAI,GAAG,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC;IAC/C,CAAC;IAED,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,sCAAsC,CAAC;YAChD,CAAC;YACD,IAAI,YAAY,KAAK,SAAS,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAChE,OAAO,6BAA6B,YAAY,gBAAgB,IAAI,GAAG,CAAC;YAC1E,CAAC;YACD,OAAO,0CAA0C,CAAC;QACpD,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,kBAAkB,GAAwB,UAAU,QAAQ;IAChE,MAAM,MAAM,GAAG,QAA6B,CAAC;IAE7C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,iBAAiB,IAAI,MAAM,CAAC,EAAE,CAAC;QACpF,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,iEAAiE;SACjF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;IAEtC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC,CAAC,uCAAuC,CAAC;KAC9G,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,qBAAqB,GAAwB,UAAU,QAAQ;IACnE,MAAM,MAAM,GAAG,QAA6B,CAAC;IAE7C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,oBAAoB,IAAI,MAAM,CAAC,EAAE,CAAC;QACvF,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,oEAAoE;SACpF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,kBAAkB,EAAE,CAAC;IAEzC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,8CAA8C,CAAC,CAAC,CAAC,0CAA0C,CAAC;KACpH,CAAC;AACJ,CAAC,CAAC;AAEF,sEAAsE;AACtE,oBAAoB;AACpB,sEAAsE;AAEtE;;GAEG;AACH,MAAM,iBAAiB,GAAmC,UAAU,QAAQ,EAAE,GAAG;IAC/E,MAAM,SAAS,GAAG,QAAsB,CAAC;IAEzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,gDAAgD,OAAO,QAAQ,EAAE;SACjF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE7D,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,sCAAsC,GAAG,GAAG;YAC9C,CAAC,CAAC,kCAAkC,GAAG,gBAAgB,aAAa,GAAG;KAC5E,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,yBAAyB,GAA2C,UAAU,QAAQ,EAAE,WAAW;IACvG,MAAM,SAAS,GAAG,QAA8B,CAAC;IAEjD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,yDAAyD,OAAO,QAAQ,EAAE;SAC1F,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,CAAC,CAAC;IAClE,MAAM,kBAAkB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1E,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,sCAAsC,WAAW,GAAG;YACtD,CAAC,CAAC,kCAAkC,WAAW,gBAAgB,kBAAkB,GAAG;KACzF,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAwC,UAAU,QAAQ,EAAE,QAAQ;IACtF,MAAM,MAAM,GAAG,QAAkC,CAAC;IAElD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,aAAa,IAAI,MAAM,CAAC,EAAE,CAAC;QAChF,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,kEAAkE;SAClF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAEzC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,2CAA2C,QAAQ,GAAG;YACxD,CAAC,CAAC,uBAAuB,QAAQ,eAAe,cAAc,GAAG;KACtE,CAAC;AACJ,CAAC,CAAC;AAEF,sEAAsE;AACtE,kBAAkB;AAClB,sEAAsE;AAEtE;;GAEG;AACH,MAAM,eAAe,GAAoC,UAAU,QAAQ,EAAE,IAAI;IAC/E,MAAM,OAAO,GAAG,QAAoB,CAAC;IAErC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,8CAA8C,OAAO,QAAQ,EAAE;SAC/E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAClD,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/D,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,oCAAoC,IAAI,GAAG;YAC7C,CAAC,CAAC,gCAAgC,IAAI,gBAAgB,gBAAgB,GAAG;KAC9E,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAqC,UAAU,QAAQ,EAAE,KAAK;IAChF,MAAM,MAAM,GAAG,QAA+B,CAAC;IAE/C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,UAAU,IAAI,MAAM,CAAC,EAAE,CAAC;QAC7E,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,8DAA8D;SAC9E,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,WAAW,KAAK,KAAK,CAAC;IAEnC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,+BAA+B,KAAK,WAAW;YACjD,CAAC,CAAC,2BAA2B,KAAK,sBAAsB,WAAW,EAAE;KAC1E,CAAC;AACJ,CAAC,CAAC;AAEF,sEAAsE;AACtE,oBAAoB;AACpB,sEAAsE;AAEtE;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAwB,UAAU,QAAQ;IAC9D,MAAM,QAAQ,GAAG,QAAmC,CAAC;IAErD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,oCAAoC,OAAO,QAAQ,EAAE;SACrE,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,KAAK,KAAK,CAAC;IACjD,MAAM,KAAK,GAAG,IAAI,IAAI,QAAQ,CAAC;IAC/B,MAAM,SAAS,GAAG,QAAQ,IAAI,QAAQ,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,IAAI,QAAQ,CAAC;IACrC,MAAM,0BAA0B,GAAG,CAAC,SAAS,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,QAAQ,CAAC,CAAC;IAEvF,MAAM,IAAI,GAAG,UAAU,IAAI,KAAK,IAAI,0BAA0B,CAAC;IAE/D,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,4CAA4C,CAAC;YACtD,CAAC;YACD,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,CAAC,UAAU;gBAAE,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YACpE,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAC9C,IAAI,CAAC,0BAA0B,EAAE,CAAC;gBAChC,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;oBAAE,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;;oBACnE,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;YAC5D,CAAC;YACD,OAAO,yCAAyC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,YAAY,GAAwB,UAAU,QAAQ;IAC1D,MAAM,QAAQ,GAAG,QAAmC,CAAC;IAErD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,oCAAoC,OAAO,QAAQ,EAAE;SACrE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,IAAI,QAAQ,CAAC;IAElC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,kCAAkC,CAAC;KACpG,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,GAAwB,UAAU,QAAQ;IACzD,MAAM,QAAQ,GAAG,QAAmC,CAAC;IAErD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,oCAAoC,OAAO,QAAQ,EAAE;SACrE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,IAAI,QAAQ,CAAC;IAEjC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,iCAAiC,CAAC;KAClG,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,eAAe,GAAoC,UAAU,QAAQ,EAAE,IAAI;IAC/E,MAAM,QAAQ,GAAG,QAAwC,CAAC;IAE1D,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CAAC,oCAAoC,OAAO,QAAQ,EAAE;SACrE,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACxC,MAAM,IAAI,GAAG,UAAU,KAAK,IAAI,CAAC;IAEjC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,IAAI;YACF,CAAC,CAAC,4CAA4C,IAAI,EAAE;YACpD,CAAC,CAAC,uBAAuB,IAAI,aAAa,UAAU,IAAI,UAAU,EAAE;KACzE,CAAC;AACJ,CAAC,CAAC;AAEF,sEAAsE;AACtE,UAAU;AACV,sEAAsE;AAEtE;;GAEG;AACU,QAAA,WAAW,GAAG;IACzB,gBAAgB;IAChB,aAAa;IACb,cAAc;IACd,SAAS;IACT,iBAAiB;IACjB,kBAAkB;IAClB,qBAAqB;IAErB,oBAAoB;IACpB,iBAAiB;IACjB,yBAAyB;IACzB,cAAc;IAEd,kBAAkB;IAClB,eAAe;IACf,cAAc;IAEd,oBAAoB;IACpB,gBAAgB;IAChB,YAAY;IACZ,WAAW;IACX,eAAe;IAEf,8CAA8C;IAC9C,GAAG,wBAAU;CACd,CAAC","sourcesContent":["/**\n * @file mcp-matchers.ts\n * @description Custom Jest matchers for MCP testing\n *\n * @example\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test('tools work', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n *\n * const result = await mcp.tools.call('my-tool', {});\n * expect(result).toBeSuccessful();\n * expect(result).toHaveTextContent();\n * });\n * ```\n */\n\nimport type { MatcherFunction } from 'expect';\nimport type { Tool, Resource, ResourceTemplate, Prompt } from '@modelcontextprotocol/sdk/types.js';\nimport type { ToolResultWrapper, ResourceContentWrapper, PromptResultWrapper } from '../client/mcp-test-client.types';\nimport { uiMatchers } from '../ui/ui-matchers';\n\n// ═══════════════════════════════════════════════════════════════════\n// HELPER TYPES\n// ═══════════════════════════════════════════════════════════════════\n\ntype ResultWrapper = ToolResultWrapper | ResourceContentWrapper;\n\n// ═══════════════════════════════════════════════════════════════════\n// TOOL MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if tools array contains a tool with the given name\n */\nconst toContainTool: MatcherFunction<[toolName: string]> = function (received, toolName) {\n const tools = received as Tool[];\n\n if (!Array.isArray(tools)) {\n return {\n pass: false,\n message: () => `Expected an array of tools, but received ${typeof received}`,\n };\n }\n\n const pass = tools.some((t) => t.name === toolName);\n const availableTools = tools.map((t) => t.name).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected tools not to contain \"${toolName}\"`\n : `Expected tools to contain \"${toolName}\", but got: [${availableTools}]`,\n };\n};\n\n/**\n * Check if result is successful (not an error)\n */\nconst toBeSuccessful: MatcherFunction<[]> = function (received) {\n const result = received as ResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('isSuccess' in result)) {\n return {\n pass: false,\n message: () => `Expected a result wrapper object with isSuccess property`,\n };\n }\n\n const pass = result.isSuccess;\n\n return {\n pass,\n message: () =>\n pass\n ? 'Expected result not to be successful'\n : `Expected result to be successful, but got error: ${result.error?.message ?? 'unknown error'}`,\n };\n};\n\n/**\n * Check if result is an error, optionally with a specific error code\n */\nconst toBeError: MatcherFunction<[expectedCode?: number]> = function (received, expectedCode) {\n const result = received as ResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('isError' in result)) {\n return {\n pass: false,\n message: () => `Expected a result wrapper object with isError property`,\n };\n }\n\n let pass = result.isError;\n\n if (pass && expectedCode !== undefined) {\n pass = result.error?.code === expectedCode;\n }\n\n return {\n pass,\n message: () => {\n if (!result.isError) {\n return 'Expected result to be an error, but it was successful';\n }\n if (expectedCode !== undefined && result.error?.code !== expectedCode) {\n return `Expected error code ${expectedCode}, but got ${result.error?.code}`;\n }\n return 'Expected result not to be an error';\n },\n };\n};\n\n/**\n * Check if tool result or resource content has text content, optionally containing specific text\n * Works with both ToolResultWrapper and ResourceContentWrapper\n */\nconst toHaveTextContent: MatcherFunction<[expectedText?: string]> = function (received, expectedText) {\n const result = received as ToolResultWrapper | ResourceContentWrapper;\n\n // Check if it's a valid wrapper object with text() method\n if (typeof result !== 'object' || result === null || !('text' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper or ResourceContentWrapper object with text method`,\n };\n }\n\n // Get text content - works for both wrapper types\n const text = result.text();\n\n // Check if has text content - ToolResultWrapper has hasTextContent, ResourceContentWrapper uses text() !== undefined\n const hasText = 'hasTextContent' in result ? result.hasTextContent() : text !== undefined;\n\n let pass = hasText;\n\n if (pass && expectedText !== undefined) {\n pass = text?.includes(expectedText) ?? false;\n }\n\n return {\n pass,\n message: () => {\n if (!hasText) {\n return 'Expected result to have text content';\n }\n if (expectedText !== undefined && !text?.includes(expectedText)) {\n return `Expected text to contain \"${expectedText}\", but got: \"${text}\"`;\n }\n return 'Expected result not to have text content';\n },\n };\n};\n\n/**\n * Check if tool result has image content\n */\nconst toHaveImageContent: MatcherFunction<[]> = function (received) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasImageContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasImageContent method`,\n };\n }\n\n const pass = result.hasImageContent();\n\n return {\n pass,\n message: () => (pass ? 'Expected result not to have image content' : 'Expected result to have image content'),\n };\n};\n\n/**\n * Check if tool result has resource content\n */\nconst toHaveResourceContent: MatcherFunction<[]> = function (received) {\n const result = received as ToolResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasResourceContent' in result)) {\n return {\n pass: false,\n message: () => `Expected a ToolResultWrapper object with hasResourceContent method`,\n };\n }\n\n const pass = result.hasResourceContent();\n\n return {\n pass,\n message: () => (pass ? 'Expected result not to have resource content' : 'Expected result to have resource content'),\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// RESOURCE MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if resources array contains a resource with the given URI\n */\nconst toContainResource: MatcherFunction<[uri: string]> = function (received, uri) {\n const resources = received as Resource[];\n\n if (!Array.isArray(resources)) {\n return {\n pass: false,\n message: () => `Expected an array of resources, but received ${typeof received}`,\n };\n }\n\n const pass = resources.some((r) => r.uri === uri);\n const availableUris = resources.map((r) => r.uri).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected resources not to contain \"${uri}\"`\n : `Expected resources to contain \"${uri}\", but got: [${availableUris}]`,\n };\n};\n\n/**\n * Check if resource templates array contains a template with the given URI template\n */\nconst toContainResourceTemplate: MatcherFunction<[uriTemplate: string]> = function (received, uriTemplate) {\n const templates = received as ResourceTemplate[];\n\n if (!Array.isArray(templates)) {\n return {\n pass: false,\n message: () => `Expected an array of resource templates, but received ${typeof received}`,\n };\n }\n\n const pass = templates.some((t) => t.uriTemplate === uriTemplate);\n const availableTemplates = templates.map((t) => t.uriTemplate).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected templates not to contain \"${uriTemplate}\"`\n : `Expected templates to contain \"${uriTemplate}\", but got: [${availableTemplates}]`,\n };\n};\n\n/**\n * Check if resource content has a specific MIME type\n */\nconst toHaveMimeType: MatcherFunction<[mimeType: string]> = function (received, mimeType) {\n const result = received as ResourceContentWrapper;\n\n if (typeof result !== 'object' || result === null || !('hasMimeType' in result)) {\n return {\n pass: false,\n message: () => `Expected a ResourceContentWrapper object with hasMimeType method`,\n };\n }\n\n const pass = result.hasMimeType(mimeType);\n const actualMimeType = result.mimeType();\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected content not to have MIME type \"${mimeType}\"`\n : `Expected MIME type \"${mimeType}\", but got \"${actualMimeType}\"`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// PROMPT MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if prompts array contains a prompt with the given name\n */\nconst toContainPrompt: MatcherFunction<[name: string]> = function (received, name) {\n const prompts = received as Prompt[];\n\n if (!Array.isArray(prompts)) {\n return {\n pass: false,\n message: () => `Expected an array of prompts, but received ${typeof received}`,\n };\n }\n\n const pass = prompts.some((p) => p.name === name);\n const availablePrompts = prompts.map((p) => p.name).join(', ');\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected prompts not to contain \"${name}\"`\n : `Expected prompts to contain \"${name}\", but got: [${availablePrompts}]`,\n };\n};\n\n/**\n * Check if prompt result has a specific number of messages\n */\nconst toHaveMessages: MatcherFunction<[count: number]> = function (received, count) {\n const result = received as PromptResultWrapper;\n\n if (typeof result !== 'object' || result === null || !('messages' in result)) {\n return {\n pass: false,\n message: () => `Expected a PromptResultWrapper object with messages property`,\n };\n }\n\n const actualCount = result.messages?.length ?? 0;\n const pass = actualCount === count;\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected prompt not to have ${count} messages`\n : `Expected prompt to have ${count} messages, but got ${actualCount}`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// PROTOCOL MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Check if response is valid JSON-RPC 2.0\n * A valid JSON-RPC 2.0 response must have:\n * - jsonrpc: \"2.0\"\n * - id (matching the request, can be null for notifications)\n * - Either result OR error (but not both)\n */\nconst toBeValidJsonRpc: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const hasJsonRpc = response['jsonrpc'] === '2.0';\n const hasId = 'id' in response;\n const hasResult = 'result' in response;\n const hasError = 'error' in response;\n const hasExactlyOneResultOrError = (hasResult || hasError) && !(hasResult && hasError);\n\n const pass = hasJsonRpc && hasId && hasExactlyOneResultOrError;\n\n return {\n pass,\n message: () => {\n if (pass) {\n return 'Expected response not to be valid JSON-RPC';\n }\n const issues: string[] = [];\n if (!hasJsonRpc) issues.push('missing or invalid \"jsonrpc\": \"2.0\"');\n if (!hasId) issues.push('missing \"id\" field');\n if (!hasExactlyOneResultOrError) {\n if (!hasResult && !hasError) issues.push('missing \"result\" or \"error\"');\n else issues.push('cannot have both \"result\" and \"error\"');\n }\n return `Expected valid JSON-RPC 2.0 response: ${issues.join(', ')}`;\n },\n };\n};\n\n/**\n * Check if JSON-RPC response has a result\n */\nconst toHaveResult: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const pass = 'result' in response;\n\n return {\n pass,\n message: () => (pass ? 'Expected response not to have result' : 'Expected response to have result'),\n };\n};\n\n/**\n * Check if JSON-RPC response has an error\n */\nconst toHaveError: MatcherFunction<[]> = function (received) {\n const response = received as Record<string, unknown>;\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const pass = 'error' in response;\n\n return {\n pass,\n message: () => (pass ? 'Expected response not to have error' : 'Expected response to have error'),\n };\n};\n\n/**\n * Check if JSON-RPC response has a specific error code\n */\nconst toHaveErrorCode: MatcherFunction<[code: number]> = function (received, code) {\n const response = received as { error?: { code: number } };\n\n if (typeof response !== 'object' || response === null) {\n return {\n pass: false,\n message: () => `Expected an object, but received ${typeof received}`,\n };\n }\n\n const actualCode = response.error?.code;\n const pass = actualCode === code;\n\n return {\n pass,\n message: () =>\n pass\n ? `Expected response not to have error code ${code}`\n : `Expected error code ${code}, but got ${actualCode ?? 'no error'}`,\n };\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * All MCP matchers as an object for expect.extend()\n */\nexport const mcpMatchers = {\n // Tool matchers\n toContainTool,\n toBeSuccessful,\n toBeError,\n toHaveTextContent,\n toHaveImageContent,\n toHaveResourceContent,\n\n // Resource matchers\n toContainResource,\n toContainResourceTemplate,\n toHaveMimeType,\n\n // Prompt matchers\n toContainPrompt,\n toHaveMessages,\n\n // Protocol matchers\n toBeValidJsonRpc,\n toHaveResult,\n toHaveError,\n toHaveErrorCode,\n\n // UI matchers (for testing tool UI responses)\n ...uiMatchers,\n};\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file platform/index.ts
|
|
3
|
+
* @description Platform utilities barrel export.
|
|
4
|
+
*
|
|
5
|
+
* Provides types and helpers for platform-specific E2E testing.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import {
|
|
10
|
+
* TestPlatformType,
|
|
11
|
+
* getPlatformClientInfo,
|
|
12
|
+
* getPlatformMetaNamespace,
|
|
13
|
+
* getForbiddenMetaPrefixes,
|
|
14
|
+
* } from '@frontmcp/testing';
|
|
15
|
+
*
|
|
16
|
+
* // Get expected meta prefixes for a platform
|
|
17
|
+
* const prefixes = getToolsListMetaPrefixes('openai');
|
|
18
|
+
* // ['openai/']
|
|
19
|
+
*
|
|
20
|
+
* // Get forbidden prefixes (should NOT appear)
|
|
21
|
+
* const forbidden = getForbiddenMetaPrefixes('openai');
|
|
22
|
+
* // ['ui/', 'frontmcp/']
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export type { TestPlatformType, PlatformMetaNamespace } from './platform-types';
|
|
26
|
+
export { getPlatformMetaNamespace, getPlatformMimeType, isOpenAIPlatform, isExtAppsPlatform, isFrontmcpPlatform, getToolsListMetaPrefixes, getToolCallMetaPrefixes, getForbiddenMetaPrefixes, } from './platform-types';
|
|
27
|
+
export type { TestClientInfo, TestClientCapabilities, ExperimentalCapabilities, McpAppsExtension, } from './platform-client-info';
|
|
28
|
+
export { getPlatformClientInfo, buildUserAgent, getPlatformUserAgent, PLATFORM_DETECTION_PATTERNS, MCP_APPS_EXTENSION_KEY, getPlatformCapabilities, requiresCapabilityDetection, } from './platform-client-info';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file platform/index.ts
|
|
4
|
+
* @description Platform utilities barrel export.
|
|
5
|
+
*
|
|
6
|
+
* Provides types and helpers for platform-specific E2E testing.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import {
|
|
11
|
+
* TestPlatformType,
|
|
12
|
+
* getPlatformClientInfo,
|
|
13
|
+
* getPlatformMetaNamespace,
|
|
14
|
+
* getForbiddenMetaPrefixes,
|
|
15
|
+
* } from '@frontmcp/testing';
|
|
16
|
+
*
|
|
17
|
+
* // Get expected meta prefixes for a platform
|
|
18
|
+
* const prefixes = getToolsListMetaPrefixes('openai');
|
|
19
|
+
* // ['openai/']
|
|
20
|
+
*
|
|
21
|
+
* // Get forbidden prefixes (should NOT appear)
|
|
22
|
+
* const forbidden = getForbiddenMetaPrefixes('openai');
|
|
23
|
+
* // ['ui/', 'frontmcp/']
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.requiresCapabilityDetection = exports.getPlatformCapabilities = exports.MCP_APPS_EXTENSION_KEY = exports.PLATFORM_DETECTION_PATTERNS = exports.getPlatformUserAgent = exports.buildUserAgent = exports.getPlatformClientInfo = exports.getForbiddenMetaPrefixes = exports.getToolCallMetaPrefixes = exports.getToolsListMetaPrefixes = exports.isFrontmcpPlatform = exports.isExtAppsPlatform = exports.isOpenAIPlatform = exports.getPlatformMimeType = exports.getPlatformMetaNamespace = void 0;
|
|
28
|
+
// Platform type utilities
|
|
29
|
+
var platform_types_1 = require("./platform-types");
|
|
30
|
+
Object.defineProperty(exports, "getPlatformMetaNamespace", { enumerable: true, get: function () { return platform_types_1.getPlatformMetaNamespace; } });
|
|
31
|
+
Object.defineProperty(exports, "getPlatformMimeType", { enumerable: true, get: function () { return platform_types_1.getPlatformMimeType; } });
|
|
32
|
+
Object.defineProperty(exports, "isOpenAIPlatform", { enumerable: true, get: function () { return platform_types_1.isOpenAIPlatform; } });
|
|
33
|
+
Object.defineProperty(exports, "isExtAppsPlatform", { enumerable: true, get: function () { return platform_types_1.isExtAppsPlatform; } });
|
|
34
|
+
Object.defineProperty(exports, "isFrontmcpPlatform", { enumerable: true, get: function () { return platform_types_1.isFrontmcpPlatform; } });
|
|
35
|
+
Object.defineProperty(exports, "getToolsListMetaPrefixes", { enumerable: true, get: function () { return platform_types_1.getToolsListMetaPrefixes; } });
|
|
36
|
+
Object.defineProperty(exports, "getToolCallMetaPrefixes", { enumerable: true, get: function () { return platform_types_1.getToolCallMetaPrefixes; } });
|
|
37
|
+
Object.defineProperty(exports, "getForbiddenMetaPrefixes", { enumerable: true, get: function () { return platform_types_1.getForbiddenMetaPrefixes; } });
|
|
38
|
+
var platform_client_info_1 = require("./platform-client-info");
|
|
39
|
+
Object.defineProperty(exports, "getPlatformClientInfo", { enumerable: true, get: function () { return platform_client_info_1.getPlatformClientInfo; } });
|
|
40
|
+
Object.defineProperty(exports, "buildUserAgent", { enumerable: true, get: function () { return platform_client_info_1.buildUserAgent; } });
|
|
41
|
+
Object.defineProperty(exports, "getPlatformUserAgent", { enumerable: true, get: function () { return platform_client_info_1.getPlatformUserAgent; } });
|
|
42
|
+
Object.defineProperty(exports, "PLATFORM_DETECTION_PATTERNS", { enumerable: true, get: function () { return platform_client_info_1.PLATFORM_DETECTION_PATTERNS; } });
|
|
43
|
+
// Capability utilities for ext-apps platform detection
|
|
44
|
+
Object.defineProperty(exports, "MCP_APPS_EXTENSION_KEY", { enumerable: true, get: function () { return platform_client_info_1.MCP_APPS_EXTENSION_KEY; } });
|
|
45
|
+
Object.defineProperty(exports, "getPlatformCapabilities", { enumerable: true, get: function () { return platform_client_info_1.getPlatformCapabilities; } });
|
|
46
|
+
Object.defineProperty(exports, "requiresCapabilityDetection", { enumerable: true, get: function () { return platform_client_info_1.requiresCapabilityDetection; } });
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/platform/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;AAKH,0BAA0B;AAC1B,mDAS0B;AARxB,0HAAA,wBAAwB,OAAA;AACxB,qHAAA,mBAAmB,OAAA;AACnB,kHAAA,gBAAgB,OAAA;AAChB,mHAAA,iBAAiB,OAAA;AACjB,oHAAA,kBAAkB,OAAA;AAClB,0HAAA,wBAAwB,OAAA;AACxB,yHAAA,uBAAuB,OAAA;AACvB,0HAAA,wBAAwB,OAAA;AAW1B,+DASgC;AAR9B,6HAAA,qBAAqB,OAAA;AACrB,sHAAA,cAAc,OAAA;AACd,4HAAA,oBAAoB,OAAA;AACpB,mIAAA,2BAA2B,OAAA;AAC3B,uDAAuD;AACvD,8HAAA,sBAAsB,OAAA;AACtB,+HAAA,uBAAuB,OAAA;AACvB,mIAAA,2BAA2B,OAAA","sourcesContent":["/**\n * @file platform/index.ts\n * @description Platform utilities barrel export.\n *\n * Provides types and helpers for platform-specific E2E testing.\n *\n * @example\n * ```typescript\n * import {\n * TestPlatformType,\n * getPlatformClientInfo,\n * getPlatformMetaNamespace,\n * getForbiddenMetaPrefixes,\n * } from '@frontmcp/testing';\n *\n * // Get expected meta prefixes for a platform\n * const prefixes = getToolsListMetaPrefixes('openai');\n * // ['openai/']\n *\n * // Get forbidden prefixes (should NOT appear)\n * const forbidden = getForbiddenMetaPrefixes('openai');\n * // ['ui/', 'frontmcp/']\n * ```\n */\n\n// Platform types\nexport type { TestPlatformType, PlatformMetaNamespace } from './platform-types';\n\n// Platform type utilities\nexport {\n getPlatformMetaNamespace,\n getPlatformMimeType,\n isOpenAIPlatform,\n isExtAppsPlatform,\n isFrontmcpPlatform,\n getToolsListMetaPrefixes,\n getToolCallMetaPrefixes,\n getForbiddenMetaPrefixes,\n} from './platform-types';\n\n// Client info utilities\nexport type {\n TestClientInfo,\n TestClientCapabilities,\n ExperimentalCapabilities,\n McpAppsExtension,\n} from './platform-client-info';\n\nexport {\n getPlatformClientInfo,\n buildUserAgent,\n getPlatformUserAgent,\n PLATFORM_DETECTION_PATTERNS,\n // Capability utilities for ext-apps platform detection\n MCP_APPS_EXTENSION_KEY,\n getPlatformCapabilities,\n requiresCapabilityDetection,\n} from './platform-client-info';\n"]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file platform-client-info.ts
|
|
3
|
+
* @description Client info, User-Agent mappings, and capabilities for each platform.
|
|
4
|
+
*
|
|
5
|
+
* These mappings enable platform detection in MCP servers via:
|
|
6
|
+
* - User-Agent header (most platforms)
|
|
7
|
+
* - Client capabilities (ext-apps via io.modelcontextprotocol/ui extension)
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { getPlatformClientInfo, getPlatformCapabilities } from '@frontmcp/testing';
|
|
12
|
+
*
|
|
13
|
+
* const client = McpTestClient.create({ baseUrl })
|
|
14
|
+
* .withPlatform('ext-apps') // Auto-sets clientInfo AND capabilities
|
|
15
|
+
* .buildAndConnect();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { TestPlatformType } from './platform-types';
|
|
19
|
+
/**
|
|
20
|
+
* MCP Apps extension key used for ext-apps platform detection.
|
|
21
|
+
*/
|
|
22
|
+
export declare const MCP_APPS_EXTENSION_KEY: "io.modelcontextprotocol/ui";
|
|
23
|
+
/**
|
|
24
|
+
* Client info for MCP initialization.
|
|
25
|
+
* Matches the Implementation type from @modelcontextprotocol/sdk.
|
|
26
|
+
*/
|
|
27
|
+
export interface TestClientInfo {
|
|
28
|
+
/** Client name (used for User-Agent header) */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Client version */
|
|
31
|
+
version: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get client info for a specific platform.
|
|
35
|
+
*
|
|
36
|
+
* These values are used to:
|
|
37
|
+
* 1. Set the clientInfo during MCP initialize
|
|
38
|
+
* 2. Generate the User-Agent header for platform detection
|
|
39
|
+
*/
|
|
40
|
+
export declare function getPlatformClientInfo(platform: TestPlatformType): TestClientInfo;
|
|
41
|
+
/**
|
|
42
|
+
* Build User-Agent header string from client info.
|
|
43
|
+
*
|
|
44
|
+
* Format: "{name}/{version}"
|
|
45
|
+
*
|
|
46
|
+
* Examples:
|
|
47
|
+
* - "ChatGPT/1.0" (OpenAI)
|
|
48
|
+
* - "mcp-ext-apps/1.0" (ext-apps)
|
|
49
|
+
* - "claude-desktop/1.0" (Claude)
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildUserAgent(clientInfo: TestClientInfo): string;
|
|
52
|
+
/**
|
|
53
|
+
* Get the User-Agent string for a platform.
|
|
54
|
+
*/
|
|
55
|
+
export declare function getPlatformUserAgent(platform: TestPlatformType): string;
|
|
56
|
+
/**
|
|
57
|
+
* Platform detection patterns for parsing User-Agent headers.
|
|
58
|
+
* These are the patterns that MCP servers use to detect platforms.
|
|
59
|
+
*
|
|
60
|
+
* Note: ext-apps is detected via capabilities, not User-Agent.
|
|
61
|
+
*/
|
|
62
|
+
export declare const PLATFORM_DETECTION_PATTERNS: Record<TestPlatformType, RegExp>;
|
|
63
|
+
/**
|
|
64
|
+
* MCP Apps extension capability for ext-apps platform detection.
|
|
65
|
+
*/
|
|
66
|
+
export interface McpAppsExtension {
|
|
67
|
+
/** Supported MIME types */
|
|
68
|
+
mimeTypes?: string[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Experimental capabilities for MCP clients.
|
|
72
|
+
*/
|
|
73
|
+
export interface ExperimentalCapabilities {
|
|
74
|
+
[MCP_APPS_EXTENSION_KEY]?: McpAppsExtension;
|
|
75
|
+
[key: string]: unknown;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Client capabilities sent during MCP initialization.
|
|
79
|
+
*/
|
|
80
|
+
export interface TestClientCapabilities {
|
|
81
|
+
sampling?: Record<string, unknown>;
|
|
82
|
+
experimental?: ExperimentalCapabilities;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get client capabilities for a specific platform.
|
|
86
|
+
*
|
|
87
|
+
* Currently only ext-apps requires special capabilities:
|
|
88
|
+
* - ext-apps: Sets io.modelcontextprotocol/ui extension for platform detection
|
|
89
|
+
*
|
|
90
|
+
* @param platform - The platform type
|
|
91
|
+
* @returns Client capabilities to send during initialization
|
|
92
|
+
*/
|
|
93
|
+
export declare function getPlatformCapabilities(platform: TestPlatformType): TestClientCapabilities;
|
|
94
|
+
/**
|
|
95
|
+
* Check if a platform requires capability-based detection (vs User-Agent).
|
|
96
|
+
*/
|
|
97
|
+
export declare function requiresCapabilityDetection(platform: TestPlatformType): boolean;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file platform-client-info.ts
|
|
4
|
+
* @description Client info, User-Agent mappings, and capabilities for each platform.
|
|
5
|
+
*
|
|
6
|
+
* These mappings enable platform detection in MCP servers via:
|
|
7
|
+
* - User-Agent header (most platforms)
|
|
8
|
+
* - Client capabilities (ext-apps via io.modelcontextprotocol/ui extension)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { getPlatformClientInfo, getPlatformCapabilities } from '@frontmcp/testing';
|
|
13
|
+
*
|
|
14
|
+
* const client = McpTestClient.create({ baseUrl })
|
|
15
|
+
* .withPlatform('ext-apps') // Auto-sets clientInfo AND capabilities
|
|
16
|
+
* .buildAndConnect();
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.PLATFORM_DETECTION_PATTERNS = exports.MCP_APPS_EXTENSION_KEY = void 0;
|
|
21
|
+
exports.getPlatformClientInfo = getPlatformClientInfo;
|
|
22
|
+
exports.buildUserAgent = buildUserAgent;
|
|
23
|
+
exports.getPlatformUserAgent = getPlatformUserAgent;
|
|
24
|
+
exports.getPlatformCapabilities = getPlatformCapabilities;
|
|
25
|
+
exports.requiresCapabilityDetection = requiresCapabilityDetection;
|
|
26
|
+
/**
|
|
27
|
+
* MCP Apps extension key used for ext-apps platform detection.
|
|
28
|
+
*/
|
|
29
|
+
exports.MCP_APPS_EXTENSION_KEY = 'io.modelcontextprotocol/ui';
|
|
30
|
+
/**
|
|
31
|
+
* Get client info for a specific platform.
|
|
32
|
+
*
|
|
33
|
+
* These values are used to:
|
|
34
|
+
* 1. Set the clientInfo during MCP initialize
|
|
35
|
+
* 2. Generate the User-Agent header for platform detection
|
|
36
|
+
*/
|
|
37
|
+
function getPlatformClientInfo(platform) {
|
|
38
|
+
switch (platform) {
|
|
39
|
+
case 'openai':
|
|
40
|
+
return {
|
|
41
|
+
name: 'ChatGPT',
|
|
42
|
+
version: '1.0',
|
|
43
|
+
};
|
|
44
|
+
case 'ext-apps':
|
|
45
|
+
return {
|
|
46
|
+
name: 'mcp-ext-apps',
|
|
47
|
+
version: '1.0',
|
|
48
|
+
};
|
|
49
|
+
case 'claude':
|
|
50
|
+
return {
|
|
51
|
+
name: 'claude-desktop',
|
|
52
|
+
version: '1.0',
|
|
53
|
+
};
|
|
54
|
+
case 'cursor':
|
|
55
|
+
return {
|
|
56
|
+
name: 'cursor',
|
|
57
|
+
version: '1.0',
|
|
58
|
+
};
|
|
59
|
+
case 'continue':
|
|
60
|
+
return {
|
|
61
|
+
name: 'continue',
|
|
62
|
+
version: '1.0',
|
|
63
|
+
};
|
|
64
|
+
case 'cody':
|
|
65
|
+
return {
|
|
66
|
+
name: 'cody',
|
|
67
|
+
version: '1.0',
|
|
68
|
+
};
|
|
69
|
+
case 'gemini':
|
|
70
|
+
return {
|
|
71
|
+
name: 'gemini',
|
|
72
|
+
version: '1.0',
|
|
73
|
+
};
|
|
74
|
+
case 'generic-mcp':
|
|
75
|
+
return {
|
|
76
|
+
name: 'generic-mcp-client',
|
|
77
|
+
version: '1.0',
|
|
78
|
+
};
|
|
79
|
+
case 'unknown':
|
|
80
|
+
default:
|
|
81
|
+
return {
|
|
82
|
+
name: 'mcp-test-client',
|
|
83
|
+
version: '1.0',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Build User-Agent header string from client info.
|
|
89
|
+
*
|
|
90
|
+
* Format: "{name}/{version}"
|
|
91
|
+
*
|
|
92
|
+
* Examples:
|
|
93
|
+
* - "ChatGPT/1.0" (OpenAI)
|
|
94
|
+
* - "mcp-ext-apps/1.0" (ext-apps)
|
|
95
|
+
* - "claude-desktop/1.0" (Claude)
|
|
96
|
+
*/
|
|
97
|
+
function buildUserAgent(clientInfo) {
|
|
98
|
+
return `${clientInfo.name}/${clientInfo.version}`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get the User-Agent string for a platform.
|
|
102
|
+
*/
|
|
103
|
+
function getPlatformUserAgent(platform) {
|
|
104
|
+
return buildUserAgent(getPlatformClientInfo(platform));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Platform detection patterns for parsing User-Agent headers.
|
|
108
|
+
* These are the patterns that MCP servers use to detect platforms.
|
|
109
|
+
*
|
|
110
|
+
* Note: ext-apps is detected via capabilities, not User-Agent.
|
|
111
|
+
*/
|
|
112
|
+
exports.PLATFORM_DETECTION_PATTERNS = {
|
|
113
|
+
openai: /chatgpt/i,
|
|
114
|
+
'ext-apps': /mcp-ext-apps/i, // Note: Actual detection uses capabilities
|
|
115
|
+
claude: /claude|claude-desktop/i,
|
|
116
|
+
cursor: /cursor/i,
|
|
117
|
+
continue: /continue/i,
|
|
118
|
+
cody: /cody/i,
|
|
119
|
+
gemini: /gemini/i,
|
|
120
|
+
'generic-mcp': /generic-mcp/i,
|
|
121
|
+
unknown: /.*/, // Matches anything (fallback)
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Get client capabilities for a specific platform.
|
|
125
|
+
*
|
|
126
|
+
* Currently only ext-apps requires special capabilities:
|
|
127
|
+
* - ext-apps: Sets io.modelcontextprotocol/ui extension for platform detection
|
|
128
|
+
*
|
|
129
|
+
* @param platform - The platform type
|
|
130
|
+
* @returns Client capabilities to send during initialization
|
|
131
|
+
*/
|
|
132
|
+
function getPlatformCapabilities(platform) {
|
|
133
|
+
const baseCapabilities = {
|
|
134
|
+
sampling: {},
|
|
135
|
+
};
|
|
136
|
+
// ext-apps requires the io.modelcontextprotocol/ui extension for detection
|
|
137
|
+
if (platform === 'ext-apps') {
|
|
138
|
+
return {
|
|
139
|
+
...baseCapabilities,
|
|
140
|
+
experimental: {
|
|
141
|
+
[exports.MCP_APPS_EXTENSION_KEY]: {
|
|
142
|
+
mimeTypes: ['text/html+mcp'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return baseCapabilities;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check if a platform requires capability-based detection (vs User-Agent).
|
|
151
|
+
*/
|
|
152
|
+
function requiresCapabilityDetection(platform) {
|
|
153
|
+
return platform === 'ext-apps';
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=platform-client-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-client-info.js","sourceRoot":"","sources":["../../../src/platform/platform-client-info.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AA2BH,sDAiDC;AAYD,wCAEC;AAKD,oDAEC;AAyDD,0DAkBC;AAKD,kEAEC;AA/KD;;GAEG;AACU,QAAA,sBAAsB,GAAG,4BAAqC,CAAC;AAa5E;;;;;;GAMG;AACH,SAAgB,qBAAqB,CAAC,QAA0B;IAC9D,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,UAAU;YACb,OAAO;gBACL,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO;gBACL,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,UAAU;YACb,OAAO;gBACL,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,MAAM;YACT,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,aAAa;YAChB,OAAO;gBACL,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,KAAK,SAAS,CAAC;QACf;YACE,OAAO;gBACL,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,KAAK;aACf,CAAC;IACN,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAAC,UAA0B;IACvD,OAAO,GAAG,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAAC,QAA0B;IAC7D,OAAO,cAAc,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACU,QAAA,2BAA2B,GAAqC;IAC3E,MAAM,EAAE,UAAU;IAClB,UAAU,EAAE,eAAe,EAAE,2CAA2C;IACxE,MAAM,EAAE,wBAAwB;IAChC,MAAM,EAAE,SAAS;IACjB,QAAQ,EAAE,WAAW;IACrB,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,SAAS;IACjB,aAAa,EAAE,cAAc;IAC7B,OAAO,EAAE,IAAI,EAAE,8BAA8B;CAC9C,CAAC;AA8BF;;;;;;;;GAQG;AACH,SAAgB,uBAAuB,CAAC,QAA0B;IAChE,MAAM,gBAAgB,GAA2B;QAC/C,QAAQ,EAAE,EAAE;KACb,CAAC;IAEF,2EAA2E;IAC3E,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC5B,OAAO;YACL,GAAG,gBAAgB;YACnB,YAAY,EAAE;gBACZ,CAAC,8BAAsB,CAAC,EAAE;oBACxB,SAAS,EAAE,CAAC,eAAe,CAAC;iBAC7B;aACF;SACF,CAAC;IACJ,CAAC;IAED,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAgB,2BAA2B,CAAC,QAA0B;IACpE,OAAO,QAAQ,KAAK,UAAU,CAAC;AACjC,CAAC","sourcesContent":["/**\n * @file platform-client-info.ts\n * @description Client info, User-Agent mappings, and capabilities for each platform.\n *\n * These mappings enable platform detection in MCP servers via:\n * - User-Agent header (most platforms)\n * - Client capabilities (ext-apps via io.modelcontextprotocol/ui extension)\n *\n * @example\n * ```typescript\n * import { getPlatformClientInfo, getPlatformCapabilities } from '@frontmcp/testing';\n *\n * const client = McpTestClient.create({ baseUrl })\n * .withPlatform('ext-apps') // Auto-sets clientInfo AND capabilities\n * .buildAndConnect();\n * ```\n */\n\nimport type { TestPlatformType } from './platform-types';\n\n/**\n * MCP Apps extension key used for ext-apps platform detection.\n */\nexport const MCP_APPS_EXTENSION_KEY = 'io.modelcontextprotocol/ui' as const;\n\n/**\n * Client info for MCP initialization.\n * Matches the Implementation type from @modelcontextprotocol/sdk.\n */\nexport interface TestClientInfo {\n /** Client name (used for User-Agent header) */\n name: string;\n /** Client version */\n version: string;\n}\n\n/**\n * Get client info for a specific platform.\n *\n * These values are used to:\n * 1. Set the clientInfo during MCP initialize\n * 2. Generate the User-Agent header for platform detection\n */\nexport function getPlatformClientInfo(platform: TestPlatformType): TestClientInfo {\n switch (platform) {\n case 'openai':\n return {\n name: 'ChatGPT',\n version: '1.0',\n };\n case 'ext-apps':\n return {\n name: 'mcp-ext-apps',\n version: '1.0',\n };\n case 'claude':\n return {\n name: 'claude-desktop',\n version: '1.0',\n };\n case 'cursor':\n return {\n name: 'cursor',\n version: '1.0',\n };\n case 'continue':\n return {\n name: 'continue',\n version: '1.0',\n };\n case 'cody':\n return {\n name: 'cody',\n version: '1.0',\n };\n case 'gemini':\n return {\n name: 'gemini',\n version: '1.0',\n };\n case 'generic-mcp':\n return {\n name: 'generic-mcp-client',\n version: '1.0',\n };\n case 'unknown':\n default:\n return {\n name: 'mcp-test-client',\n version: '1.0',\n };\n }\n}\n\n/**\n * Build User-Agent header string from client info.\n *\n * Format: \"{name}/{version}\"\n *\n * Examples:\n * - \"ChatGPT/1.0\" (OpenAI)\n * - \"mcp-ext-apps/1.0\" (ext-apps)\n * - \"claude-desktop/1.0\" (Claude)\n */\nexport function buildUserAgent(clientInfo: TestClientInfo): string {\n return `${clientInfo.name}/${clientInfo.version}`;\n}\n\n/**\n * Get the User-Agent string for a platform.\n */\nexport function getPlatformUserAgent(platform: TestPlatformType): string {\n return buildUserAgent(getPlatformClientInfo(platform));\n}\n\n/**\n * Platform detection patterns for parsing User-Agent headers.\n * These are the patterns that MCP servers use to detect platforms.\n *\n * Note: ext-apps is detected via capabilities, not User-Agent.\n */\nexport const PLATFORM_DETECTION_PATTERNS: Record<TestPlatformType, RegExp> = {\n openai: /chatgpt/i,\n 'ext-apps': /mcp-ext-apps/i, // Note: Actual detection uses capabilities\n claude: /claude|claude-desktop/i,\n cursor: /cursor/i,\n continue: /continue/i,\n cody: /cody/i,\n gemini: /gemini/i,\n 'generic-mcp': /generic-mcp/i,\n unknown: /.*/, // Matches anything (fallback)\n};\n\n// ═══════════════════════════════════════════════════════════════════\n// PLATFORM CAPABILITIES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * MCP Apps extension capability for ext-apps platform detection.\n */\nexport interface McpAppsExtension {\n /** Supported MIME types */\n mimeTypes?: string[];\n}\n\n/**\n * Experimental capabilities for MCP clients.\n */\nexport interface ExperimentalCapabilities {\n [MCP_APPS_EXTENSION_KEY]?: McpAppsExtension;\n [key: string]: unknown;\n}\n\n/**\n * Client capabilities sent during MCP initialization.\n */\nexport interface TestClientCapabilities {\n sampling?: Record<string, unknown>;\n experimental?: ExperimentalCapabilities;\n}\n\n/**\n * Get client capabilities for a specific platform.\n *\n * Currently only ext-apps requires special capabilities:\n * - ext-apps: Sets io.modelcontextprotocol/ui extension for platform detection\n *\n * @param platform - The platform type\n * @returns Client capabilities to send during initialization\n */\nexport function getPlatformCapabilities(platform: TestPlatformType): TestClientCapabilities {\n const baseCapabilities: TestClientCapabilities = {\n sampling: {},\n };\n\n // ext-apps requires the io.modelcontextprotocol/ui extension for detection\n if (platform === 'ext-apps') {\n return {\n ...baseCapabilities,\n experimental: {\n [MCP_APPS_EXTENSION_KEY]: {\n mimeTypes: ['text/html+mcp'],\n },\n },\n };\n }\n\n return baseCapabilities;\n}\n\n/**\n * Check if a platform requires capability-based detection (vs User-Agent).\n */\nexport function requiresCapabilityDetection(platform: TestPlatformType): boolean {\n return platform === 'ext-apps';\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file platform-types.ts
|
|
3
|
+
* @description Platform type definitions for E2E testing.
|
|
4
|
+
*
|
|
5
|
+
* These types mirror the AIPlatformType from @frontmcp/ui/adapters
|
|
6
|
+
* for use in testing without creating a hard dependency.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { TestPlatformType } from '@frontmcp/testing';
|
|
11
|
+
*
|
|
12
|
+
* const platform: TestPlatformType = 'openai';
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Supported AI platform types for testing.
|
|
17
|
+
*
|
|
18
|
+
* - `openai`: OpenAI ChatGPT (uses openai/* meta keys)
|
|
19
|
+
* - `ext-apps`: MCP Apps per SEP-1865 (uses ui/* meta keys)
|
|
20
|
+
* - `claude`: Claude Desktop (uses frontmcp/* + ui/* keys)
|
|
21
|
+
* - `cursor`: Cursor IDE (uses frontmcp/* + ui/* keys)
|
|
22
|
+
* - `continue`: Continue Dev (uses frontmcp/* + ui/* keys)
|
|
23
|
+
* - `cody`: Sourcegraph Cody (uses frontmcp/* + ui/* keys)
|
|
24
|
+
* - `gemini`: Google Gemini (uses frontmcp/* + ui/* keys)
|
|
25
|
+
* - `generic-mcp`: Generic MCP client (uses frontmcp/* + ui/* keys)
|
|
26
|
+
* - `unknown`: Unknown platform (uses frontmcp/* + ui/* keys)
|
|
27
|
+
*/
|
|
28
|
+
export type TestPlatformType = 'openai' | 'ext-apps' | 'claude' | 'cursor' | 'continue' | 'cody' | 'gemini' | 'generic-mcp' | 'unknown';
|
|
29
|
+
/**
|
|
30
|
+
* Platform meta namespace used for tool responses.
|
|
31
|
+
*
|
|
32
|
+
* - `openai`: Uses `openai/*` keys only
|
|
33
|
+
* - `ui`: Uses `ui/*` keys only (ext-apps per SEP-1865)
|
|
34
|
+
* - `frontmcp`: Uses `frontmcp/*` + `ui/*` keys for compatibility
|
|
35
|
+
*/
|
|
36
|
+
export type PlatformMetaNamespace = 'openai' | 'ui' | 'frontmcp';
|
|
37
|
+
/**
|
|
38
|
+
* Get the meta namespace for a platform type.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getPlatformMetaNamespace(platform: TestPlatformType): PlatformMetaNamespace;
|
|
41
|
+
/**
|
|
42
|
+
* Get the expected MIME type for a platform.
|
|
43
|
+
*
|
|
44
|
+
* - OpenAI uses `text/html+skybridge`
|
|
45
|
+
* - All other platforms use `text/html+mcp`
|
|
46
|
+
*/
|
|
47
|
+
export declare function getPlatformMimeType(platform: TestPlatformType): string;
|
|
48
|
+
/**
|
|
49
|
+
* Check if a platform uses OpenAI-specific meta keys.
|
|
50
|
+
*/
|
|
51
|
+
export declare function isOpenAIPlatform(platform: TestPlatformType): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Check if a platform is ext-apps (SEP-1865 MCP Apps).
|
|
54
|
+
*/
|
|
55
|
+
export declare function isExtAppsPlatform(platform: TestPlatformType): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Check if a platform uses FrontMCP meta keys (non-OpenAI, non-ext-apps).
|
|
58
|
+
*/
|
|
59
|
+
export declare function isFrontmcpPlatform(platform: TestPlatformType): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Get all expected meta key prefixes for a platform's tools/list response.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getToolsListMetaPrefixes(platform: TestPlatformType): string[];
|
|
64
|
+
/**
|
|
65
|
+
* Get all expected meta key prefixes for a platform's tool/call response.
|
|
66
|
+
*/
|
|
67
|
+
export declare function getToolCallMetaPrefixes(platform: TestPlatformType): string[];
|
|
68
|
+
/**
|
|
69
|
+
* Get forbidden meta key prefixes for a platform.
|
|
70
|
+
* These prefixes should NOT appear in responses for the given platform.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getForbiddenMetaPrefixes(platform: TestPlatformType): string[];
|