| 100 | } |
| 101 | |
| 102 | async function getToolGroups(page: McpPage): Promise<ToolGroups> { |
| 103 | // Check if there is a `devtoolstooldiscovery` event listener |
| 104 | const windowHandle = await page.pptrPage.evaluateHandle(() => window); |
| 105 | // @ts-expect-error internal API |
| 106 | const client = page.pptrPage._client(); |
| 107 | const {listeners}: {listeners: Protocol.DOMDebugger.EventListener[]} = |
| 108 | await client.send('DOMDebugger.getEventListeners', { |
| 109 | objectId: windowHandle.remoteObject().objectId, |
| 110 | }); |
| 111 | if (listeners.find(l => l.type === 'devtoolstooldiscovery') === undefined) { |
| 112 | return []; |
| 113 | } |
| 114 | |
| 115 | const toolGroups = await page.pptrPage.evaluate(() => { |
| 116 | if (window.__dtmcp) { |
| 117 | window.__dtmcp.toolGroups = []; |
| 118 | } |
| 119 | return new Promise<ToolGroups>(resolve => { |
| 120 | const event = new CustomEvent('devtoolstooldiscovery'); |
| 121 | const groups: ToolGroups = []; |
| 122 | // @ts-expect-error Adding custom property |
| 123 | event.respondWith = toolGroup => { |
| 124 | if (!window.__dtmcp) { |
| 125 | window.__dtmcp = {}; |
| 126 | } |
| 127 | if (!window.__dtmcp.toolGroups) { |
| 128 | window.__dtmcp.toolGroups = []; |
| 129 | } |
| 130 | |
| 131 | if ( |
| 132 | typeof toolGroup.name !== 'string' || |
| 133 | (toolGroup.description && |
| 134 | typeof toolGroup.description !== 'string') || |
| 135 | !Array.isArray(toolGroup.tools) |
| 136 | ) { |
| 137 | console.error('Invalid toolGroup:', toolGroup); |
| 138 | return; |
| 139 | } |
| 140 | for (const tool of toolGroup.tools) { |
| 141 | if ( |
| 142 | typeof tool.name !== 'string' || |
| 143 | typeof tool.description !== 'string' || |
| 144 | typeof tool.inputSchema !== 'object' || |
| 145 | typeof tool.execute !== 'function' |
| 146 | ) { |
| 147 | console.error('Invalid tool:', tool); |
| 148 | return; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | window.__dtmcp.toolGroups.push(toolGroup); |
| 153 | |
| 154 | // When receiving a toolGroup for the first time, expose a simple execution helper |
| 155 | if (!window.__dtmcp.executeTool) { |
| 156 | window.__dtmcp.executeTool = async (toolName, args) => { |
| 157 | if ( |
| 158 | !window.__dtmcp?.toolGroups || |
| 159 | window.__dtmcp.toolGroups.length === 0 |