feat(mcp): added browser MCP server filtering to prevent conflicts with builtin tool

- Added filterBrowser configuration option to disable MCP browser servers when builtin browser tool is enabled.
- Added isBrowserMCPServer() function to detect browser automation MCP servers by name, URL, or command patterns.
- Added filterBrowserMCPServers() function to remove browser MCP servers from loaded configurations.
- Added BrowserFilterResult type for browser MCP server filtering results.
This commit is contained in:
can1357
2026-02-21 16:29:28 +01:00
parent a117b35f9f
commit d5b28d6d3b
6 changed files with 119 additions and 6 deletions
+6
View File
@@ -1,6 +1,12 @@
# Changelog
## [Unreleased]
### Added
- Added `filterBrowser` option to filter out browser automation MCP servers when builtin browser tool is enabled
- Added `isBrowserMCPServer()` function to detect browser automation MCP servers by name, URL, or command patterns
- Added `filterBrowserMCPServers()` function to remove browser MCP servers from loaded configurations
- Added `BrowserFilterResult` type for browser MCP server filtering results
## [12.17.0] - 2026-02-21
### Added
+102 -5
View File
@@ -18,6 +18,8 @@ export interface LoadMCPConfigsOptions {
enableProjectConfig?: boolean;
/** Whether to filter out Exa MCP servers (default: true) */
filterExa?: boolean;
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
filterBrowser?: boolean;
}
/** Result of loading MCP configs */
@@ -91,6 +93,7 @@ function convertToLegacyConfig(server: MCPServer): MCPServerConfig {
export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOptions): Promise<LoadMCPConfigsResult> {
const enableProjectConfig = options?.enableProjectConfig ?? true;
const filterExa = options?.filterExa ?? true;
const filterBrowser = options?.filterBrowser ?? false;
// Load MCP servers via capability system
const result = await loadCapability<MCPServer>(mcpCapability.id, { cwd });
@@ -103,8 +106,8 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
// Load user-level disabled servers list
const disabledServers = new Set(await readDisabledServers(getMCPConfigPath("user", cwd)));
// Convert to legacy format and preserve source metadata
const configs: Record<string, MCPServerConfig> = {};
const sources: Record<string, SourceMeta> = {};
let configs: Record<string, MCPServerConfig> = {};
let sources: Record<string, SourceMeta> = {};
for (const server of servers) {
const config = convertToLegacyConfig(server);
if (config.enabled === false || (server._source.level !== "user" && disabledServers.has(server.name))) {
@@ -114,11 +117,19 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
sources[server.name] = server._source;
}
const exaApiKeys: string[] = [];
let exaApiKeys: string[] = [];
if (filterExa) {
const filterResult = filterExaMCPServers(configs, sources);
return { configs: filterResult.configs, exaApiKeys: filterResult.exaApiKeys, sources: filterResult.sources };
const exaResult = filterExaMCPServers(configs, sources);
configs = exaResult.configs;
sources = exaResult.sources;
exaApiKeys = exaResult.exaApiKeys;
}
if (filterBrowser) {
const browserResult = filterBrowserMCPServers(configs, sources);
configs = browserResult.configs;
sources = browserResult.sources;
}
return { configs, exaApiKeys, sources };
@@ -264,3 +275,89 @@ export function validateServerConfig(name: string, config: MCPServerConfig): str
return errors;
}
/** Known browser automation MCP server names (lowercase) */
const BROWSER_MCP_NAMES = new Set([
"puppeteer",
"playwright",
"browserbase",
"browser-tools",
"browser-use",
"browser",
]);
/** Patterns matching browser MCP package names in command/args */
const BROWSER_MCP_PKG_PATTERN =
// Official packages
// - @modelcontextprotocol/server-puppeteer
// - @playwright/mcp
// - @browserbasehq/mcp-server-browserbase
// - @agentdeskai/browser-tools-mcp
// - @agent-infra/mcp-server-browser
// Community packages: puppeteer-mcp-server, playwright-mcp, pptr-mcp, etc.
/(?:@modelcontextprotocol\/server-puppeteer|@playwright\/mcp|@browserbasehq\/mcp-server-browserbase|@agentdeskai\/browser-tools-mcp|@agent-infra\/mcp-server-browser|puppeteer-mcp|playwright-mcp|pptr-mcp|browser-use-mcp|mcp-browser-use)/i;
/** URL patterns for hosted browser MCP services */
const BROWSER_MCP_URL_PATTERN = /browserbase\.com|browser-use\.com/i;
/**
* Check if a server config is a browser automation MCP server.
*/
export function isBrowserMCPServer(name: string, config: MCPServerConfig): boolean {
// Check by server name
if (BROWSER_MCP_NAMES.has(name.toLowerCase())) {
return true;
}
// Check by URL for HTTP/SSE servers
if (config.type === "http" || config.type === "sse") {
const httpConfig = config as { url?: string };
if (httpConfig.url && BROWSER_MCP_URL_PATTERN.test(httpConfig.url)) {
return true;
}
}
// Check by command/args for stdio servers
if (!config.type || config.type === "stdio") {
const stdioConfig = config as { command?: string; args?: string[] };
if (stdioConfig.command && BROWSER_MCP_PKG_PATTERN.test(stdioConfig.command)) {
return true;
}
if (stdioConfig.args?.some(arg => BROWSER_MCP_PKG_PATTERN.test(arg))) {
return true;
}
}
return false;
}
/** Result of filtering browser MCP servers */
export interface BrowserFilterResult {
/** Configs with browser servers removed */
configs: Record<string, MCPServerConfig>;
/** Source metadata for remaining servers */
sources: Record<string, SourceMeta>;
}
/**
* Filter out browser automation MCP servers.
* Since we have a native browser tool, we don't need these MCP servers.
*/
export function filterBrowserMCPServers(
configs: Record<string, MCPServerConfig>,
sources: Record<string, SourceMeta>,
): BrowserFilterResult {
const filtered: Record<string, MCPServerConfig> = {};
const filteredSources: Record<string, SourceMeta> = {};
for (const [name, config] of Object.entries(configs)) {
if (!isBrowserMCPServer(name, config)) {
filtered[name] = config;
if (sources[name]) {
filteredSources[name] = sources[name];
}
}
}
return { configs: filtered, sources: filteredSources };
}
+3 -1
View File
@@ -8,10 +8,12 @@
// Client
export { callTool, connectToServer, disconnectServer, listTools, serverSupportsTools } from "./client";
// Config
export type { ExaFilterResult, LoadMCPConfigsOptions, LoadMCPConfigsResult } from "./config";
export type { BrowserFilterResult, ExaFilterResult, LoadMCPConfigsOptions, LoadMCPConfigsResult } from "./config";
export {
extractExaApiKey,
filterBrowserMCPServers,
filterExaMCPServers,
isBrowserMCPServer,
isExaMCPServer,
loadAllMCPConfigs,
validateServerConfig,
+3
View File
@@ -32,6 +32,8 @@ export interface MCPToolsLoadOptions {
enableProjectConfig?: boolean;
/** Whether to filter out Exa MCP servers (default: true) */
filterExa?: boolean;
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
filterBrowser?: boolean;
/** SQLite storage for MCP tool cache (null disables cache) */
cacheStorage?: AgentStorage | null;
/** Auth storage used to resolve OAuth credentials before initial MCP connect */
@@ -69,6 +71,7 @@ export async function discoverAndLoadMCPTools(cwd: string, options?: MCPToolsLoa
onConnecting: options?.onConnecting,
enableProjectConfig: options?.enableProjectConfig,
filterExa: options?.filterExa,
filterBrowser: options?.filterBrowser,
});
} catch (error) {
// If discovery fails entirely, return empty result
+3
View File
@@ -68,6 +68,8 @@ export interface MCPDiscoverOptions {
enableProjectConfig?: boolean;
/** Whether to filter out Exa MCP servers (default: true) */
filterExa?: boolean;
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
filterBrowser?: boolean;
/** Called when starting to connect to servers */
onConnecting?: (serverNames: string[]) => void;
}
@@ -105,6 +107,7 @@ export class MCPManager {
const { configs, exaApiKeys, sources } = await loadAllMCPConfigs(this.cwd, {
enableProjectConfig: options?.enableProjectConfig,
filterExa: options?.filterExa,
filterBrowser: options?.filterBrowser,
});
const result = await this.connectServers(configs, sources, options?.onConnecting);
result.exaApiKeys = exaApiKeys;
+2
View File
@@ -806,6 +806,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
enableProjectConfig: settings.get("mcp.enableProjectConfig") ?? true,
// Always filter Exa - we have native integration
filterExa: true,
// Filter browser MCP servers when builtin browser tool is active
filterBrowser: (settings.get("browser.enabled") as boolean) ?? false,
cacheStorage: settings.getStorage(),
authStorage,
});