aironin-browse-mcp
Version:
aiRonin Browse MCP server with headed Chrome support
163 lines • 5.53 kB
JavaScript
import * as net from "node:net";
import axios from "axios";
import * as dns from "node:dns";
/**
* Check if a port is open on a given host
*/
export async function isPortOpen(host, port, timeout = 1000) {
return new Promise((resolve) => {
const socket = new net.Socket();
let status = false;
// Set timeout
socket.setTimeout(timeout);
// Handle successful connection
socket.on("connect", () => {
status = true;
socket.destroy();
});
// Handle any errors
socket.on("error", () => {
socket.destroy();
});
// Handle timeout
socket.on("timeout", () => {
socket.destroy();
});
// Handle close
socket.on("close", () => {
resolve(status);
});
// Attempt to connect
socket.connect(port, host);
});
}
/**
* Try to connect to Chrome at a specific IP address
*/
export async function tryChromeHostUrl(chromeHostUrl) {
try {
console.error(`Trying to connect to Chrome at: ${chromeHostUrl}/json/version`);
await axios.get(`${chromeHostUrl}/json/version`, { timeout: 1000 });
return true;
}
catch (error) {
return false;
}
}
/**
* Get Docker host IP
*/
export async function getDockerHostIP() {
try {
// Try to resolve host.docker.internal (works on Docker Desktop)
return new Promise((resolve) => {
dns.lookup("host.docker.internal", (err, address) => {
if (err) {
resolve(null);
}
else {
resolve(address);
}
});
});
}
catch (error) {
console.error("Could not determine Docker host IP:", error);
return null;
}
}
/**
* Scan a network range for Chrome debugging port
*/
export async function scanNetworkForChrome(baseIP, port) {
if (!baseIP || !baseIP.match(/^\d+\.\d+\.\d+\./)) {
return null;
}
// Extract the network prefix (e.g., "192.168.65.")
const networkPrefix = baseIP.split(".").slice(0, 3).join(".") + ".";
// Common Docker host IPs to try first
const priorityIPs = [
networkPrefix + "1", // Common gateway
networkPrefix + "2", // Common host
networkPrefix + "254", // Common host in some Docker setups
];
console.error(`Scanning priority IPs in network ${networkPrefix}*`);
// Check priority IPs first
for (const ip of priorityIPs) {
const isOpen = await isPortOpen(ip, port);
if (isOpen) {
console.error(`Found Chrome debugging port open on ${ip}`);
return ip;
}
}
return null;
}
// Function to discover Chrome instances on the network
const discoverChromeHosts = async (port) => {
// Get all network interfaces
const ipAddresses = [];
// Try to get Docker host IP
const hostIP = await getDockerHostIP();
if (hostIP) {
console.error("Found Docker host IP:", hostIP);
ipAddresses.push(hostIP);
}
// Remove duplicates
const uniqueIPs = [...new Set(ipAddresses)];
console.error("IP Addresses to try:", uniqueIPs);
// Try connecting to each IP address
for (const ip of uniqueIPs) {
const hostEndpoint = `http://${ip}:${port}`;
const hostIsValid = await tryChromeHostUrl(hostEndpoint);
if (hostIsValid) {
// Store the successful IP for future use
console.error(`✅ Found Chrome at ${hostEndpoint}`);
// Return the host URL and endpoint
return hostEndpoint;
}
}
return null;
};
/**
* Test connection to a remote browser debugging websocket.
* First tries specific hosts, then attempts auto-discovery if needed.
* @param browserHostUrl Optional specific host URL to check first
* @param port Browser debugging port (default: 9222)
* @returns WebSocket debugger URL if connection is successful, null otherwise
*/
export async function discoverChromeHostUrl(port = 9222) {
// First try specific hosts
const hostsToTry = [`http://localhost:${port}`, `http://127.0.0.1:${port}`];
// Try each host directly first
for (const hostUrl of hostsToTry) {
console.error(`Trying to connect to: ${hostUrl}`);
try {
const hostIsValid = await tryChromeHostUrl(hostUrl);
if (hostIsValid)
return hostUrl;
}
catch (error) {
console.error(`Failed to connect to ${hostUrl}: ${error instanceof Error ? error.message : error}`);
}
}
// If direct connections failed, attempt auto-discovery
console.error("Direct connections failed. Attempting auto-discovery...");
const discoveredHostUrl = await discoverChromeHosts(port);
if (discoveredHostUrl) {
console.error(`Trying to connect to discovered host: ${discoveredHostUrl}`);
try {
const hostIsValid = await tryChromeHostUrl(discoveredHostUrl);
if (hostIsValid)
return discoveredHostUrl;
console.error(`Failed to connect to discovered host ${discoveredHostUrl}`);
}
catch (error) {
console.error(`Error connecting to discovered host: ${error instanceof Error ? error.message : error}`);
}
}
else {
console.error("No browser instances discovered on network");
}
return null;
}
//# sourceMappingURL=browserDiscovery.js.map