65 lines
2.8 KiB
JavaScript
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);
|