1717import { debug } from 'playwright-core/lib/utilsBundle' ;
1818import { ManualPromise } from 'playwright-core/lib/utils' ;
1919
20- import { defineToolSchema } from './tool' ;
2120import * as mcpBundle from './bundle' ;
2221import * as mcpServer from './server' ;
2322import * as mcpHttp from './http' ;
@@ -27,7 +26,11 @@ import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
2726
2827const mdbDebug = debug ( 'pw:mcp:mdb' ) ;
2928const errorsDebug = debug ( 'pw:mcp:errors' ) ;
30- const z = mcpBundle . z ;
29+
30+ export type MDBPushClientCallback = ( mcpUrl : string , introMessage ?: string ) => Promise < void > ;
31+ export type MDBServerBackendFactory = Omit < mcpServer . ServerBackendFactory , 'create' > & {
32+ create : ( pushClient : MDBPushClientCallback ) => mcpServer . ServerBackend ;
33+ } ;
3134
3235export class MDBBackend implements mcpServer . ServerBackend {
3336 private _onPauseClient : { client : Client , tools : mcpServer . Tool [ ] , transport : StreamableHTTPClientTransport } | undefined ;
@@ -37,8 +40,8 @@ export class MDBBackend implements mcpServer.ServerBackend {
3740 private _progress : mcpServer . CallToolResult [ 'content' ] = [ ] ;
3841 private _progressCallback : mcpServer . ProgressCallback ;
3942
40- constructor ( mainBackend : mcpServer . ServerBackend ) {
41- this . _mainBackend = mainBackend ;
43+ constructor ( mainBackendFactory : MDBServerBackendFactory ) {
44+ this . _mainBackend = mainBackendFactory . create ( this . _createOnPauseClient . bind ( this ) ) ;
4245 this . _progressCallback = ( params : mcpServer . ProgressParams ) => {
4346 if ( params . message )
4447 this . _progress . push ( { type : 'text' , text : params . message } ) ;
@@ -57,11 +60,6 @@ export class MDBBackend implements mcpServer.ServerBackend {
5760 }
5861
5962 async callTool ( name : string , args : mcpServer . CallToolRequest [ 'params' ] [ 'arguments' ] ) : Promise < mcpServer . CallToolResult > {
60- if ( name === pushToolsSchema . name ) {
61- await this . _createOnPauseClient ( pushToolsSchema . inputSchema . parse ( args || { } ) ) ;
62- return { content : [ { type : 'text' , text : 'Tools pushed' } ] } ;
63- }
64-
6563 if ( this . _onPauseClient ?. tools . find ( tool => tool . name === name ) ) {
6664 const result = await this . _onPauseClient . client . callTool ( {
6765 name,
@@ -95,16 +93,16 @@ export class MDBBackend implements mcpServer.ServerBackend {
9593 return result ;
9694 }
9795
98- private async _createOnPauseClient ( params : { mcpUrl : string , introMessage ?: string } ) {
96+ private async _createOnPauseClient ( mcpUrl : string , introMessage ?: string ) {
9997 if ( this . _onPauseClient )
10098 await this . _onPauseClient . client . close ( ) . catch ( errorsDebug ) ;
10199
102- this . _onPauseClient = await this . _createClient ( params . mcpUrl ) ;
100+ this . _onPauseClient = await this . _createClient ( mcpUrl ) ;
103101
104102 this . _interruptPromise ?. resolve ( {
105103 content : [ {
106104 type : 'text' ,
107- text : params . introMessage || '' ,
105+ text : introMessage || '' ,
108106 } ] ,
109107 } ) ;
110108 this . _interruptPromise = undefined ;
@@ -128,100 +126,18 @@ export class MDBBackend implements mcpServer.ServerBackend {
128126 }
129127}
130128
131- const pushToolsSchema = defineToolSchema ( {
132- name : 'mdb_push_tools' ,
133- title : 'Push MCP tools to the tools stack' ,
134- description : 'Push MCP tools to the tools stack' ,
135- inputSchema : z . object ( {
136- mcpUrl : z . string ( ) ,
137- introMessage : z . string ( ) . optional ( ) ,
138- } ) ,
139- type : 'readOnly' ,
140- } ) ;
141-
142- export async function runMainBackend ( backendFactory : mcpServer . ServerBackendFactory , options ?: { port ?: number } ) : Promise < string | undefined > {
143- const mdbBackend = new MDBBackend ( backendFactory . create ( ) ) ;
144- // Start HTTP unconditionally.
129+ // TODO: add all options from mcpHttp.startHttpServer.
130+ export async function runMainBackend ( backendFactory : MDBServerBackendFactory , options ?: { port ?: number } ) : Promise < string | undefined > {
131+ const mdbBackend = new MDBBackend ( backendFactory ) ;
145132 const factory : mcpServer . ServerBackendFactory = {
146133 ...backendFactory ,
147134 create : ( ) => mdbBackend
148135 } ;
149- const url = await startAsHttp ( factory , { port : options ?. port || 0 } ) ;
150- process . env . PLAYWRIGHT_DEBUGGER_MCP = url ;
151-
152- if ( options ?. port !== undefined )
153- return url ;
154-
155- // Start stdio conditionally.
156- await mcpServer . connect ( factory , new mcpBundle . StdioServerTransport ( ) , false ) ;
157- }
158-
159- export async function runOnPauseBackendLoop ( backend : mcpServer . ServerBackend , introMessage : string ) {
160- const wrappedBackend = new ServerBackendWithCloseListener ( backend ) ;
161-
162- const factory = {
163- name : 'on-pause-backend' ,
164- nameInConfig : 'on-pause-backend' ,
165- version : '0.0.0' ,
166- create : ( ) => wrappedBackend ,
167- } ;
168-
169- const httpServer = await mcpHttp . startHttpServer ( { port : 0 } ) ;
170- const url = await mcpHttp . installHttpTransport ( httpServer , factory , true ) ;
171-
172- const client = new mcpBundle . Client ( { name : 'Pushing client' , version : '0.0.0' } ) ;
173- client . setRequestHandler ( mcpBundle . PingRequestSchema , ( ) => ( { } ) ) ;
174- const transport = new mcpBundle . StreamableHTTPClientTransport ( new URL ( process . env . PLAYWRIGHT_DEBUGGER_MCP ! ) ) ;
175- await client . connect ( transport ) ;
176-
177- const pushToolsResult = await client . callTool ( {
178- name : pushToolsSchema . name ,
179- arguments : {
180- mcpUrl : url ,
181- introMessage,
182- } ,
183- } ) ;
184- if ( pushToolsResult . isError )
185- errorsDebug ( 'Failed to push tools' , pushToolsResult . content ) ;
186- await transport . terminateSession ( ) ;
187- await client . close ( ) ;
188-
189- await wrappedBackend . waitForClosed ( ) ;
190- httpServer . close ( ) ;
191- }
192-
193- async function startAsHttp ( backendFactory : mcpServer . ServerBackendFactory , options : { port : number } ) {
194- const httpServer = await mcpHttp . startHttpServer ( options ) ;
195- return await mcpHttp . installHttpTransport ( httpServer , backendFactory , true ) ;
196- }
197136
198-
199- class ServerBackendWithCloseListener implements mcpServer . ServerBackend {
200- private _backend : mcpServer . ServerBackend ;
201- private _serverClosedPromise = new ManualPromise < void > ( ) ;
202-
203- constructor ( backend : mcpServer . ServerBackend ) {
204- this . _backend = backend ;
205- }
206-
207- async initialize ( server : mcpServer . Server , clientInfo : mcpServer . ClientInfo ) : Promise < void > {
208- await this . _backend . initialize ?.( server , clientInfo ) ;
209- }
210-
211- async listTools ( ) : Promise < mcpServer . Tool [ ] > {
212- return this . _backend . listTools ( ) ;
213- }
214-
215- async callTool ( name : string , args : mcpServer . CallToolRequest [ 'params' ] [ 'arguments' ] , progress : mcpServer . ProgressCallback ) : Promise < mcpServer . CallToolResult > {
216- return this . _backend . callTool ( name , args , progress ) ;
137+ if ( options ?. port !== undefined ) {
138+ const httpServer = await mcpHttp . startHttpServer ( options ) ;
139+ return await mcpHttp . installHttpTransport ( httpServer , factory , true ) ;
217140 }
218141
219- serverClosed ( server : mcpServer . Server ) {
220- this . _backend . serverClosed ?.( server ) ;
221- this . _serverClosedPromise . resolve ( ) ;
222- }
223-
224- async waitForClosed ( ) {
225- await this . _serverClosedPromise ;
226- }
142+ await mcpServer . connect ( factory , new mcpBundle . StdioServerTransport ( ) , false ) ;
227143}
0 commit comments