chore: mcp backend switcher (#854)

This commit is contained in:
Yury Semikhatsky
2025-08-11 14:16:43 -07:00
committed by GitHub
parent db9cfe1720
commit f010164bf1
10 changed files with 224 additions and 70 deletions

View File

@@ -15,7 +15,6 @@
*/
import { fileURLToPath } from 'url';
import { z } from 'zod';
import { FullConfig } from './config.js';
import { Context } from './context.js';
import { logUnhandledError } from './log.js';
@@ -23,17 +22,12 @@ import { Response } from './response.js';
import { SessionLog } from './sessionLog.js';
import { filteredTools } from './tools.js';
import { packageJSON } from './package.js';
import { defineTool } from './tools/tool.js';
import type { Tool } from './tools/tool.js';
import type { BrowserContextFactory } from './browserContextFactory.js';
import type * as mcpServer from './mcp/server.js';
import type { ServerBackend } from './mcp/server.js';
type NonEmptyArray<T> = [T, ...T[]];
export type FactoryList = NonEmptyArray<BrowserContextFactory>;
export class BrowserServerBackend implements ServerBackend {
name = 'Playwright';
version = packageJSON.version;
@@ -44,12 +38,10 @@ export class BrowserServerBackend implements ServerBackend {
private _config: FullConfig;
private _browserContextFactory: BrowserContextFactory;
constructor(config: FullConfig, factories: FactoryList) {
constructor(config: FullConfig, factory: BrowserContextFactory) {
this._config = config;
this._browserContextFactory = factories[0];
this._browserContextFactory = factory;
this._tools = filteredTools(config);
if (factories.length > 1)
this._tools.push(this._defineContextSwitchTool(factories));
}
async initialize(server: mcpServer.Server): Promise<void> {
@@ -77,8 +69,9 @@ export class BrowserServerBackend implements ServerBackend {
return this._tools.map(tool => tool.schema);
}
async callTool(schema: mcpServer.ToolSchema<any>, parsedArguments: any) {
async callTool(schema: mcpServer.ToolSchema<any>, rawArguments: any) {
const context = this._context!;
const parsedArguments = schema.inputSchema.parse(rawArguments || {});
const response = new Response(context, schema.name, parsedArguments);
const tool = this._tools.find(tool => tool.schema.name === schema.name)!;
context.setRunningTool(true);
@@ -97,46 +90,4 @@ export class BrowserServerBackend implements ServerBackend {
serverClosed() {
void this._context!.dispose().catch(logUnhandledError);
}
private _defineContextSwitchTool(factories: FactoryList): Tool<any> {
const self = this;
return defineTool({
capability: 'core',
schema: {
name: 'browser_connect',
title: 'Connect to a browser context',
description: [
'Connect to a browser using one of the available methods:',
...factories.map(factory => `- "${factory.name}": ${factory.description}`),
].join('\n'),
inputSchema: z.object({
method: z.enum(factories.map(factory => factory.name) as [string, ...string[]]).default(factories[0].name).describe('The method to use to connect to the browser'),
}),
type: 'readOnly',
},
async handle(context, params, response) {
const factory = factories.find(factory => factory.name === params.method);
if (!factory) {
response.addError('Unknown connection method: ' + params.method);
return;
}
await self._setContextFactory(factory);
response.addResult('Successfully changed connection method.');
}
});
}
private async _setContextFactory(newFactory: BrowserContextFactory) {
if (this._context) {
const options = {
...this._context.options,
browserContextFactory: newFactory,
};
await this._context.dispose();
this._context = new Context(options);
}
this._browserContextFactory = newFactory;
}
}