@@ -33,10 +33,12 @@ import { contextFactory } from '../browserContextFactory.js';
33
33
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' ;
34
34
import type { ClientVersion , ServerBackend } from '../mcp/server.js' ;
35
35
import type { Root , Tool , CallToolResult , CallToolRequest } from '@modelcontextprotocol/sdk/types.js' ;
36
+ import type { Browser , BrowserContext , BrowserServer } from 'playwright' ;
36
37
37
38
const contextSwitchOptions = z . object ( {
38
39
connectionString : z . string ( ) . optional ( ) . describe ( 'The connection string to use to connect to the browser' ) ,
39
40
lib : z . string ( ) . optional ( ) . describe ( 'The library to use for the connection' ) ,
41
+ debugController : z . boolean ( ) . optional ( ) . describe ( 'Enable the debug controller' )
40
42
} ) ;
41
43
42
44
class VSCodeProxyBackend implements ServerBackend {
@@ -47,15 +49,18 @@ class VSCodeProxyBackend implements ServerBackend {
47
49
private _contextSwitchTool : Tool ;
48
50
private _roots : Root [ ] = [ ] ;
49
51
private _clientVersion ?: ClientVersion ;
52
+ private _context ?: BrowserContext ;
53
+ private _browser ?: Browser ;
54
+ private _browserServer ?: BrowserServer ;
50
55
51
- constructor ( private readonly _config : FullConfig , private readonly _defaultTransportFactory : ( ) => Promise < Transport > ) {
56
+ constructor ( private readonly _config : FullConfig , private readonly _defaultTransportFactory : ( delegate : VSCodeProxyBackend ) => Promise < Transport > ) {
52
57
this . _contextSwitchTool = this . _defineContextSwitchTool ( ) ;
53
58
}
54
59
55
60
async initialize ( server : mcpServer . Server , clientVersion : ClientVersion , roots : Root [ ] ) : Promise < void > {
56
61
this . _clientVersion = clientVersion ;
57
62
this . _roots = roots ;
58
- const transport = await this . _defaultTransportFactory ( ) ;
63
+ const transport = await this . _defaultTransportFactory ( this ) ;
59
64
await this . _setCurrentClient ( transport ) ;
60
65
}
61
66
@@ -80,9 +85,47 @@ class VSCodeProxyBackend implements ServerBackend {
80
85
void this . _currentClient ?. close ( ) . catch ( logUnhandledError ) ;
81
86
}
82
87
88
+ onContext ( context : BrowserContext ) {
89
+ this . _context = context ;
90
+ context . on ( 'close' , ( ) => {
91
+ this . _context = undefined ;
92
+ } ) ;
93
+ }
94
+
95
+ private async _getDebugControllerURL ( ) {
96
+ if ( ! this . _context )
97
+ return ;
98
+
99
+ const browser = this . _context . browser ( ) as any ;
100
+ if ( ! browser || ! browser . _launchServer )
101
+ return ;
102
+
103
+ if ( this . _browser !== browser )
104
+ this . _browserServer = undefined ;
105
+
106
+ if ( ! this . _browserServer )
107
+ this . _browserServer = await browser . _launchServer ( { _debugController : true } ) as BrowserServer ;
108
+
109
+ const url = new URL ( this . _browserServer . wsEndpoint ( ) ) ;
110
+ url . searchParams . set ( 'debug-controller' , '1' ) ;
111
+ return url . toString ( ) ;
112
+ }
113
+
83
114
private async _callContextSwitchTool ( params : z . infer < typeof contextSwitchOptions > ) : Promise < CallToolResult > {
115
+ if ( params . debugController ) {
116
+ const url = await this . _getDebugControllerURL ( ) ;
117
+ const lines = [ `### Result` ] ;
118
+ if ( url ) {
119
+ lines . push ( `URL: ${ url } ` ) ;
120
+ lines . push ( `Version: ${ packageJSON . dependencies . playwright } ` ) ;
121
+ } else {
122
+ lines . push ( `No open browsers.` ) ;
123
+ }
124
+ return { content : [ { type : 'text' , text : lines . join ( '\n' ) } ] } ;
125
+ }
126
+
84
127
if ( ! params . connectionString || ! params . lib ) {
85
- const transport = await this . _defaultTransportFactory ( ) ;
128
+ const transport = await this . _defaultTransportFactory ( this ) ;
86
129
await this . _setCurrentClient ( transport ) ;
87
130
return {
88
131
content : [ { type : 'text' , text : '### Result\nSuccessfully disconnected.\n' } ] ,
@@ -142,7 +185,20 @@ export async function runVSCodeTools(config: FullConfig) {
142
185
name : 'Playwright w/ vscode' ,
143
186
nameInConfig : 'playwright-vscode' ,
144
187
version : packageJSON . version ,
145
- create : ( ) => new VSCodeProxyBackend ( config , ( ) => mcpServer . wrapInProcess ( new BrowserServerBackend ( config , contextFactory ( config ) ) ) )
188
+ create : ( ) => new VSCodeProxyBackend (
189
+ config ,
190
+ delegate => mcpServer . wrapInProcess (
191
+ new BrowserServerBackend ( config ,
192
+ {
193
+ async createContext ( clientInfo , abortSignal , toolName ) {
194
+ const context = await contextFactory ( config ) . createContext ( clientInfo , abortSignal , toolName ) ;
195
+ delegate . onContext ( context . browserContext ) ;
196
+ return context ;
197
+ } ,
198
+ }
199
+ )
200
+ )
201
+ )
146
202
} ;
147
203
await mcpServer . start ( serverBackendFactory , config . server ) ;
148
204
return ;
0 commit comments