Skip to main content
Glama

interceptor_browser_launch

Launch a stealth Chromium browser with proxy settings, certificate trust, and fingerprint patches to evade detection and simulate human browsing behavior.

Instructions

Launch cloakbrowser (stealth Chromium) with proxy flags and SPKI certificate trust. Built-in source-level fingerprint patches + humanize mode. Driven via Playwright — locator-based tools replace CDP.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlNoURL to open (default: about:blank)
headlessNoRun headless (default: false)
humanizeNoEnable cloakbrowser's humanize mode (default: true)
human_presetNoHuman behavior presetdefault
timezoneNoIANA timezone, e.g. 'America/New_York'
localeNoBCP 47 locale, e.g. 'en-US'
viewport_widthNoViewport width in px
viewport_heightNoViewport height in px

Implementation Reference

  • BrowserInterceptor.activate() — the core implementation that launches cloakbrowser (stealth Chromium) via Playwright. Handles proxy config, SPKI certificate trust, humanize mode, viewport, timezone, locale, console logging, and returns targetId.
    async activate(options: ActivateOptions): Promise<ActivateResult> {
      const { proxyPort, certFingerprint } = options;
      const url = typeof options.url === "string" ? options.url : undefined;
      const headless = typeof options.headless === "boolean" ? options.headless : false;
      const humanize = options.humanize !== false;
      const humanPreset = options.humanPreset === "careful" ? "careful" : "default";
      const timezone = typeof options.timezone === "string" ? options.timezone : undefined;
      const locale = typeof options.locale === "string" ? options.locale : undefined;
      const viewport = options.viewport as { width: number; height: number } | undefined;
    
      const { launchContext } = await import("cloakbrowser");
    
      const args = [
        `--ignore-certificate-errors-spki-list=${certFingerprint}`,
        "--proxy-bypass-list=<-loopback>",
        "--disable-quic",
      ];
    
      const context = await launchContext({
        headless,
        proxy: { server: `http://127.0.0.1:${proxyPort}` },
        args,
        humanize,
        humanPreset,
        ...(timezone ? { timezone } : {}),
        ...(locale ? { locale } : {}),
        ...(viewport ? { viewport } : {}),
      });
    
      const browser = context.browser();
      if (!browser) {
        await context.close().catch(() => {});
        throw new Error("cloakbrowser launchContext returned a context without a browser handle.");
      }
    
      const page = await context.newPage();
    
      const consoleBuffer: ConsoleEntry[] = [];
      page.on("console", (msg: ConsoleMessage) => {
        const loc = msg.location();
        consoleBuffer.push({
          type: msg.type(),
          text: msg.text(),
          location: loc.url ? `${loc.url}:${loc.lineNumber ?? 0}:${loc.columnNumber ?? 0}` : "",
          timestamp: Date.now(),
        });
        if (consoleBuffer.length > CONSOLE_BUFFER_MAX) {
          consoleBuffer.splice(0, consoleBuffer.length - CONSOLE_BUFFER_MAX);
        }
      });
    
      if (url) {
        try {
          await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30_000 });
        } catch {
          // Non-fatal: caller can retry via interceptor_browser_navigate.
        }
      }
    
      const pid = typeof process.pid === "number" ? process.pid : 0;
      const targetId = `browser_${pid}_${Date.now()}`;
    
      const target: ActiveTarget = {
        id: targetId,
        description: `cloakbrowser (headless=${headless})`,
        activatedAt: Date.now(),
        details: {
          proxyPort,
          url: url ?? "about:blank",
          headless,
          humanize,
          humanPreset,
          ...(timezone ? { timezone } : {}),
          ...(locale ? { locale } : {}),
        },
      };
    
      this.launched.set(targetId, { target, browser, context, page, consoleBuffer });
    
      return { targetId, details: target.details };
    }
  • Zod schema for the tool's input parameters: url (string), headless (bool, default false), humanize (bool, default true), human_preset (enum default/careful, default default), timezone (string), locale (string), viewport_width (number), viewport_height (number).
    {
      url: z.string().optional().describe("URL to open (default: about:blank)"),
      headless: z.boolean().optional().default(false).describe("Run headless (default: false)"),
      humanize: z.boolean().optional().default(true).describe("Enable cloakbrowser's humanize mode (default: true)"),
      human_preset: z.enum(["default", "careful"]).optional().default("default")
        .describe("Human behavior preset"),
      timezone: z.string().optional().describe("IANA timezone, e.g. 'America/New_York'"),
      locale: z.string().optional().describe("BCP 47 locale, e.g. 'en-US'"),
      viewport_width: z.number().optional().describe("Viewport width in px"),
      viewport_height: z.number().optional().describe("Viewport height in px"),
    },
  • MCP tool registration via server.tool() with name "interceptor_browser_launch", description, input schema, and async handler that calls interceptorManager.activate("browser", ...).
    server.tool(
      "interceptor_browser_launch",
      "Launch cloakbrowser (stealth Chromium) with proxy flags and SPKI certificate trust. Built-in source-level fingerprint patches + humanize mode. Driven via Playwright — locator-based tools replace CDP.",
      {
        url: z.string().optional().describe("URL to open (default: about:blank)"),
        headless: z.boolean().optional().default(false).describe("Run headless (default: false)"),
        humanize: z.boolean().optional().default(true).describe("Enable cloakbrowser's humanize mode (default: true)"),
        human_preset: z.enum(["default", "careful"]).optional().default("default")
          .describe("Human behavior preset"),
        timezone: z.string().optional().describe("IANA timezone, e.g. 'America/New_York'"),
        locale: z.string().optional().describe("BCP 47 locale, e.g. 'en-US'"),
        viewport_width: z.number().optional().describe("Viewport width in px"),
        viewport_height: z.number().optional().describe("Viewport height in px"),
      },
      async ({ url, headless, humanize, human_preset, timezone, locale, viewport_width, viewport_height }) => {
        try {
          const proxyInfo = requireProxy();
          const viewport = (viewport_width && viewport_height)
            ? { width: viewport_width, height: viewport_height }
            : undefined;
    
          const result = await interceptorManager.activate("browser", {
            ...proxyInfo,
            url,
            headless,
            humanize,
            humanPreset: human_preset,
            timezone,
            locale,
            viewport,
          });
          return {
            content: [{
              type: "text",
              text: JSON.stringify({ status: "success", ...result }),
            }],
          };
        } catch (e) {
          return { content: [{ type: "text", text: JSON.stringify({ status: "error", error: errorToString(e) }) }] };
        }
      },
    );
  • requireProxy() helper — ensures proxy is running and returns proxy port, cert PEM, and cert fingerprint needed by the browser launch handler.
    function requireProxy(): { proxyPort: number; certPem: string; certFingerprint: string } {
      if (!proxyManager.isRunning()) {
        throw new Error("Proxy is not running. Start it first with proxy_start.");
      }
      const cert = proxyManager.getCert();
      if (!cert) {
        throw new Error("No certificate available. Start the proxy first.");
      }
      return {
        proxyPort: proxyManager.getPort()!,
        certPem: cert.cert,
        certFingerprint: cert.fingerprint,
      };
    }
  • InterceptorManager.activate() — dispatches to the registered BrowserInterceptor's activate() method, which is the actual handler.
    /** Activate a specific interceptor. */
    async activate(interceptorId: string, options: ActivateOptions): Promise<ActivateResult> {
      const interceptor = this.interceptors.get(interceptorId);
      if (!interceptor) {
        throw new Error(`Interceptor '${interceptorId}' not found. Available: ${[...this.interceptors.keys()].join(", ")}`);
      }
      const activable = await interceptor.isActivable();
      if (!activable) {
        throw new Error(`Interceptor '${interceptorId}' is not activable. Required tooling may be missing.`);
      }
      return interceptor.activate(options);
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden. It discloses behavioral aspects: launching with proxy and SPKI trust, source-level fingerprint patches, humanize mode, and Playwright driving. However, it lacks details on side effects, prerequisites (e.g., proxy setup), error conditions, or return value.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is exceptionally concise: two sentences (40 words) with no filler. Every word adds value: first sentence states purpose and key features, second clarifies technical driver and subsequent tool usage. It is front-loaded and efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of output schema and annotations, the description should cover return values and setup dependencies. It mentions Playwright and locator tools but does not specify what the tool returns (e.g., browser session ID) or prerequisites (e.g., proxy configuration). This is adequate but has clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 100% description coverage for all 8 parameters, each with defaults and one enum. The description does not add additional meaning beyond the schema, so the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool 'launch[es] cloakbrowser (stealth Chromium)' with specific features (proxy flags, SPKI trust, fingerprint patches, humanize mode). It differentiates from sibling tools like interceptor_camoufox_launch by specifying the browser type and mentioning Playwright-driven locator tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool is for launching a stealth browser for tasks requiring fingerprint protection and humanization, but it does not explicitly state when to use this tool versus alternatives (e.g., interceptor_camoufox_launch) or mention scenarios where it should not be used.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yfe404/proxy-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server