dev-mcps/browser-mcp/src/index.js

65 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
import { startStreamableHttpServer } from "./streamable-http.js";
import { BrowserService } from "./browser-service.js";
import { TOOL_DEFINITIONS } from "./tool-definitions.js";
function csv(value) { return (value ?? "").split(",").map((item) => item.trim()).filter(Boolean); }
function integer(value, fallback) { const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; }
const maxSessions = integer(process.env.BROWSER_MCP_MAX_SESSIONS, 16);
const browserService = new BrowserService({
allowedOrigins: csv(process.env.BROWSER_MCP_ALLOWED_ORIGINS),
maxSessions,
defaultTimeoutMs: integer(process.env.BROWSER_MCP_DEFAULT_TIMEOUT_MS, 15000),
browserFactory: async () => {
const { chromium } = await import("playwright");
return chromium.launch({ headless: true, chromiumSandbox: true });
}
});
const calls = {
browser_navigate: (args, context) => browserService.navigate(args, context),
browser_click: (args, context) => browserService.click(args, context),
browser_fill: (args, context) => browserService.fill(args, context),
browser_wait: (args, context) => browserService.wait(args, context),
browser_snapshot: (args, context) => browserService.snapshot(args, context),
browser_screenshot: (args, context) => browserService.screenshot(args, context),
browser_console_messages: (args, context) => browserService.consoleMessages(args, context),
browser_network_errors: (args, context) => browserService.networkErrors(args, context)
};
async function auditedCall(name, args, context) {
const event = { timestamp: new Date().toISOString(), server: "browser-mcp", tool: name, subject: context.subject, sessionId: context.sessionId };
try {
const result = await calls[name](args, context);
process.stderr.write(`${JSON.stringify({ ...event, outcome: "success" })}\n`);
return result;
} catch (error) {
process.stderr.write(`${JSON.stringify({ ...event, outcome: "failure", errorType: error instanceof Error ? error.name : "Error" })}\n`);
throw error;
}
}
const listener = await startStreamableHttpServer({
serverName: "browser-mcp",
serverVersion: "1.0.0",
tools: TOOL_DEFINITIONS,
callTool: auditedCall,
apiKeys: csv(process.env.BROWSER_MCP_API_KEYS),
allowedOrigins: csv(process.env.BROWSER_MCP_CORS_ORIGINS),
host: process.env.BROWSER_MCP_HOST ?? "0.0.0.0",
port: integer(process.env.BROWSER_MCP_PORT, 3000),
maxSessions,
sessionTtlMs: integer(process.env.BROWSER_MCP_SESSION_TTL_SECONDS, 1800) * 1000,
onSessionClose: ({ id }) => browserService.closeSession(id)
});
process.stderr.write(`browser-mcp listening on ${listener.host}:${listener.port}\n`);
async function shutdown() {
await listener.close();
await browserService.close();
process.exit(0);
}
process.once("SIGTERM", shutdown);
process.once("SIGINT", shutdown);