diff --git a/package-lock.json b/package-lock.json index d9f4e951..ca6f3664 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.21.1", + "@modelcontextprotocol/sdk": "1.24.1", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.31.0", + "puppeteer": "24.32.0", "rollup": "4.53.3", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", @@ -380,9 +380,9 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.21.1.tgz", - "integrity": "sha512-UyLFcJLDvUuZbGnaQqXFT32CpPpGj7VS19roLut6gkQVhb439xUzYWbsUvdI3ZPL+2hnFosuugtYWE0Mcs1rmQ==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.1.tgz", + "integrity": "sha512-YTg4v6bKSst8EJM8NXHC3nGm8kgHD08IbIBbognUeLAgGLVgLpYrgQswzLQd4OyTL4l614ejhqsDrV1//t02Qw==", "dev": true, "license": "MIT", "dependencies": { @@ -395,20 +395,25 @@ "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@cfworker/json-schema": "^4.1.1" + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { "@cfworker/json-schema": { "optional": true + }, + "zod": { + "optional": false } } }, @@ -488,9 +493,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.10.13", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.13.tgz", - "integrity": "sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.11.0.tgz", + "integrity": "sha512-n6oQX6mYkG8TRPuPXmbPidkUbsSRalhmaaVAQxvH1IkQy63cwsH+kOjB3e4cpCDHg0aSvsiX9bQ4s2VB6mGWUQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2068,9 +2073,9 @@ } }, "node_modules/bare-fs": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.1.tgz", - "integrity": "sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz", + "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -2653,9 +2658,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1521046", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", - "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", + "version": "0.0.1534754", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1534754.tgz", + "integrity": "sha512-26T91cV5dbOYnXdJi5qQHoTtUoNEqwkHcAyu/IKtjIAxiEqPMrDiRkDOPWVsGfNZGmlQVHQbZRSjD8sxagWVsQ==", "dev": true, "license": "BSD-3-Clause", "peer": true @@ -4653,6 +4658,16 @@ "dev": true, "license": "ISC" }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-cleanup": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/js-cleanup/-/js-cleanup-1.2.0.tgz", @@ -5487,18 +5502,18 @@ } }, "node_modules/puppeteer": { - "version": "24.31.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.31.0.tgz", - "integrity": "sha512-q8y5yLxLD8xdZdzNWqdOL43NbfvUOp60SYhaLZQwHC9CdKldxQKXOyJAciOr7oUJfyAH/KgB2wKvqT2sFKoVXA==", + "version": "24.32.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.32.0.tgz", + "integrity": "sha512-exyxHPV5DSsigIhM/pzLcyzl5XU4Dp5lNP+APwIeStDxAdYqpMnJ1qN0QHXghjJx+cQJczby+ySH5rgv/5GQLw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.10.13", + "@puppeteer/browsers": "2.11.0", "chromium-bidi": "11.0.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1521046", - "puppeteer-core": "24.31.0", + "devtools-protocol": "0.0.1534754", + "puppeteer-core": "24.32.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -5509,16 +5524,16 @@ } }, "node_modules/puppeteer-core": { - "version": "24.31.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.31.0.tgz", - "integrity": "sha512-pnAohhSZipWQoFpXuGV7xCZfaGhqcBR9C4pVrU0QSrcMi7tQMH9J9lDBqBvyMAHQqe8HCARuREqFuVKRQOgTvg==", + "version": "24.32.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.32.0.tgz", + "integrity": "sha512-MqzLLeJjqjtHK9J44+KE3kjtXXhFpPvg+AvXl/oy/jB8MeeNH66/4MNotOTqGZ6MPaxWi51YJ1ASga6OIff6xw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.10.13", + "@puppeteer/browsers": "2.11.0", "chromium-bidi": "11.0.0", "debug": "^4.4.3", - "devtools-protocol": "0.0.1521046", + "devtools-protocol": "0.0.1534754", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.3.9", "ws": "^8.18.3" @@ -7099,13 +7114,13 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", "dev": true, "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25 || ^4" } } } diff --git a/package.json b/package.json index e36902c2..65e9aeec 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "mcpName": "io.github.ChromeDevTools/chrome-devtools-mcp", "devDependencies": { "@eslint/js": "^9.35.0", - "@modelcontextprotocol/sdk": "1.21.1", + "@modelcontextprotocol/sdk": "1.24.1", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", @@ -60,7 +60,7 @@ "eslint-plugin-import": "^2.32.0", "globals": "^16.4.0", "prettier": "^3.6.2", - "puppeteer": "24.31.0", + "puppeteer": "24.32.0", "rollup": "4.53.3", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-license": "^3.6.0", diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index 5feb66fd..70f8dbf3 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -11,9 +11,11 @@ import {join} from 'node:path'; import {describe, it} from 'node:test'; import { + getImageContent, getMockAggregatedIssue, getMockRequest, getMockResponse, + getTextContent, html, stabilizeResponseOutput, withMcpContext, @@ -25,7 +27,7 @@ describe('McpResponse', () => { response.setIncludePages(true); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -35,7 +37,7 @@ describe('McpResponse', () => { response.appendResponseLine('Testing 2'); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -45,7 +47,7 @@ describe('McpResponse', () => { page.accessibility.snapshot = async () => null; const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - assert.deepStrictEqual(result[0].text, `# test response`); + assert.deepStrictEqual(getTextContent(result[0]), `# test response`); }); }); @@ -63,7 +65,7 @@ describe('McpResponse', () => { response.includeSnapshot(); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -81,7 +83,7 @@ describe('McpResponse', () => { response.includeSnapshot(); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -94,7 +96,7 @@ describe('McpResponse', () => { }); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -110,7 +112,7 @@ describe('McpResponse', () => { }); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - t.assert.snapshot?.(stabilizeResponseOutput(result[0].text)); + t.assert.snapshot?.(stabilizeResponseOutput(getTextContent(result[0]))); }); const content = await readFile(filePath, 'utf-8'); t.assert.snapshot?.(stabilizeResponseOutput(content)); @@ -124,7 +126,7 @@ describe('McpResponse', () => { context.setNetworkConditions('Slow 3G'); const result = await response.handle('test', context); assert.equal(result[0].type, 'text'); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -133,17 +135,17 @@ describe('McpResponse', () => { const result = await response.handle('test', context); context.setNetworkConditions(null); assert.equal(result[0].type, 'text'); - assert.strictEqual(result[0].text, `# test response`); + assert.strictEqual(getTextContent(result[0]), `# test response`); }); }); it('adds image when image is attached', async () => { await withMcpContext(async (response, context) => { response.attachImage({data: 'imageBase64', mimeType: 'image/png'}); const result = await response.handle('test', context); - assert.strictEqual(result[0].text, `# test response`); + assert.strictEqual(getTextContent(result[0]), `# test response`); assert.equal(result[1].type, 'image'); - assert.strictEqual(result[1].data, 'imageBase64'); - assert.strictEqual(result[1].mimeType, 'image/png'); + assert.strictEqual(getImageContent(result[1]).data, 'imageBase64'); + assert.strictEqual(getImageContent(result[1]).mimeType, 'image/png'); }); }); @@ -151,7 +153,7 @@ describe('McpResponse', () => { await withMcpContext(async (response, context) => { context.setCpuThrottlingRate(4); const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -159,7 +161,7 @@ describe('McpResponse', () => { await withMcpContext(async (response, context) => { context.setCpuThrottlingRate(1); const result = await response.handle('test', context); - assert.strictEqual(result[0].text, `# test response`); + assert.strictEqual(getTextContent(result[0]), `# test response`); }); }); @@ -177,7 +179,7 @@ describe('McpResponse', () => { await dialogPromise; const result = await response.handle('test', context); await context.getDialog()?.dismiss(); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -195,7 +197,7 @@ describe('McpResponse', () => { await dialogPromise; const result = await response.handle('test', context); await context.getDialog()?.dismiss(); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -206,7 +208,7 @@ describe('McpResponse', () => { return [getMockRequest({stableId: 1}), getMockRequest({stableId: 2})]; }; const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -217,7 +219,7 @@ describe('McpResponse', () => { return [getMockRequest()]; }; const result = await response.handle('test', context); - assert.strictEqual(result[0].text, `# test response`); + assert.strictEqual(getTextContent(result[0]), `# test response`); }); }); @@ -249,7 +251,7 @@ describe('McpResponse', () => { const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -265,7 +267,7 @@ describe('McpResponse', () => { }; response.attachNetworkRequest(1); const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -283,8 +285,8 @@ describe('McpResponse', () => { }); await consoleMessagePromise; const result = await response.handle('test', context); - assert.ok(result[0].text); - t.assert.snapshot?.(result[0].text); + assert.ok(getTextContent(result[0])); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -292,8 +294,8 @@ describe('McpResponse', () => { await withMcpContext(async (response, context) => { response.setIncludeConsoleData(true); const result = await response.handle('test', context); - assert.ok(result[0].text); - t.assert.snapshot?.(result[0].text); + assert.ok(getTextContent(result[0])); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -311,7 +313,7 @@ describe('McpResponse', () => { }; const result = await response.handle('test', context); - const text = (result[0].text as string).toString(); + const text = getTextContent(result[0]); assert.ok(text.includes('')); }); }); @@ -353,7 +355,7 @@ describe('McpResponse network request filtering', () => { ]; }; const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -370,7 +372,7 @@ describe('McpResponse network request filtering', () => { ]; }; const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -387,7 +389,7 @@ describe('McpResponse network request filtering', () => { ]; }; const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -405,7 +407,7 @@ describe('McpResponse network request filtering', () => { }; const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); @@ -424,7 +426,7 @@ describe('McpResponse network request filtering', () => { ]; }; const result = await response.handle('test', context); - t.assert.snapshot?.(result[0].text); + t.assert.snapshot?.(getTextContent(result[0])); }); }); }); @@ -436,7 +438,7 @@ describe('McpResponse network pagination', () => { context.getNetworkRequests = () => requests; response.setIncludeNetworkRequests(true); const result = await response.handle('test', context); - const text = (result[0].text as string).toString(); + const text = getTextContent(result[0]); assert.ok(text.includes('Showing 1-5 of 5 (Page 1 of 1).')); assert.ok(!text.includes('Next page:')); assert.ok(!text.includes('Previous page:')); @@ -453,7 +455,7 @@ describe('McpResponse network pagination', () => { }; response.setIncludeNetworkRequests(true, {pageSize: 10}); const result = await response.handle('test', context); - const text = (result[0].text as string).toString(); + const text = getTextContent(result[0]); assert.ok(text.includes('Showing 1-10 of 30 (Page 1 of 3).')); assert.ok(text.includes('Next page: 1')); assert.ok(!text.includes('Previous page:')); @@ -471,7 +473,7 @@ describe('McpResponse network pagination', () => { pageIdx: 1, }); const result = await response.handle('test', context); - const text = (result[0].text as string).toString(); + const text = getTextContent(result[0]); assert.ok(text.includes('Showing 11-20 of 25 (Page 2 of 3).')); assert.ok(text.includes('Next page: 2')); assert.ok(text.includes('Previous page: 0')); @@ -487,7 +489,7 @@ describe('McpResponse network pagination', () => { pageIdx: 10, // Invalid page number }); const result = await response.handle('test', context); - const text = (result[0].text as string).toString(); + const text = getTextContent(result[0]); assert.ok( text.includes('Invalid page number provided. Showing first page.'), ); diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index 54ece24a..2597b873 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -15,7 +15,7 @@ import { listConsoleMessages, } from '../../src/tools/console.js'; import {serverHooks} from '../server.js'; -import {withMcpContext} from '../utils.js'; +import {getTextContent, withMcpContext} from '../utils.js'; describe('console', () => { before(async () => { @@ -37,10 +37,8 @@ describe('console', () => { ); await listConsoleMessages.handler({params: {}}, response, context); const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; - assert.ok( - textContent.text.includes('msgid=1 [error] This is an error'), - ); + const textContent = getTextContent(formattedResponse[0]); + assert.ok(textContent.includes('msgid=1 [error] This is an error')); }); }); @@ -50,10 +48,8 @@ describe('console', () => { await page.setContent(''); await listConsoleMessages.handler({params: {}}, response, context); const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; - assert.ok( - textContent.text.includes('msgid=1 [error] undefined (0 args)'), - ); + const textContent = getTextContent(formattedResponse[0]); + assert.ok(textContent.includes('msgid=1 [error] undefined (0 args)')); }); }); @@ -70,9 +66,9 @@ describe('console', () => { await issuePromise; await listConsoleMessages.handler({params: {}}, response, context); const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; + const textContent = getTextContent(formattedResponse[0]); assert.ok( - textContent.text.includes( + textContent.includes( `msgid=1 [issue] An element doesn't have an autocomplete attribute (count: 1)`, ), ); @@ -93,9 +89,9 @@ describe('console', () => { await listConsoleMessages.handler({params: {}}, response, context); { const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; + const textContent = getTextContent(formattedResponse[0]); assert.ok( - textContent.text.includes( + textContent.includes( `msgid=1 [issue] An element doesn't have an autocomplete attribute (count: 1)`, ), ); @@ -111,9 +107,9 @@ describe('console', () => { await anotherIssuePromise; { const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; + const textContent = getTextContent(formattedResponse[0]); assert.ok( - textContent.text.includes( + textContent.includes( `msgid=2 [issue] An element doesn't have an autocomplete attribute (count: 1)`, ), ); @@ -138,9 +134,9 @@ describe('console', () => { context, ); const formattedResponse = await response.handle('test', context); - const textContent = formattedResponse[0] as {text: string}; + const textContent = getTextContent(formattedResponse[0]); assert.ok( - textContent.text.includes('msgid=1 [error] This is an error'), + textContent.includes('msgid=1 [error] This is an error'), 'Should contain console message body', ); }); @@ -168,7 +164,7 @@ describe('console', () => { context, ); const formattedResponse = await response2.handle('test', context); - t.assert.snapshot?.(formattedResponse[0].text); + t.assert.snapshot?.(getTextContent(formattedResponse[0])); }); }); it('gets issue details with request id parsing', async t => { @@ -223,7 +219,7 @@ describe('console', () => { context, ); const formattedResponse = await response2.handle('test', context); - const rawText = formattedResponse[0].text as string; + const rawText = getTextContent(formattedResponse[0]); const sanitizedText = rawText .replaceAll(/ID: \d+/g, 'ID: ') .replaceAll(/reqid=\d+/g, 'reqid=') diff --git a/tests/tools/network.test.js.snapshot b/tests/tools/network.test.js.snapshot index af1314d6..7ec05add 100644 --- a/tests/tools/network.test.js.snapshot +++ b/tests/tools/network.test.js.snapshot @@ -6,7 +6,7 @@ Status: [success - 200] - accept-language:en-US,en;q=0.9 - upgrade-insecure-requests:1 - user-agent: -- sec-ch-ua:"Not_A Brand";v="99", "Chromium";v="142" +- sec-ch-ua:"Chromium";v="", "Not A(Brand";v="24" - sec-ch-ua-mobile:?0 - sec-ch-ua-platform:"" - accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 diff --git a/tests/tools/network.test.ts b/tests/tools/network.test.ts index 44c6bc9e..26e19540 100644 --- a/tests/tools/network.test.ts +++ b/tests/tools/network.test.ts @@ -12,7 +12,12 @@ import { listNetworkRequests, } from '../../src/tools/network.js'; import {serverHooks} from '../server.js'; -import {html, withMcpContext, stabilizeResponseOutput} from '../utils.js'; +import { + getTextContent, + html, + stabilizeResponseOutput, + withMcpContext, +} from '../utils.js'; describe('network', () => { const server = serverHooks(); @@ -44,7 +49,9 @@ describe('network', () => { context, ); const responseData = await response.handle('list_request', context); - t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + t.assert.snapshot?.( + stabilizeResponseOutput(getTextContent(responseData[0])), + ); }); }); @@ -69,7 +76,9 @@ describe('network', () => { context, ); const responseData = await response.handle('list_request', context); - t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + t.assert.snapshot?.( + stabilizeResponseOutput(getTextContent(responseData[0])), + ); }); }); @@ -107,7 +116,9 @@ describe('network', () => { context, ); const responseData = await response.handle('list_request', context); - t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + t.assert.snapshot?.( + stabilizeResponseOutput(getTextContent(responseData[0])), + ); }); }); }); @@ -159,7 +170,9 @@ describe('network', () => { ); const responseData = await response.handle('get_request', context); - t.assert.snapshot?.(stabilizeResponseOutput(responseData[0].text)); + t.assert.snapshot?.( + stabilizeResponseOutput(getTextContent(responseData[0])), + ); }); }); }); diff --git a/tests/utils.ts b/tests/utils.ts index dc525437..5043dd23 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type {CallToolResult} from '@modelcontextprotocol/sdk/types.js'; import logger from 'debug'; import type {Browser} from 'puppeteer'; import puppeteer, {Locator} from 'puppeteer'; @@ -21,6 +22,25 @@ import {McpContext} from '../src/McpContext.js'; import {McpResponse} from '../src/McpResponse.js'; import {stableIdSymbol} from '../src/PageCollector.js'; +export function getTextContent( + content: CallToolResult['content'][number], +): string { + if (content.type === 'text') { + return content.text; + } + throw new Error(`Expected text content but got ${content.type}`); +} + +export function getImageContent(content: CallToolResult['content'][number]): { + data: string; + mimeType: string; +} { + if (content.type === 'image') { + return {data: content.data, mimeType: content.mimeType}; + } + throw new Error(`Expected image content but got ${content.type}`); +} + const browsers = new Map(); let context: McpContext | undefined;