@@ -7,6 +7,7 @@ import fs from 'node:fs/promises';
77import  os  from  'node:os' ; 
88import  path  from  'node:path' ; 
99
10+ import  { extractUrlLikeFromDevToolsTitle ,  urlsEqual }  from  './DevtoolsUtils.js' ; 
1011import  type  { ListenerMap }  from  './PageCollector.js' ; 
1112import  { NetworkCollector ,  PageCollector }  from  './PageCollector.js' ; 
1213import  { Locator }  from  './third_party/index.js' ; 
@@ -40,8 +41,8 @@ export interface TextSnapshot {
4041} 
4142
4243interface  McpContextOptions  { 
43-   // Whether the DevTools windows are exposed as pages. 
44-   devtools : boolean ; 
44+   // Whether the DevTools windows are exposed as pages for debugging of DevTools . 
45+   experimentalDevToolsDebugging : boolean ; 
4546} 
4647
4748const  DEFAULT_TIMEOUT  =  5_000 ; 
@@ -82,6 +83,7 @@ export class McpContext implements Context {
8283
8384  // The most recent page state. 
8485  #pages: Page [ ]  =  [ ] ; 
86+   #pageToDevToolsPage =  new  Map < Page ,  Page > ( ) ; 
8587  #selectedPageIdx =  0 ; 
8688  // The most recent snapshot. 
8789  #textSnapshot: TextSnapshot  |  null  =  null ; 
@@ -324,19 +326,57 @@ export class McpContext implements Context {
324326   * Creates a snapshot of the pages. 
325327   */ 
326328  async  createPagesSnapshot ( ) : Promise < Page [ ] >  { 
327-     this . #pages =  ( await  this . browser . pages ( ) ) . filter ( page  =>  { 
328-       if  ( page . url ( ) . startsWith ( 'devtools://' ) )  { 
329-         return  this . #options. devtools ; 
330-       } 
331-       return  true ; 
329+     const  allPages  =  await  this . browser . pages ( ) ; 
330+ 
331+     this . #pages =  allPages . filter ( page  =>  { 
332+       // If we allow debugging DevTools windows, return all pages. 
333+       // If we are in regular mode, the user should only see non-DevTools page. 
334+       return  ( 
335+         this . #options. experimentalDevToolsDebugging  || 
336+         ! page . url ( ) . startsWith ( 'devtools://' ) 
337+       ) ; 
332338    } ) ; 
339+ 
340+     await  this . #detectOpenDevToolsWindows( allPages ) ; 
341+ 
333342    return  this . #pages; 
334343  } 
335344
345+   async  #detectOpenDevToolsWindows( pages : Page [ ] )  { 
346+     this . #pageToDevToolsPage =  new  Map < Page ,  Page > ( ) ; 
347+     for  ( const  devToolsPage  of  pages )  { 
348+       if  ( devToolsPage . url ( ) . startsWith ( 'devtools://' ) )  { 
349+         try  { 
350+           const  data  =  await  devToolsPage 
351+             // @ts -expect-error no types for _client(). 
352+             . _client ( ) 
353+             . send ( 'Target.getTargetInfo' ) ; 
354+           const  devtoolsPageTitle  =  data . targetInfo . title ; 
355+           const  urlLike  =  extractUrlLikeFromDevToolsTitle ( devtoolsPageTitle ) ; 
356+           if  ( ! urlLike )  { 
357+             continue ; 
358+           } 
359+           // TODO: lookup without a loop. 
360+           for  ( const  page  of  this . #pages)  { 
361+             if  ( urlsEqual ( page . url ( ) ,  urlLike ) )  { 
362+               this . #pageToDevToolsPage. set ( page ,  devToolsPage ) ; 
363+             } 
364+           } 
365+         }  catch  { 
366+           // no-op 
367+         } 
368+       } 
369+     } 
370+   } 
371+ 
336372  getPages ( ) : Page [ ]  { 
337373    return  this . #pages; 
338374  } 
339375
376+   getDevToolsPage ( page : Page ) : Page  |  undefined  { 
377+     return  this . #pageToDevToolsPage. get ( page ) ; 
378+   } 
379+ 
340380  /** 
341381   * Creates a text snapshot of a page. 
342382   */ 
0 commit comments