@@ -24,65 +24,67 @@ import { packageJSON } from '../utils/package.js';
24
24
25
25
26
26
import type { Server } from '@modelcontextprotocol/sdk/server/index.js' ;
27
- import type { ToolDefinition , ServerBackend , ToolResponse } from './server.js' ;
27
+ import type { ServerBackend } from './server.js' ;
28
28
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' ;
29
+ import type { Root , Tool , CallToolResult , CallToolRequest } from '@modelcontextprotocol/sdk/types.js' ;
29
30
30
- type NonEmptyArray < T > = [ T , ...T [ ] ] ;
31
-
32
- export type MCPFactory = {
31
+ export type MCPProvider = {
33
32
name : string ;
34
33
description : string ;
35
- create ( ) : Promise < Transport > ;
34
+ connect ( ) : Promise < Transport > ;
36
35
} ;
37
36
38
- export type MCPFactoryList = NonEmptyArray < MCPFactory > ;
39
-
40
37
export class ProxyBackend implements ServerBackend {
41
38
name = 'Playwright MCP Client Switcher' ;
42
39
version = packageJSON . version ;
43
40
44
- private _mcpFactories : MCPFactoryList ;
41
+ private _mcpProviders : MCPProvider [ ] ;
45
42
private _currentClient : Client | undefined ;
46
- private _contextSwitchTool : ToolDefinition ;
47
- private _tools : ToolDefinition [ ] = [ ] ;
48
- private _server : Server | undefined ;
43
+ private _contextSwitchTool : Tool ;
44
+ private _roots : Root [ ] = [ ] ;
49
45
50
- constructor ( clientFactories : MCPFactoryList ) {
51
- this . _mcpFactories = clientFactories ;
46
+ constructor ( mcpProviders : MCPProvider [ ] ) {
47
+ this . _mcpProviders = mcpProviders ;
52
48
this . _contextSwitchTool = this . _defineContextSwitchTool ( ) ;
53
49
}
54
50
55
51
async initialize ( server : Server ) : Promise < void > {
56
- this . _server = server ;
57
- await this . _setCurrentClient ( this . _mcpFactories [ 0 ] ) ;
52
+ const version = server . getClientVersion ( ) ;
53
+ const capabilities = server . getClientCapabilities ( ) ;
54
+ if ( capabilities ?. roots && version && clientsWithRoots . includes ( version . name ) ) {
55
+ const { roots } = await server . listRoots ( ) ;
56
+ this . _roots = roots ;
57
+ }
58
+
59
+ await this . _setCurrentClient ( this . _mcpProviders [ 0 ] ) ;
58
60
}
59
61
60
- tools ( ) : ToolDefinition [ ] {
61
- if ( this . _mcpFactories . length === 1 )
62
- return this . _tools ;
62
+ async listTools ( ) : Promise < Tool [ ] > {
63
+ const response = await this . _currentClient ! . listTools ( ) ;
64
+ if ( this . _mcpProviders . length === 1 )
65
+ return response . tools ;
63
66
return [
64
- ...this . _tools ,
67
+ ...response . tools ,
65
68
this . _contextSwitchTool ,
66
69
] ;
67
70
}
68
71
69
- async callTool ( name : string , rawArguments : any ) : Promise < ToolResponse > {
72
+ async callTool ( name : string , args : CallToolRequest [ 'params' ] [ 'arguments' ] ) : Promise < CallToolResult > {
70
73
if ( name === this . _contextSwitchTool . name )
71
- return this . _callContextSwitchTool ( rawArguments ) ;
72
- const result = await this . _currentClient ! . callTool ( {
74
+ return this . _callContextSwitchTool ( args ) ;
75
+ return await this . _currentClient ! . callTool ( {
73
76
name,
74
- arguments : rawArguments ,
75
- } ) ;
76
- return result as unknown as ToolResponse ;
77
+ arguments : args ,
78
+ } ) as CallToolResult ;
77
79
}
78
80
79
81
serverClosed ?( ) : void {
80
82
void this . _currentClient ?. close ( ) . catch ( logUnhandledError ) ;
81
83
}
82
84
83
- private async _callContextSwitchTool ( params : any ) : Promise < ToolResponse > {
85
+ private async _callContextSwitchTool ( params : any ) : Promise < CallToolResult > {
84
86
try {
85
- const factory = this . _mcpFactories . find ( factory => factory . name === params . name ) ;
87
+ const factory = this . _mcpProviders . find ( factory => factory . name === params . name ) ;
86
88
if ( ! factory )
87
89
throw new Error ( 'Unknown connection method: ' + params . name ) ;
88
90
@@ -98,16 +100,16 @@ export class ProxyBackend implements ServerBackend {
98
100
}
99
101
}
100
102
101
- private _defineContextSwitchTool ( ) : ToolDefinition {
103
+ private _defineContextSwitchTool ( ) : Tool {
102
104
return {
103
105
name : 'browser_connect' ,
104
106
description : [
105
107
'Connect to a browser using one of the available methods:' ,
106
- ...this . _mcpFactories . map ( factory => `- "${ factory . name } ": ${ factory . description } ` ) ,
108
+ ...this . _mcpProviders . map ( factory => `- "${ factory . name } ": ${ factory . description } ` ) ,
107
109
] . join ( '\n' ) ,
108
110
inputSchema : zodToJsonSchema ( z . object ( {
109
- name : z . enum ( this . _mcpFactories . map ( factory => factory . name ) as [ string , ...string [ ] ] ) . default ( this . _mcpFactories [ 0 ] . name ) . describe ( 'The method to use to connect to the browser' ) ,
110
- } ) , { strictUnions : true } ) as ToolDefinition [ 'inputSchema' ] ,
111
+ name : z . enum ( this . _mcpProviders . map ( factory => factory . name ) as [ string , ...string [ ] ] ) . default ( this . _mcpProviders [ 0 ] . name ) . describe ( 'The method to use to connect to the browser' ) ,
112
+ } ) , { strictUnions : true } ) as Tool [ 'inputSchema' ] ,
111
113
annotations : {
112
114
title : 'Connect to a browser context' ,
113
115
readOnlyHint : true ,
@@ -116,7 +118,7 @@ export class ProxyBackend implements ServerBackend {
116
118
} ;
117
119
}
118
120
119
- private async _setCurrentClient ( factory : MCPFactory ) {
121
+ private async _setCurrentClient ( factory : MCPProvider ) {
120
122
await this . _currentClient ?. close ( ) ;
121
123
this . _currentClient = undefined ;
122
124
@@ -126,23 +128,13 @@ export class ProxyBackend implements ServerBackend {
126
128
listRoots : true ,
127
129
} ,
128
130
} ) ;
129
- client . setRequestHandler ( ListRootsRequestSchema , async ( ) => {
130
- const clientName = this . _server ! . getClientVersion ( ) ?. name ;
131
- if ( this . _server ! . getClientCapabilities ( ) ?. roots && (
132
- clientName === 'Visual Studio Code' ||
133
- clientName === 'Visual Studio Code - Insiders' ) ) {
134
- const { roots } = await this . _server ! . listRoots ( ) ;
135
- return { roots } ;
136
- }
137
- return { roots : [ ] } ;
138
- } ) ;
131
+ client . setRequestHandler ( ListRootsRequestSchema , ( ) => ( { roots : this . _roots } ) ) ;
139
132
client . setRequestHandler ( PingRequestSchema , ( ) => ( { } ) ) ;
140
133
141
- const transport = await factory . create ( ) ;
134
+ const transport = await factory . connect ( ) ;
142
135
await client . connect ( transport ) ;
143
-
144
136
this . _currentClient = client ;
145
- const tools = await this . _currentClient . listTools ( ) ;
146
- this . _tools = tools . tools ;
147
137
}
148
138
}
139
+
140
+ const clientsWithRoots = [ 'Visual Studio Code' , 'Visual Studio Code - Insiders' ] ;
0 commit comments