diff --git a/src/McpPage.ts b/src/McpPage.ts index 0a32c2de8..7dd2955d7 100644 --- a/src/McpPage.ts +++ b/src/McpPage.ts @@ -14,7 +14,7 @@ import type { WebMCPTool, } from './third_party/index.js'; import {takeSnapshot} from './tools/snapshot.js'; -import type {ToolGroup, ToolDefinition} from './tools/thirdPartyDeveloper.js'; +import type {ToolGroups} from './tools/thirdPartyDeveloper.js'; import type { ContextPage, DevToolsData, @@ -59,7 +59,7 @@ export class McpPage implements ContextPage { #dialog?: Dialog; #dialogHandler: (dialog: Dialog) => void; - thirdPartyDeveloperTools: ToolGroup | undefined; + thirdPartyDeveloperTools: ToolGroups = []; constructor(page: Page, id: number) { this.pptrPage = page; @@ -90,7 +90,7 @@ export class McpPage implements ContextPage { } } - getThirdPartyDeveloperTools(): ToolGroup | undefined { + getThirdPartyDeveloperTools(): ToolGroups { return this.thirdPartyDeveloperTools; } diff --git a/src/McpResponse.ts b/src/McpResponse.ts index b00fba007..9dfea8be2 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -28,7 +28,7 @@ import type { Extension, } from './third_party/index.js'; import {handleDialog} from './tools/pages.js'; -import type {ToolGroup, ToolDefinition} from './tools/thirdPartyDeveloper.js'; +import type {ToolGroups} from './tools/thirdPartyDeveloper.js'; import type { DevToolsData, ImageContentData, @@ -99,9 +99,7 @@ export function replaceHtmlElementsWithUids(schema: JSONSchema7Definition) { } } -async function getToolGroup( - page: McpPage, -): Promise | undefined | null> { +async function getToolGroups(page: McpPage): Promise { // Check if there is a `devtoolstooldiscovery` event listener const windowHandle = await page.pptrPage.evaluateHandle(() => window); // @ts-expect-error internal API @@ -111,51 +109,88 @@ async function getToolGroup( objectId: windowHandle.remoteObject().objectId, }); if (listeners.find(l => l.type === 'devtoolstooldiscovery') === undefined) { - return; + return []; } - const toolGroup = await page.pptrPage.evaluate(() => { - return new Promise | undefined | null>( - resolve => { - const event = new CustomEvent('devtoolstooldiscovery'); - // @ts-expect-error Adding custom property - event.respondWith = (toolGroup: ToolGroup) => { - if (!window.__dtmcp) { - window.__dtmcp = {}; + const toolGroups = await page.pptrPage.evaluate(() => { + return new Promise(resolve => { + const event = new CustomEvent('devtoolstooldiscovery'); + const groups: ToolGroups = []; + // @ts-expect-error Adding custom property + event.respondWith = toolGroup => { + if (!window.__dtmcp) { + window.__dtmcp = {}; + } + if (!window.__dtmcp.toolGroups) { + window.__dtmcp.toolGroups = []; + } + + if ( + typeof toolGroup.name !== 'string' || + typeof toolGroup.description !== 'string' || + !Array.isArray(toolGroup.tools) + ) { + console.error('Invalid toolGroup:', toolGroup); + return; + } + for (const tool of toolGroup.tools) { + if ( + typeof tool.name !== 'string' || + typeof tool.description !== 'string' || + typeof tool.inputSchema !== 'object' || + typeof tool.execute !== 'function' + ) { + console.error('Invalid tool:', tool); + return; } - window.__dtmcp.toolGroup = toolGroup; + } - // When receiving a toolGroup for the first time, expose a simple execution helper - if (!window.__dtmcp.executeTool) { - window.__dtmcp.executeTool = async (toolName, args) => { - if (!window.__dtmcp?.toolGroup) { - throw new Error('No tools found on the page'); - } - const tool = window.__dtmcp.toolGroup.tools.find( - t => t.name === toolName, - ); - if (!tool) { - throw new Error(`Tool ${toolName} not found`); + window.__dtmcp.toolGroups.push(toolGroup); + + // When receiving a toolGroup for the first time, expose a simple execution helper + if (!window.__dtmcp.executeTool) { + window.__dtmcp.executeTool = async (toolName, args) => { + if ( + !window.__dtmcp?.toolGroups || + window.__dtmcp.toolGroups.length === 0 + ) { + throw new Error('No tools found on the page'); + } + for (const group of window.__dtmcp.toolGroups) { + const tool = group.tools?.find(t => t.name === toolName); + if (tool) { + return await tool.execute(args); } - return await tool.execute(args); - }; - } + } + throw new Error(`Tool ${toolName} not found`); + }; + } - resolve(toolGroup); - }; - window.dispatchEvent(event); - // If the page does not synchronously call `event.respondWith`, return instead of timing out + groups.push(toolGroup); + }; + window.dispatchEvent(event); + // If at least one toolGroup was added synchronously, resolve with the array. + // Otherwise, use setTimeout to allow for any microtask/asynchronous respondWith calls, or resolve with an empty array. + if (groups.length > 0) { + resolve(groups); + } else { setTimeout(() => { - resolve(null); + if (groups.length > 0) { + resolve(groups); + } else { + resolve([]); + } }, 0); - }, - ); + } + }); }); - for (const tool of toolGroup?.tools ?? []) { - replaceHtmlElementsWithUids(tool.inputSchema); + for (const group of toolGroups) { + for (const tool of group.tools ?? []) { + replaceHtmlElementsWithUids(tool.inputSchema); + } } - return toolGroup; + return toolGroups; } export class McpResponse implements Response { @@ -562,14 +597,13 @@ export class McpResponse implements Response { extensions = await context.listExtensions(); } - // Null indicates no tools. - let thirdPartyDeveloperTools: ToolGroup | undefined | null; + let thirdPartyDeveloperTools: ToolGroups = []; if ( this.#args.categoryExperimentalThirdParty && this.#listThirdPartyDeveloperTools && this.#page ) { - thirdPartyDeveloperTools = await getToolGroup(this.#page); + thirdPartyDeveloperTools = await getToolGroups(this.#page); if (thirdPartyDeveloperTools) { this.#page.thirdPartyDeveloperTools = thirdPartyDeveloperTools; } @@ -707,7 +741,7 @@ export class McpResponse implements Response { traceInsight?: TraceInsightData; extensions?: Map; lighthouseResult?: LighthouseData; - thirdPartyDeveloperTools?: ToolGroup | null; + thirdPartyDeveloperTools: ToolGroups; webmcpTools?: WebMCPTool[]; errorMessage?: string; }, @@ -724,7 +758,7 @@ export class McpResponse implements Response { traceInsights?: Array<{insightName: string; insightKey: string}>; lighthouseResult?: object; extensions?: object[]; - thirdPartyDeveloperTools?: object; + thirdPartyDeveloperTools?: object[]; webmcpTools?: object[]; message?: string; networkConditions?: string; @@ -1052,19 +1086,11 @@ Call ${handleDialog.name} to handle it before continuing.`); } } - if (data.thirdPartyDeveloperTools !== undefined) { - if (data.thirdPartyDeveloperTools) { - structuredContent.thirdPartyDeveloperTools = - data.thirdPartyDeveloperTools; - } + if (data.thirdPartyDeveloperTools.length) { + structuredContent.thirdPartyDeveloperTools = + data.thirdPartyDeveloperTools; response.push('## Third-party developer tools'); - if ( - data.thirdPartyDeveloperTools === null || - !data.thirdPartyDeveloperTools?.tools - ) { - response.push('No third-party developer tools available.'); - } else { - const toolGroup = data.thirdPartyDeveloperTools; + for (const toolGroup of data.thirdPartyDeveloperTools) { response.push(`${toolGroup.name}: ${toolGroup.description}`); response.push('Available tools:'); const toolDefinitionsMessage = toolGroup.tools diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 7350b2b45..ee6cad5dc 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -27,10 +27,7 @@ import type {PaginationOptions} from '../utils/types.js'; import type {WaitForEventsResult} from '../WaitForHelper.js'; import type {ToolCategory} from './categories.js'; -import type { - ToolGroup, - ToolDefinition as ThirdPartyDeveloperToolDefinition, -} from './thirdPartyDeveloper.js'; +import type {ToolGroups} from './thirdPartyDeveloper.js'; export interface BaseToolDefinition< Schema extends zod.ZodRawShape = zod.ZodRawShape, @@ -270,9 +267,8 @@ export type ContextPage = Readonly<{ action: () => Promise, options?: {timeout?: number; handleDialog?: 'accept' | 'dismiss' | string}, ): Promise; - getThirdPartyDeveloperTools(): - | ToolGroup - | undefined; + getThirdPartyDeveloperTools(): ToolGroups; + executeThirdPartyDeveloperTool( toolName: string, params: Record, diff --git a/src/tools/thirdPartyDeveloper.ts b/src/tools/thirdPartyDeveloper.ts index 3a1ffb939..31246b438 100644 --- a/src/tools/thirdPartyDeveloper.ts +++ b/src/tools/thirdPartyDeveloper.ts @@ -21,11 +21,17 @@ export interface ToolGroup { tools: T[]; } +export type ToolGroups = Array>; + declare global { interface Window { __dtmcp?: { - toolGroup?: ToolGroup< - ToolDefinition & {execute: (args: Record) => unknown} + toolGroups?: Array< + ToolGroup< + ToolDefinition & { + execute: (args: Record) => unknown; + } + > >; stashedElements?: Element[]; executeTool?: ( @@ -90,8 +96,16 @@ export const executeThirdPartyDeveloperTool = definePageTool({ } } - const toolGroup = request.page.getThirdPartyDeveloperTools(); - const tool = toolGroup?.tools.find(t => t.name === toolName); + const toolGroups = request.page.getThirdPartyDeveloperTools(); + let tool; + if (toolGroups) { + for (const group of toolGroups) { + tool = group.tools?.find(t => t.name === toolName); + if (tool) { + break; + } + } + } if (!tool) { throw new Error(`Tool ${toolName} not found`); } diff --git a/tests/McpResponse.test.js.snapshot b/tests/McpResponse.test.js.snapshot index b98a0aef4..52652269d 100644 --- a/tests/McpResponse.test.js.snapshot +++ b/tests/McpResponse.test.js.snapshot @@ -1261,24 +1261,26 @@ name="myTool", description="Does something", inputSchema={"type":"object","prope exports[`third-party developer tools > lists third-party developer tools 2`] = ` { - "thirdPartyDeveloperTools": { - "name": "My Tool Group", - "description": "A group of tools", - "tools": [ - { - "name": "myTool", - "description": "Does something", - "inputSchema": { - "type": "object", - "properties": { - "foo": { - "type": "string" + "thirdPartyDeveloperTools": [ + { + "name": "My Tool Group", + "description": "A group of tools", + "tools": [ + { + "name": "myTool", + "description": "Does something", + "inputSchema": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } } } } - } - ] - } + ] + } + ] } `; diff --git a/tests/McpResponse.test.ts b/tests/McpResponse.test.ts index d63bf7ba5..1d1e67031 100644 --- a/tests/McpResponse.test.ts +++ b/tests/McpResponse.test.ts @@ -1070,22 +1070,24 @@ describe('third-party developer tools', () => { async (response, context) => { const mcpPage = context.getSelectedMcpPage(); stubToolDiscovery(mcpPage.pptrPage); - sinon.stub(mcpPage.pptrPage, 'evaluate').resolves({ - name: 'My Tool Group', - description: 'A group of tools', - tools: [ - { - name: 'myTool', - description: 'Does something', - inputSchema: { - type: 'object', - properties: { - foo: {type: 'string'}, + sinon.stub(mcpPage.pptrPage, 'evaluate').resolves([ + { + name: 'My Tool Group', + description: 'A group of tools', + tools: [ + { + name: 'myTool', + description: 'Does something', + inputSchema: { + type: 'object', + properties: { + foo: {type: 'string'}, + }, }, }, - }, - ], - }); + ], + }, + ]); response.setListThirdPartyDeveloperTools(); const {content, structuredContent} = await response.handle( 'test', diff --git a/tests/tools/thirdPartyDeveloper.test.ts b/tests/tools/thirdPartyDeveloper.test.ts index 1514e2f0a..9e35c0f2e 100644 --- a/tests/tools/thirdPartyDeveloper.test.ts +++ b/tests/tools/thirdPartyDeveloper.test.ts @@ -17,10 +17,7 @@ import { executeThirdPartyDeveloperTool, listThirdPartyDeveloperTools, } from '../../src/tools/thirdPartyDeveloper.js'; -import type { - ToolGroup, - ToolDefinition, -} from '../../src/tools/thirdPartyDeveloper.js'; +import type {ToolGroups} from '../../src/tools/thirdPartyDeveloper.js'; import {withMcpContext} from '../utils.js'; describe('thirdPartyDeveloperTools', () => { @@ -33,27 +30,29 @@ describe('thirdPartyDeveloperTools', () => { await page.pptrPage.evaluate(() => { window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: { - type: 'object', - properties: { - arg: {type: 'string'}, + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: { + type: 'object', + properties: { + arg: {type: 'string'}, + }, }, + execute: () => 'result', }, - execute: () => 'result', - }, - ], - }, + ], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -68,7 +67,9 @@ describe('thirdPartyDeveloperTools', () => { context, ); // @ts-expect-error `structuredContent` has `thirdPartyDeveloperTools` - const actualGroup = result.structuredContent.thirdPartyDeveloperTools; + const groups = result.structuredContent.thirdPartyDeveloperTools; + assert.strictEqual(groups.length, 1); + const actualGroup = groups[0]; assert.strictEqual(actualGroup.name, 'test-group'); assert.strictEqual(actualGroup.description, 'test description'); assert.strictEqual(actualGroup.tools.length, 1); @@ -111,14 +112,14 @@ describe('thirdPartyDeveloperTools', () => { 'list_3p_developer_tools', context, ); - assert.ok('thirdPartyDeveloperTools' in result.structuredContent); - assert.deepEqual( + assert.ok(result.structuredContent); + assert.deepStrictEqual( ( result.structuredContent as { - thirdPartyDeveloperTools: ToolGroup; + thirdPartyDeveloperTools?: ToolGroups; } ).thirdPartyDeveloperTools, - {}, + undefined, ); }, undefined, @@ -147,10 +148,11 @@ describe('thirdPartyDeveloperTools', () => { 'list_3p_developer_tools', context, ); - assert.strictEqual( + assert.ok(result.structuredContent); + assert.deepStrictEqual( ( result.structuredContent as { - thirdPartyDeveloperTools: ToolGroup; + thirdPartyDeveloperTools?: ToolGroups; } ).thirdPartyDeveloperTools, undefined, @@ -176,9 +178,13 @@ describe('thirdPartyDeveloperTools', () => { 'list_3p_developer_tools', context, ); - assert.strictEqual( - (result.structuredContent as {thirdPartyDeveloperTools: undefined}) - .thirdPartyDeveloperTools, + assert.ok(result.structuredContent); + assert.deepStrictEqual( + ( + result.structuredContent as { + thirdPartyDeveloperTools?: ToolGroups; + } + ).thirdPartyDeveloperTools, undefined, ); }, @@ -186,6 +192,68 @@ describe('thirdPartyDeveloperTools', () => { {categoryExperimentalThirdParty: true} as ParsedArguments, ); }); + + it('lists multiple toolgroups', async () => { + await withMcpContext( + async (response, context) => { + const page = await context.newPage(); + response.setPage(page); + + await page.pptrPage.evaluate(() => { + window.addEventListener('devtoolstooldiscovery', (e: Event) => { + // @ts-expect-error Event has `respondWith` + e.respondWith?.({ + name: 'group-1', + description: 'desc-1', + tools: [ + { + name: 'tool-1', + description: 'tool-1-desc', + inputSchema: {}, + execute: () => 'r1', + }, + ], + }); + }); + window.addEventListener('devtoolstooldiscovery', (e: Event) => { + // @ts-expect-error Event has `respondWith` + e.respondWith?.({ + name: 'group-2', + description: 'desc-2', + tools: [ + { + name: 'tool-2', + description: 'tool-2-desc', + inputSchema: {}, + execute: () => 'r2', + }, + ], + }); + }); + }); + + await listThirdPartyDeveloperTools.handler( + {params: {}, page}, + response, + context, + ); + + const result = await response.handle( + 'list_3p_developer_tools', + context, + ); + const actualGroups = + // @ts-expect-error structuredContent has `thirdPartyDeveloperTools` + result.structuredContent.thirdPartyDeveloperTools; + assert.ok(actualGroups); + assert.strictEqual(actualGroups.length, 2); + assert.strictEqual(actualGroups[0].name, 'group-1'); + assert.strictEqual(actualGroups[1].name, 'group-2'); + }, + undefined, + {categoryExperimentalThirdParty: true} as ParsedArguments, + ); + }); }); describe('execute_3p_developer_tool', () => { @@ -210,28 +278,30 @@ describe('thirdPartyDeveloperTools', () => { async (response, context) => { await setupThirdPartyDeveloperTools(response, context, () => { window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: { - type: 'object', - properties: { - arg: {type: 'string'}, + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: { + type: 'object', + properties: { + arg: {type: 'string'}, + }, + required: ['arg'], }, - required: ['arg'], + execute: () => 'result', }, - execute: () => 'result', - }, - ], - }, + ], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -260,15 +330,17 @@ describe('thirdPartyDeveloperTools', () => { await withMcpContext(async (response, context) => { await setupThirdPartyDeveloperTools(response, context, () => { window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [], - }, + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -296,28 +368,30 @@ describe('thirdPartyDeveloperTools', () => { async (response, context) => { await setupThirdPartyDeveloperTools(response, context, () => { window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: { - type: 'object', - properties: { - arg: {type: 'string'}, + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: { + type: 'object', + properties: { + arg: {type: 'string'}, + }, + required: ['arg'], }, - required: ['arg'], + execute: () => 'result', }, - execute: () => 'result', - }, - ], - }, + ], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -348,22 +422,24 @@ describe('thirdPartyDeveloperTools', () => { async (response, context) => { await setupThirdPartyDeveloperTools(response, context, () => { window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: {}, - execute: () => ({foo: 'bar'}), - }, - ], - }, + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: {}, + execute: () => ({foo: 'bar'}), + }, + ], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -393,23 +469,25 @@ describe('thirdPartyDeveloperTools', () => { const page = await context.newPage(); response.setPage(page); - page.thirdPartyDeveloperTools = { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: { - type: 'object', - properties: { - element: {type: 'object'}, + page.thirdPartyDeveloperTools = [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: { + type: 'object', + properties: { + element: {type: 'object'}, + }, + required: ['element'], }, - required: ['element'], }, - }, - ], - }; + ], + }, + ]; await page.pptrPage.evaluate(() => { window.__dtmcp = { @@ -484,25 +562,27 @@ describe('thirdPartyDeveloperTools', () => { async (response, context) => { await setupThirdPartyDeveloperTools(response, context, () => { window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: {}, - execute: () => ({ - foo: 'bar', - func: () => undefined, - }), - }, - ], - }, + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: {}, + execute: () => ({ + foo: 'bar', + func: () => undefined, + }), + }, + ], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -532,26 +612,28 @@ describe('thirdPartyDeveloperTools', () => { async (response, context) => { await setupThirdPartyDeveloperTools(response, context, () => { window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: {}, - execute: () => { - const obj: Record = {foo: 'bar'}; - obj.self = obj; - return obj; + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: {}, + execute: () => { + const obj: Record = {foo: 'bar'}; + obj.self = obj; + return obj; + }, }, - }, - ], - }, + ], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -584,25 +666,27 @@ describe('thirdPartyDeveloperTools', () => { val = 'value'; } window.__dtmcp = { - toolGroup: { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: {}, - execute: () => ({ - foo: 'bar', - custom: new CustomClass(), - }), - }, - ], - }, + toolGroups: [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: {}, + execute: () => ({ + foo: 'bar', + custom: new CustomClass(), + }), + }, + ], + }, + ], }; window.addEventListener('devtoolstooldiscovery', (e: Event) => { // @ts-expect-error Event has `respondWith` - e.respondWith(window.__dtmcp?.toolGroup); + e.respondWith(window.__dtmcp?.toolGroups[0]); }); }); @@ -637,17 +721,19 @@ describe('thirdPartyDeveloperTools', () => { const page = await context.newPage(); response.setPage(page); - page.thirdPartyDeveloperTools = { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: {}, - }, - ], - }; + page.thirdPartyDeveloperTools = [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: {}, + }, + ], + }, + ]; await page.pptrPage.evaluate(() => { window.__dtmcp = { @@ -694,17 +780,19 @@ describe('thirdPartyDeveloperTools', () => { const page = await context.newPage(); response.setPage(page); - page.thirdPartyDeveloperTools = { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: {}, - }, - ], - }; + page.thirdPartyDeveloperTools = [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: {}, + }, + ], + }, + ]; await page.pptrPage.evaluate(() => { window.__dtmcp = { @@ -760,17 +848,19 @@ describe('thirdPartyDeveloperTools', () => { const page = await context.newPage(); response.setPage(page); - page.thirdPartyDeveloperTools = { - name: 'test-group', - description: 'test description', - tools: [ - { - name: 'test-tool', - description: 'test tool description', - inputSchema: {}, - }, - ], - }; + page.thirdPartyDeveloperTools = [ + { + name: 'test-group', + description: 'test description', + tools: [ + { + name: 'test-tool', + description: 'test tool description', + inputSchema: {}, + }, + ], + }, + ]; await page.pptrPage.evaluate(() => { window.__dtmcp = {