chore(extension): do not show tab selector for browser_navigate (#923)

This commit is contained in:
Yury Semikhatsky
2025-08-22 10:02:09 -07:00
committed by GitHub
parent fb65bc7559
commit 64af5f8763
8 changed files with 52 additions and 26 deletions

View File

@@ -42,7 +42,7 @@ export function contextFactory(config: FullConfig): BrowserContextFactory {
export type ClientInfo = { name?: string, version?: string, rootPath?: string };
export interface BrowserContextFactory {
createContext(clientInfo: ClientInfo, abortSignal: AbortSignal): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }>;
createContext(clientInfo: ClientInfo, abortSignal: AbortSignal, toolName: string | undefined): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }>;
}
class BaseContextFactory implements BrowserContextFactory {

View File

@@ -69,7 +69,7 @@ export class BrowserServerBackend implements ServerBackend {
const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});
const context = this._context!;
const response = new Response(context, name, parsedArguments);
context.setRunningTool(true);
context.setRunningTool(name);
try {
await tool.handle(context, parsedArguments, response);
await response.finish();
@@ -77,7 +77,7 @@ export class BrowserServerBackend implements ServerBackend {
} catch (error: any) {
response.addError(String(error));
} finally {
context.setRunningTool(false);
context.setRunningTool(undefined);
}
return response.serialize();
}

View File

@@ -50,7 +50,7 @@ export class Context {
private static _allContexts: Set<Context> = new Set();
private _closeBrowserContextPromise: Promise<void> | undefined;
private _isRunningTool: boolean = false;
private _runningToolName: string | undefined;
private _abortController = new AbortController();
constructor(options: ContextOptions) {
@@ -145,11 +145,11 @@ export class Context {
}
isRunningTool() {
return this._isRunningTool;
return this._runningToolName !== undefined;
}
setRunningTool(isRunningTool: boolean) {
this._isRunningTool = isRunningTool;
setRunningTool(name: string | undefined) {
this._runningToolName = name;
}
private async _closeBrowserContextImpl() {
@@ -202,7 +202,7 @@ export class Context {
if (this._closeBrowserContextPromise)
throw new Error('Another browser context is being closed.');
// TODO: move to the browser context factory to make it based on isolation mode.
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal);
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal, this._runningToolName);
const { browserContext } = result;
await this._setupRequestInterception(browserContext);
if (this.sessionLog)

View File

@@ -94,11 +94,11 @@ export class CDPRelayServer {
return `${this._wsHost}${this._extensionPath}`;
}
async ensureExtensionConnectionForMCPContext(clientInfo: ClientInfo, abortSignal: AbortSignal) {
async ensureExtensionConnectionForMCPContext(clientInfo: ClientInfo, abortSignal: AbortSignal, toolName: string | undefined) {
debugLogger('Ensuring extension connection for MCP context');
if (this._extensionConnection)
return;
this._connectBrowser(clientInfo);
this._connectBrowser(clientInfo, toolName);
debugLogger('Waiting for incoming extension connection');
await Promise.race([
this._extensionConnectionPromise,
@@ -110,7 +110,7 @@ export class CDPRelayServer {
debugLogger('Extension connection established');
}
private _connectBrowser(clientInfo: ClientInfo) {
private _connectBrowser(clientInfo: ClientInfo, toolName: string | undefined) {
const mcpRelayEndpoint = `${this._wsHost}${this._extensionPath}`;
// Need to specify "key" in the manifest.json to make the id stable when loading from file.
const url = new URL('chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html');
@@ -121,6 +121,8 @@ export class CDPRelayServer {
};
url.searchParams.set('client', JSON.stringify(client));
url.searchParams.set('pwMcpVersion', packageJSON.version);
if (toolName)
url.searchParams.set('newTab', String(toolName === 'browser_navigate'));
const href = url.toString();
const executableInfo = registry.findExecutable(this._browserChannel);
if (!executableInfo)

View File

@@ -32,8 +32,8 @@ export class ExtensionContextFactory implements BrowserContextFactory {
this._userDataDir = userDataDir;
}
async createContext(clientInfo: ClientInfo, abortSignal: AbortSignal): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }> {
const browser = await this._obtainBrowser(clientInfo, abortSignal);
async createContext(clientInfo: ClientInfo, abortSignal: AbortSignal, toolName: string | undefined): Promise<{ browserContext: playwright.BrowserContext, close: () => Promise<void> }> {
const browser = await this._obtainBrowser(clientInfo, abortSignal, toolName);
return {
browserContext: browser.contexts()[0],
close: async () => {
@@ -43,9 +43,9 @@ export class ExtensionContextFactory implements BrowserContextFactory {
};
}
private async _obtainBrowser(clientInfo: ClientInfo, abortSignal: AbortSignal): Promise<playwright.Browser> {
private async _obtainBrowser(clientInfo: ClientInfo, abortSignal: AbortSignal, toolName: string | undefined): Promise<playwright.Browser> {
const relay = await this._startRelay(abortSignal);
await relay.ensureExtensionConnectionForMCPContext(clientInfo, abortSignal);
await relay.ensureExtensionConnectionForMCPContext(clientInfo, abortSignal, toolName);
return await playwright.chromium.connectOverCDP(relay.cdpEndpoint());
}