feat: add browser configuration tool and fix STDIO mode

- Add browser_configure tool to change headless/headed mode, viewport, and user agent during session
- Fix STDIO entry point by preventing stdin close handlers in STDIO mode
- Fix headed mode default behavior when DISPLAY is available on Linux
- Add dynamic browser configuration update mechanism in Context class

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ryan Malloy 2025-08-11 03:39:24 -06:00
parent e846cd509c
commit aa84278d36
5 changed files with 235 additions and 12 deletions

87
src/tools/configure.ts Normal file
View file

@ -0,0 +1,87 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { z } from 'zod';
import { defineTool } from './tool.js';
import type { Context } from '../context.js';
import type { Response } from '../response.js';
const configureSchema = z.object({
headless: z.boolean().optional().describe('Whether to run the browser in headless mode'),
viewport: z.object({
width: z.number(),
height: z.number(),
}).optional().describe('Browser viewport size'),
userAgent: z.string().optional().describe('User agent string for the browser'),
});
export default [
defineTool({
capability: 'core',
schema: {
name: 'browser_configure',
title: 'Configure browser settings',
description: 'Change browser configuration settings like headless/headed mode, viewport size, or user agent for subsequent operations. This will close the current browser and restart it with new settings.',
inputSchema: configureSchema,
type: 'destructive',
},
handle: async (context: Context, params: z.output<typeof configureSchema>, response: Response) => {
try {
const currentConfig = context.config;
const changes: string[] = [];
// Track what's changing
if (params.headless !== undefined) {
const currentHeadless = currentConfig.browser.launchOptions.headless;
if (params.headless !== currentHeadless) {
changes.push(`headless: ${currentHeadless}${params.headless}`);
}
}
if (params.viewport) {
const currentViewport = currentConfig.browser.contextOptions.viewport;
if (!currentViewport || currentViewport.width !== params.viewport.width || currentViewport.height !== params.viewport.height) {
changes.push(`viewport: ${currentViewport?.width || 'default'}x${currentViewport?.height || 'default'}${params.viewport.width}x${params.viewport.height}`);
}
}
if (params.userAgent) {
const currentUA = currentConfig.browser.contextOptions.userAgent;
if (params.userAgent !== currentUA) {
changes.push(`userAgent: ${currentUA || 'default'}${params.userAgent}`);
}
}
if (changes.length === 0) {
response.addResult('No configuration changes detected. Current settings remain the same.');
return;
}
// Apply the configuration changes
await context.updateBrowserConfig({
headless: params.headless,
viewport: params.viewport,
userAgent: params.userAgent,
});
response.addResult(`Browser configuration updated successfully:\n${changes.map(c => `${c}`).join('\n')}\n\nThe browser has been restarted with the new settings.`);
} catch (error) {
throw new Error(`Failed to update browser configuration: ${error}`);
}
},
}),
];