🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@agent360/browser-mcp

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@agent360/browser-mcp - npm Package Compare versions

Comparing version
1.16.1
to
1.19.0
+2
-2
extension/manifest.json
{
"manifest_version": 3,
"name": "Agent360 Browser MCP",
"version": "1.16.0",
"description": "Control your real Chrome from Claude Code — navigate, click, fill, screenshot, solve CAPTCHAs. 24 tools, multi-session, human-in-the-loop.",
"version": "1.19.0",
"description": "Control your real Chrome from Claude Code — navigate, click, fill, set dates, dismiss overlays, autocomplete, upload, screenshot, solve CAPTCHAs. 33 tools, multi-session, human-in-the-loop.",
"permissions": [

@@ -7,0 +7,0 @@ "tabs",

@@ -12,3 +12,3 @@ /**

const BASE_PORT = 9876;
const MAX_PORT = 9885;
const MAX_PORT = 9895;
const connections = new Map(); // port → WebSocket

@@ -96,4 +96,18 @@

// Listen for terminate signals from background.js (sent when last tab in a session closes)
chrome.runtime.onMessage.addListener((msg) => {
if (msg.type !== 'terminate_mcp_session' || typeof msg.port !== 'number') return;
const ws = connections.get(msg.port);
if (!ws) return;
try {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'terminate' }));
}
} catch {}
try { ws.close(); } catch {}
// ws.onclose handler removes from connections + notifies background
});
// Initial scan + frequent rescan for new servers
scanPorts();
setInterval(scanPorts, 2000);

@@ -17,6 +17,12 @@ #!/usr/bin/env node

import { execSync } from 'child_process';
import { dirname } from 'path';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { readFileSync } from 'fs';
import { TOOLS, PROVIDER_PAGES } from './tools.js';
// Read version from package.json — single source of truth, never drifts
const PKG_VERSION = JSON.parse(
readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'package.json'), 'utf8')
).version;
// ── Auto-update on startup ─────────────────────────────────────────────────

@@ -51,3 +57,3 @@

const BASE_PORT = 9876;
const MAX_PORT = 9885;
const MAX_PORT = 9895; // 20 ports instead of 10 — zombies die within 5s via parent check
let extensionSocket = null;

@@ -95,2 +101,8 @@ let activePort = null;

try { msg = JSON.parse(data.toString()); } catch { return; }
if (msg.type === 'terminate') {
process.stderr.write('[MCP] Terminate signal received from extension (last tab closed) — exiting\n');
process.exit(0);
}
const { id, result, error } = msg;

@@ -134,8 +146,12 @@ const p = pending.get(id);

function sendToExtension(method, params = {}, timeoutMs = 30000) {
async function sendToExtension(method, params = {}, timeoutMs = 30000, _retries = 5) {
// Retry if extension is temporarily disconnected (reconnects every 2s)
if (!extensionSocket || extensionSocket.readyState !== 1) {
if (_retries > 0) {
await new Promise(r => setTimeout(r, 1500));
return sendToExtension(method, params, timeoutMs, _retries - 1);
}
throw new Error('Chrome extension not connected after 5 retries. Open Chrome and ensure Agent360 Browser MCP extension is installed.');
}
return new Promise((resolve, reject) => {
if (!extensionSocket || extensionSocket.readyState !== 1) {
reject(new Error('Chrome extension not connected. Open Chrome and ensure Agent360 Browser MCP extension is installed.'));
return;
}
const id = ++cmdId;

@@ -213,2 +229,8 @@ const timer = setTimeout(() => {

## Hard inputs — use the specialised tools first
- **Date inputs** → use browser_set_date (NOT browser_fill). Handles native date inputs, masked text inputs (MM/DD/YYYY etc.), AND calendar pickers (MUI, react-datepicker, AntD, Lexical/Meta). 3-path fallback with read-back verification.
- **Autocomplete / combobox** (Languages on Meta Ads, country selects, async dropdowns) → use browser_set_combobox (NOT browser_select_option). Types partial query, waits for filtered listbox, clicks option. Supports multi-value chips.
- **Drag-drop file zones without visible file input** → use browser_drop_file (NOT browser_upload_file). Finds hidden input in subtree/parent.
- **Annoying popups blocking the flow** (cookie banners, "Don't show again", Advantage+ tooltips, draft-confirm prompts) → call browser_dismiss_overlays before each major step. It only clicks safe close affordances by default; preserves forms with editable text fields.
## When things fail

@@ -219,2 +241,3 @@ - Element not found → try text-based selector instead of CSS

- CAPTCHA blocks page → use browser_ask_user, let human solve it
- browser_fill seemingly succeeds but value reverts → switch to browser_set_date or browser_set_combobox (most reverts are React-controlled validators)

@@ -228,3 +251,3 @@ ## Extension updates

const mcpServer = new Server(
{ name: 'agent360-browser', version: '1.16.0' },
{ name: 'agent360-browser', version: PKG_VERSION },
{ capabilities: { tools: {} } },

@@ -272,2 +295,6 @@ { instructions: INSTRUCTIONS },

browser_solve_captcha: 'solve_captcha',
browser_set_date: 'set_date',
browser_dismiss_overlays: 'dismiss_overlays',
browser_set_combobox: 'set_combobox',
browser_drop_file: 'drop_file',
};

@@ -356,3 +383,16 @@

// Detect Claude Code exit (stdin closes when conversation ends)
// Detect Claude Code exit — check if parent process is still alive
// stdin.on('end') doesn't work because MCP SDK's StdioServerTransport owns stdin
const parentPid = process.ppid;
const parentCheck = setInterval(() => {
try {
process.kill(parentPid, 0); // signal 0 = check if process exists
} catch {
process.stderr.write(`[MCP] Parent process ${parentPid} died — shutting down\n`);
clearInterval(parentCheck);
process.exit(0);
}
}, 5000); // check every 5 seconds
// Also listen for stdin close as backup
process.stdin.on('end', () => {

@@ -359,0 +399,0 @@ process.stderr.write('[MCP] stdin closed — shutting down\n');

{
"name": "@agent360/browser-mcp",
"version": "1.16.1",
"version": "1.19.0",
"description": "Browser MCP — control your real Chrome from Claude Code. 29 tools, CAPTCHA solving, file upload, multi-session, human-in-the-loop.",

@@ -5,0 +5,0 @@ "mcpName": "io.github.Agent360dk/browser-mcp",

@@ -59,3 +59,3 @@ /**

name: 'browser_fill',
description: 'Fill a form input field with a value. Supports CSS selectors AND text-based selectors. Auto-scrolls and focuses the element. Works on CSP-strict sites via Chrome Debugger API.',
description: 'Fill a form input field with a value. Supports CSS selectors AND text-based selectors. Auto-scrolls and focuses the element. Works on CSP-strict sites via Chrome Debugger API. For date inputs use browser_set_date, for autocomplete/combobox use browser_set_combobox.',
inputSchema: {

@@ -124,3 +124,3 @@ type: 'object',

name: 'browser_select_option',
description: 'Select an option from a dropdown menu. Works with native <select> elements AND custom dropdowns (Angular Material, React Select, etc.). For custom dropdowns: clicks the trigger, waits for options, then clicks the matching option by text.',
description: 'Select an option from a dropdown menu. Works with native <select> elements AND custom dropdowns (Angular Material, React Select, etc.). For custom dropdowns: clicks the trigger, waits for options, then clicks the matching option by text. For autocomplete (typing filters options) use browser_set_combobox instead.',
inputSchema: {

@@ -137,2 +137,55 @@ type: 'object',

{
name: 'browser_dismiss_overlays',
description: 'Dismiss visible popups, modals, tooltips, banners, and "Are you sure?"-style overlays in one call. Heuristic-based: finds close affordance via aria-label, text content (Skip/Cancel/Ikke nu/Don\'t show/Got it/Close), or × character button. Use when a flow is interrupted by unexpected dialogs (cookie banners, onboarding tooltips, draft-confirm prompts on Meta Ads, etc.). Returns list of what was dismissed.',
inputSchema: {
type: 'object',
properties: {
scope: { type: 'string', enum: ['non_critical', 'aggressive'], description: 'non_critical (default): skip dialogs containing editable form inputs (preserves user data). aggressive: dismiss everything.' },
max_passes: { type: 'number', description: 'Number of dismissal passes (some overlays reveal others when closed). Default: 3' },
},
},
},
{
name: 'browser_set_combobox',
description: 'Set value(s) on an autocomplete/combobox input. Handles the click → type query → wait for filtered listbox → click option flow as one MCP call. Supports multi-select (e.g., Languages on Meta Ads). Use when browser_select_option fails because options render lazily after typing.',
inputSchema: {
type: 'object',
properties: {
selector: { type: 'string', description: 'CSS selector for the combobox/autocomplete input' },
value: { type: 'string', description: 'Single value to select (use this OR values)' },
values: { type: 'array', items: { type: 'string' }, description: 'Array of values for multi-select. E.g. ["Danish", "English", "Swedish"]' },
multi: { type: 'boolean', description: 'True if combobox accepts multiple values (chips). Default: auto-detected from presence of values array' },
query_chars: { type: 'number', description: 'How many characters to type as filter query (default: 4 or full value length, whichever is smaller)' },
wait_ms: { type: 'number', description: 'Max ms to wait for options listbox to appear after typing (default: 3000)' },
},
required: ['selector'],
},
},
{
name: 'browser_drop_file',
description: 'Upload a file by finding a hidden <input type="file"> within a drag-drop zone\'s subtree (or parent up to 2 levels). Use when browser_upload_file fails because the dropzone has no visible file input. Returns clear error if no input is found anywhere — pure drop-zones without backing inputs require manual handling.',
inputSchema: {
type: 'object',
properties: {
selector: { type: 'string', description: 'CSS selector for the drop-zone target element (e.g. ".upload-area")' },
file: { type: 'string', description: 'Single absolute file path' },
files: { type: 'array', items: { type: 'string' }, description: 'Array of absolute file paths' },
},
required: ['selector'],
},
},
{
name: 'browser_set_date',
description: 'Robustly set a date input — handles native <input type="date">, masked text inputs (e.g. MM/DD/YYYY), and calendar pickers (MUI, react-datepicker, AntD, Lexical/Meta). Tries native value-set, format-aware typing via Input.insertText, and ARIA-based picker navigation in sequence with read-back verification. Use instead of browser_fill when fill fails or for any input that opens a calendar widget.',
inputSchema: {
type: 'object',
properties: {
selector: { type: 'string', description: 'CSS selector for the date input element' },
date: { type: 'string', description: 'ISO date string (YYYY-MM-DD), e.g. "2026-05-15"' },
skip_picker: { type: 'boolean', description: 'If true, only try native + masked paths and skip calendar-picker navigation (default: false)' },
},
required: ['selector', 'date'],
},
},
{
name: 'browser_handle_dialog',

@@ -313,3 +366,3 @@ description: 'Handle JavaScript alert(), confirm(), or prompt() dialogs. Call this BEFORE triggering the action that causes the dialog. Waits for the dialog to appear, then accepts or dismisses it.',

name: 'browser_upload_file',
description: 'Upload a file to a <input type="file"> element on the page. Uses Chrome Debugger API to set files programmatically — no dialog needed.',
description: 'Upload a file to a <input type="file"> element on the page. Uses Chrome Debugger API to set files programmatically — no dialog needed. For drag-drop zones without visible file input use browser_drop_file.',
inputSchema: {

@@ -316,0 +369,0 @@ type: 'object',

Sorry, the diff of this file is too big to display