cloakbrowser-mcp 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cloakbrowser-mcp contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # cloakbrowser-mcp
2
+
3
+ <p align="center">
4
+ <img src="docs/assets/brand/logo-wordmark.svg" alt="CloakBrowser MCP" width="640" />
5
+ </p>
6
+
7
+ [![CI](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/ci.yml)
8
+ [![Actionlint](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/actionlint.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/actionlint.yml)
9
+ [![CodeQL](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/codeql.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/codeql.yml)
10
+ [![Dependency Review](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/dependency-review.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/dependency-review.yml)
11
+ [![OpenSSF Scorecard](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/scorecard.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/scorecard.yml)
12
+ [![Zizmor](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/zizmor.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/zizmor.yml)
13
+ [![NPM Release](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/npm-release.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/npm-release.yml)
14
+ [![Docker Release](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/docker-release.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/docker-release.yml)
15
+ [![Docs Release](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/docs-release.yml/badge.svg)](https://github.com/swimmwatch/cloakbrowser-mcp/actions/workflows/docs-release.yml)
16
+ [![npm](https://img.shields.io/npm/v/cloakbrowser-mcp.svg?logo=npm)](https://www.npmjs.com/package/cloakbrowser-mcp)
17
+ [![Node.js >=20](https://img.shields.io/badge/Node.js-%3E%3D20-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
18
+ [![MCP Server](https://img.shields.io/badge/MCP-server-000000)](https://modelcontextprotocol.io/)
19
+ [![Docker](https://img.shields.io/badge/Docker-ready-2496ED?logo=docker&logoColor=white)](docs/docker.md)
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
21
+
22
+ `cloakbrowser-mcp` is a stdio [Model Context Protocol](https://modelcontextprotocol.io/) browser automation server that runs upstream [`@playwright/mcp`](https://github.com/microsoft/playwright-mcp) with the [CloakBrowser](https://github.com/CloakHQ/CloakBrowser) Chromium binary. It provides Playwright MCP-compatible tools through a thin CloakBrowser bridge for npm and Docker users.
23
+
24
+ Documentation: [swimmwatch.github.io/cloakbrowser-mcp](https://swimmwatch.github.io/cloakbrowser-mcp/)
25
+
26
+ The server is intentionally thin:
27
+
28
+ - upstream Playwright MCP owns browser tool schemas, descriptions, and responses;
29
+ - this package generates a Playwright MCP config that points `launchOptions.executablePath` to CloakBrowser;
30
+ - the bridge exposes upstream tools unchanged;
31
+ - the only local tools are `cloakbrowser_binary_info` and `cloakbrowser_bridge_info`.
32
+
33
+ ## Version compatibility
34
+
35
+ | cloakbrowser-mcp | @playwright/mcp | Playwright MCP Docker base | CloakBrowser | Node.js | Transport | Platform | Parity |
36
+ | --- | --- | --- | --- | --- | --- | --- | --- |
37
+ | `1.0.1` | `^0.0.75` | `mcr.microsoft.com/playwright/mcp:v0.0.75` | `^0.3.30` | `>=20` | stdio | `linux/amd64` Docker, Node.js local | Upstream default tools compared in CI. |
38
+ | `1.0.0` | `^0.0.75` | `mcr.microsoft.com/playwright/mcp:v0.0.75` | `^0.3.30` | `>=20` | stdio | `linux/amd64` Docker, Node.js local | Upstream default tools compared in CI. |
39
+
40
+ See [Version Compatibility](docs/version-compatibility.md) for the maintained compatibility table.
41
+
42
+ ## Run from npm
43
+
44
+ ```bash
45
+ npx -y cloakbrowser-mcp@latest --help
46
+ npx -y cloakbrowser-mcp@latest
47
+ ```
48
+
49
+ Requires Node.js 20 or newer. The first real browser action may download the CloakBrowser binary unless it is already cached.
50
+
51
+ ## Run from Docker
52
+
53
+ ```bash
54
+ docker pull ghcr.io/swimmwatch/cloakbrowser-mcp:latest
55
+ docker run --rm --init -i \
56
+ -v "$PWD/artifacts:/data" \
57
+ ghcr.io/swimmwatch/cloakbrowser-mcp:latest
58
+ ```
59
+
60
+ The Docker image is based on the pinned official Playwright MCP image, installs the bridge under `/opt/cloakbrowser-mcp`, and writes artifacts to `/data` by default.
61
+
62
+ ## MCP client configuration
63
+
64
+ ### npm
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "cloakbrowser": {
70
+ "command": "npx",
71
+ "args": ["-y", "cloakbrowser-mcp@latest"],
72
+ "env": {
73
+ "PLAYWRIGHT_MCP_OUTPUT_DIR": "/tmp/cloakbrowser-artifacts",
74
+ "PLAYWRIGHT_MCP_HEADLESS": "true"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ### Docker
82
+
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "cloakbrowser": {
87
+ "command": "docker",
88
+ "args": [
89
+ "run",
90
+ "--rm",
91
+ "--init",
92
+ "-i",
93
+ "-v",
94
+ "/tmp/cloakbrowser-artifacts:/data",
95
+ "ghcr.io/swimmwatch/cloakbrowser-mcp:latest"
96
+ ]
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Configuration
103
+
104
+ Use upstream `PLAYWRIGHT_MCP_*` variables for browser, artifact, timeout, network, and tool capability settings. Cloak-specific bridge toggles use `CLOAK_PLAYWRIGHT_MCP_*`.
105
+
106
+ Common variables:
107
+
108
+ | Variable | Default | Description |
109
+ | --- | --- | --- |
110
+ | `PLAYWRIGHT_MCP_BROWSER_ENGINE` | `cloak` | `cloak` uses CloakBrowser. `playwright` uses the upstream Playwright MCP browser runtime. |
111
+ | `PLAYWRIGHT_MCP_HEADLESS` | `true` | Runs Chromium headless. |
112
+ | `PLAYWRIGHT_MCP_OUTPUT_DIR` | `.playwright-mcp` | Artifact directory for npm usage. Docker defaults to `/data`. |
113
+ | `PLAYWRIGHT_MCP_OUTPUT_MODE` | `stdout` | Upstream output mode, either `stdout` or `file`. |
114
+ | `CLOAK_PLAYWRIGHT_MCP_CONSOLE_FALLBACK` | `true` | Enables the compatibility patch for console messages. |
115
+ | `CLOAK_PLAYWRIGHT_MCP_STEALTH_ARGS` | `true` | Adds CloakBrowser default stealth launch arguments. |
116
+ | `CLOAK_PLAYWRIGHT_MCP_EXTRA_ARGS` | unset | Comma-separated or JSON array of extra Chromium launch arguments. |
117
+
118
+ The old `CLOAKBROWSER_MCP_*` variables are not supported.
119
+
120
+ ## Tools
121
+
122
+ The upstream Playwright MCP tool list is authoritative. This project does not reimplement or re-document upstream browser schemas in source code.
123
+
124
+ Local tools:
125
+
126
+ - `cloakbrowser_binary_info` returns CloakBrowser package, platform, cache, and resolved binary data.
127
+ - `cloakbrowser_bridge_info` returns bridge metadata, upstream package/version, and local tool names.
128
+
129
+ ## Development
130
+
131
+ ```bash
132
+ npm install
133
+ npm run build
134
+ npm test
135
+ npm run docker:build
136
+ npm run docker:smoke
137
+ npm run server:validate
138
+ npm run bridge:compare -- cloakbrowser-mcp:dev --report bridge-parity-report.json
139
+ ```
140
+
141
+ Documentation starts at [docs/getting-started.md](docs/getting-started.md). Contributor material is grouped under [docs/contributor-guide.md](docs/contributor-guide.md).
@@ -0,0 +1,36 @@
1
+ import { binaryInfo } from 'cloakbrowser';
2
+ import { type EnvReader } from './env.js';
3
+ export type BrowserEngine = 'cloak' | 'playwright';
4
+ export interface BridgeRuntime {
5
+ browserEngine: BrowserEngine;
6
+ configPath: string;
7
+ tempDir: string;
8
+ childEnv: Record<string, string>;
9
+ outputDir: string;
10
+ cloakBinaryPath?: string;
11
+ config: PlaywrightMcpBridgeConfig;
12
+ dispose(): void;
13
+ }
14
+ export interface PrepareBridgeRuntimeOptions {
15
+ env?: EnvReader;
16
+ tempRoot?: string;
17
+ ensureCloakBinary?: () => Promise<string>;
18
+ }
19
+ export interface PlaywrightMcpBridgeConfig {
20
+ browser?: {
21
+ browserName?: 'chromium';
22
+ launchOptions?: {
23
+ executablePath?: string;
24
+ headless?: boolean;
25
+ args?: string[];
26
+ chromiumSandbox?: boolean;
27
+ ignoreDefaultArgs?: string[];
28
+ };
29
+ initScript?: string[];
30
+ };
31
+ }
32
+ export declare function prepareBridgeRuntime(options?: PrepareBridgeRuntimeOptions): Promise<BridgeRuntime>;
33
+ export declare function createLaunchArgs(env: EnvReader): string[];
34
+ export declare function createChildEnv(env: EnvReader, outputDir: string): Record<string, string>;
35
+ export declare function getCurrentCloakBinaryInfo(): ReturnType<typeof binaryInfo>;
36
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/bridge/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAuC,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAyD,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAIjG,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,YAAY,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,yBAAyB,CAAC;IAClC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,UAAU,CAAC;QACzB,aAAa,CAAC,EAAE;YACd,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;YAChB,eAAe,CAAC,EAAE,OAAO,CAAC;YAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;SAC9B,CAAC;QACF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACH;AAID,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,aAAa,CAAC,CAuDxB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,EAAE,CAQzD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxF;AAED,wBAAgB,yBAAyB,IAAI,UAAU,CAAC,OAAO,UAAU,CAAC,CAEzE"}
@@ -0,0 +1,105 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import path from 'node:path';
4
+ import { binaryInfo, ensureBinary, getDefaultStealthArgs } from 'cloakbrowser';
5
+ import { appendNodeOption, envBool, envInt, envList, envString } from './env.js';
6
+ import { resolvePlaywrightCoreBundlePath } from './paths.js';
7
+ import { consoleFallbackInitScript, consoleFallbackPreloadScript } from '../runtime/consoleFallback.js';
8
+ const ignoredAutomationArgs = ['--enable-automation', '--enable-unsafe-swiftshader'];
9
+ export async function prepareBridgeRuntime(options = {}) {
10
+ const env = options.env ?? process.env;
11
+ const tempDir = mkdtempSync(path.join(options.tempRoot ?? tmpdir(), 'cloakbrowser-mcp-'));
12
+ const outputDir = envString(env, 'PLAYWRIGHT_MCP_OUTPUT_DIR', path.resolve('.playwright-mcp'));
13
+ mkdirSync(outputDir, { recursive: true });
14
+ const browserEngine = parseBrowserEngine(envString(env, 'PLAYWRIGHT_MCP_BROWSER_ENGINE', 'cloak'));
15
+ const childEnv = createChildEnv(env, outputDir);
16
+ const useCloak = browserEngine === 'cloak';
17
+ const launchOptions = {
18
+ headless: envBool(env, 'PLAYWRIGHT_MCP_HEADLESS', true),
19
+ args: useCloak ? createLaunchArgs(env) : envList(env, 'CLOAK_PLAYWRIGHT_MCP_EXTRA_ARGS'),
20
+ chromiumSandbox: useCloak ? !envBool(env, 'CLOAK_PLAYWRIGHT_MCP_NO_SANDBOX', true) : undefined,
21
+ ignoreDefaultArgs: useCloak ? ignoredAutomationArgs : undefined,
22
+ };
23
+ const config = {
24
+ browser: {
25
+ browserName: 'chromium',
26
+ launchOptions,
27
+ },
28
+ };
29
+ let cloakBinaryPath;
30
+ if (useCloak) {
31
+ cloakBinaryPath = await suppressStdout(options.ensureCloakBinary ?? ensureBinary);
32
+ config.browser.launchOptions.executablePath = cloakBinaryPath;
33
+ childEnv.PLAYWRIGHT_MCP_EXECUTABLE_PATH = cloakBinaryPath;
34
+ childEnv.CLOAKBROWSER_AUTO_UPDATE = childEnv.CLOAKBROWSER_AUTO_UPDATE ?? 'false';
35
+ }
36
+ if (useCloak && envBool(env, 'CLOAK_PLAYWRIGHT_MCP_CONSOLE_FALLBACK', true)) {
37
+ const initScriptPath = path.join(tempDir, 'console-fallback-init.js');
38
+ const preloadPath = path.join(tempDir, 'console-fallback.cjs');
39
+ writeFileSync(initScriptPath, consoleFallbackInitScript);
40
+ writeFileSync(preloadPath, consoleFallbackPreloadScript(resolvePlaywrightCoreBundlePath()));
41
+ config.browser.initScript = [...(config.browser.initScript ?? []), initScriptPath];
42
+ childEnv.NODE_OPTIONS = appendNodeOption(childEnv.NODE_OPTIONS, `--require=${preloadPath}`);
43
+ }
44
+ const configPath = path.join(tempDir, 'playwright-mcp.config.json');
45
+ writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
46
+ childEnv.PLAYWRIGHT_MCP_CONFIG = configPath;
47
+ return {
48
+ browserEngine,
49
+ configPath,
50
+ tempDir,
51
+ childEnv,
52
+ outputDir,
53
+ cloakBinaryPath,
54
+ config,
55
+ dispose() {
56
+ rmSync(tempDir, { recursive: true, force: true });
57
+ },
58
+ };
59
+ }
60
+ export function createLaunchArgs(env) {
61
+ const args = new Set();
62
+ if (envBool(env, 'CLOAK_PLAYWRIGHT_MCP_STEALTH_ARGS', true)) {
63
+ for (const arg of getDefaultStealthArgs())
64
+ args.add(arg);
65
+ }
66
+ if (envBool(env, 'CLOAK_PLAYWRIGHT_MCP_NO_SANDBOX', true))
67
+ args.add('--no-sandbox');
68
+ for (const arg of envList(env, 'CLOAK_PLAYWRIGHT_MCP_EXTRA_ARGS'))
69
+ args.add(arg);
70
+ return [...args];
71
+ }
72
+ export function createChildEnv(env, outputDir) {
73
+ const result = {};
74
+ for (const [key, value] of Object.entries(env)) {
75
+ if (typeof value === 'string')
76
+ result[key] = value;
77
+ }
78
+ result.PLAYWRIGHT_MCP_HEADLESS = envString(env, 'PLAYWRIGHT_MCP_HEADLESS', 'true');
79
+ result.PLAYWRIGHT_MCP_OUTPUT_DIR = outputDir;
80
+ result.PLAYWRIGHT_MCP_OUTPUT_MODE = envString(env, 'PLAYWRIGHT_MCP_OUTPUT_MODE', 'stdout');
81
+ result.PLAYWRIGHT_MCP_TIMEOUT_ACTION = String(envInt(env, 'PLAYWRIGHT_MCP_TIMEOUT_ACTION', 5000));
82
+ result.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION = String(envInt(env, 'PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION', 60000));
83
+ result.CLOAKBROWSER_AUTO_UPDATE = envString(env, 'CLOAKBROWSER_AUTO_UPDATE', 'false');
84
+ return result;
85
+ }
86
+ export function getCurrentCloakBinaryInfo() {
87
+ return binaryInfo();
88
+ }
89
+ function parseBrowserEngine(value) {
90
+ if (value === 'cloak' || value === 'playwright')
91
+ return value;
92
+ throw new Error(`PLAYWRIGHT_MCP_BROWSER_ENGINE must be "cloak" or "playwright", got "${value}"`);
93
+ }
94
+ async function suppressStdout(fn) {
95
+ const stdout = process.stdout;
96
+ const write = stdout.write.bind(stdout);
97
+ process.stdout.write = (() => true);
98
+ try {
99
+ return await fn();
100
+ }
101
+ finally {
102
+ process.stdout.write = write;
103
+ }
104
+ }
105
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/bridge/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type EnvReader = Readonly<Record<string, string | undefined>>;
2
+ export declare function envString(env: EnvReader, name: string, fallback: string): string;
3
+ export declare function envBool(env: EnvReader, name: string, fallback: boolean): boolean;
4
+ export declare function envInt(env: EnvReader, name: string, fallback: number): number;
5
+ export declare function envList(env: EnvReader, name: string, fallback?: string): string[];
6
+ export declare function appendNodeOption(existing: string | undefined, option: string): string;
7
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/bridge/env.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAErE,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAIhF;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7E;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM,EAAE,CAc7E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAErF"}
@@ -0,0 +1,38 @@
1
+ export function envString(env, name, fallback) {
2
+ return env[name] ?? fallback;
3
+ }
4
+ export function envBool(env, name, fallback) {
5
+ const value = env[name];
6
+ if (value === undefined)
7
+ return fallback;
8
+ return ['1', 'true', 'yes', 'on'].includes(value.toLowerCase());
9
+ }
10
+ export function envInt(env, name, fallback) {
11
+ const value = env[name];
12
+ if (value === undefined || value === '')
13
+ return fallback;
14
+ const parsed = Number.parseInt(value, 10);
15
+ if (!Number.isInteger(parsed))
16
+ throw new Error(`${name} must be an integer, got "${value}"`);
17
+ return parsed;
18
+ }
19
+ export function envList(env, name, fallback = '') {
20
+ const raw = env[name] ?? fallback;
21
+ if (!raw.trim())
22
+ return [];
23
+ if (raw.trim().startsWith('[')) {
24
+ const parsed = JSON.parse(raw);
25
+ if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== 'string')) {
26
+ throw new Error(`${name} must be a JSON string array`);
27
+ }
28
+ return parsed;
29
+ }
30
+ return raw
31
+ .split(',')
32
+ .map((item) => item.trim())
33
+ .filter(Boolean);
34
+ }
35
+ export function appendNodeOption(existing, option) {
36
+ return existing ? `${existing} ${option}` : option;
37
+ }
38
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/bridge/env.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,GAAc,EAAE,IAAY,EAAE,QAAgB;IACtE,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAc,EAAE,IAAY,EAAE,QAAiB;IACrE,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACzC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,GAAc,EAAE,IAAY,EAAE,QAAgB;IACnE,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IACzD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6BAA6B,KAAK,GAAG,CAAC,CAAC;IAC7F,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAc,EAAE,IAAY,EAAE,QAAQ,GAAG,EAAE;IACjE,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;IAClC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAC3B,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8BAA8B,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAA4B,EAAE,MAAc;IAC3E,OAAO,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AACrD,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function resolvePlaywrightMcpCliPath(): string;
2
+ export declare function resolvePlaywrightCoreBundlePath(): string;
3
+ export declare function currentModuleDir(metaUrl: string): string;
4
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/bridge/paths.ts"],"names":[],"mappings":"AAOA,wBAAgB,2BAA2B,IAAI,MAAM,CAMpD;AAED,wBAAgB,+BAA+B,IAAI,MAAM,CAiBxD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -0,0 +1,30 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const requireFromHere = createRequire(import.meta.url);
6
+ export function resolvePlaywrightMcpCliPath() {
7
+ if (process.env.PLAYWRIGHT_MCP_CLI_PATH)
8
+ return process.env.PLAYWRIGHT_MCP_CLI_PATH;
9
+ if (existsSync('/app/cli.js'))
10
+ return '/app/cli.js';
11
+ const packageJson = requireFromHere.resolve('@playwright/mcp/package.json');
12
+ return path.join(path.dirname(packageJson), 'cli.js');
13
+ }
14
+ export function resolvePlaywrightCoreBundlePath() {
15
+ if (process.env.CLOAK_PLAYWRIGHT_MCP_CORE_BUNDLE_PATH) {
16
+ return process.env.CLOAK_PLAYWRIGHT_MCP_CORE_BUNDLE_PATH;
17
+ }
18
+ if (existsSync('/app/node_modules/playwright-core/lib/coreBundle.js')) {
19
+ return '/app/node_modules/playwright-core/lib/coreBundle';
20
+ }
21
+ const packageJson = requireFromHere.resolve('@playwright/mcp/package.json');
22
+ const nestedCoreBundle = path.join(path.dirname(packageJson), 'node_modules', 'playwright-core', 'lib', 'coreBundle');
23
+ if (existsSync(`${nestedCoreBundle}.js`))
24
+ return nestedCoreBundle;
25
+ return requireFromHere.resolve('playwright-core/lib/coreBundle');
26
+ }
27
+ export function currentModuleDir(metaUrl) {
28
+ return path.dirname(fileURLToPath(metaUrl));
29
+ }
30
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/bridge/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEvD,MAAM,UAAU,2BAA2B;IACzC,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IACpF,IAAI,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,aAAa,CAAC;IAEpD,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,+BAA+B;IAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,qCAAqC,EAAE,CAAC;QACtD,OAAO,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC;IAC3D,CAAC;IACD,IAAI,UAAU,CAAC,qDAAqD,CAAC,EAAE,CAAC;QACtE,OAAO,kDAAkD,CAAC;IAC5D,CAAC;IACD,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC5E,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAChC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EACzB,cAAc,EACd,iBAAiB,EACjB,KAAK,EACL,YAAY,CACb,CAAC;IACF,IAAI,UAAU,CAAC,GAAG,gBAAgB,KAAK,CAAC;QAAE,OAAO,gBAAgB,CAAC;IAClE,OAAO,eAAe,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import { type BridgeRuntime } from './config.js';
3
+ export declare const localToolNames: readonly ["cloakbrowser_binary_info", "cloakbrowser_bridge_info"];
4
+ export type LocalToolName = (typeof localToolNames)[number];
5
+ export declare function createLocalTools(): Tool[];
6
+ export declare function isLocalTool(name: string): name is LocalToolName;
7
+ export declare function callLocalTool(name: LocalToolName, runtime: BridgeRuntime, upstreamToolCount: number): CallToolResult;
8
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/bridge/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAS5E,eAAO,MAAM,cAAc,mEAAoE,CAAC;AAGhG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAiC5D,wBAAgB,gBAAgB,IAAI,IAAI,EAAE,CAEzC;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,aAAa,CAE/D;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,aAAa,EACtB,iBAAiB,EAAE,MAAM,GACxB,cAAc,CA0BhB"}
@@ -0,0 +1,72 @@
1
+ import { getCurrentCloakBinaryInfo } from './config.js';
2
+ import { CLOAKBROWSER_TOOL_COUNT, PLAYWRIGHT_MCP_BROWSER_TOOL_COUNT, PLAYWRIGHT_MCP_PACKAGE, PLAYWRIGHT_MCP_VERSION, PROJECT_METADATA, } from '../project/metadata.js';
3
+ export const localToolNames = ['cloakbrowser_binary_info', 'cloakbrowser_bridge_info'];
4
+ const localToolNameSet = new Set(localToolNames);
5
+ const emptyInputSchema = {
6
+ type: 'object',
7
+ properties: {},
8
+ additionalProperties: false,
9
+ };
10
+ const localTools = [
11
+ {
12
+ name: 'cloakbrowser_binary_info',
13
+ title: 'CloakBrowser binary info',
14
+ description: 'Return CloakBrowser package, cache, platform, and resolved browser binary information.',
15
+ inputSchema: emptyInputSchema,
16
+ annotations: {
17
+ readOnlyHint: true,
18
+ idempotentHint: true,
19
+ openWorldHint: false,
20
+ },
21
+ },
22
+ {
23
+ name: 'cloakbrowser_bridge_info',
24
+ title: 'CloakBrowser bridge info',
25
+ description: 'Return runtime metadata for the CloakBrowser bridge over upstream Playwright MCP.',
26
+ inputSchema: emptyInputSchema,
27
+ annotations: {
28
+ readOnlyHint: true,
29
+ idempotentHint: true,
30
+ openWorldHint: false,
31
+ },
32
+ },
33
+ ];
34
+ export function createLocalTools() {
35
+ return localTools;
36
+ }
37
+ export function isLocalTool(name) {
38
+ return localToolNameSet.has(name);
39
+ }
40
+ export function callLocalTool(name, runtime, upstreamToolCount) {
41
+ if (name === 'cloakbrowser_binary_info') {
42
+ return jsonResult({
43
+ browserEngine: runtime.browserEngine,
44
+ executablePath: runtime.cloakBinaryPath ?? null,
45
+ outputDir: runtime.outputDir,
46
+ binary: getCurrentCloakBinaryInfo(),
47
+ });
48
+ }
49
+ return jsonResult({
50
+ name: PROJECT_METADATA.mcpName,
51
+ title: PROJECT_METADATA.title,
52
+ version: PROJECT_METADATA.version,
53
+ runtime: 'playwright-mcp-bridge',
54
+ browserEngine: runtime.browserEngine,
55
+ upstream: {
56
+ package: PLAYWRIGHT_MCP_PACKAGE,
57
+ version: PLAYWRIGHT_MCP_VERSION,
58
+ toolCount: upstreamToolCount || PLAYWRIGHT_MCP_BROWSER_TOOL_COUNT,
59
+ },
60
+ localTools: {
61
+ toolCount: CLOAKBROWSER_TOOL_COUNT,
62
+ names: localToolNames,
63
+ },
64
+ });
65
+ }
66
+ function jsonResult(value) {
67
+ return {
68
+ content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
69
+ structuredContent: value,
70
+ };
71
+ }
72
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/bridge/tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAsB,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,uBAAuB,EACvB,iCAAiC,EACjC,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,0BAA0B,EAAE,0BAA0B,CAAU,CAAC;AAChG,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAS,cAAc,CAAC,CAAC;AAIzD,MAAM,gBAAgB,GAAG;IACvB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE,EAAE;IACd,oBAAoB,EAAE,KAAK;CACnB,CAAC;AAEX,MAAM,UAAU,GAAW;IACzB;QACE,IAAI,EAAE,0BAA0B;QAChC,KAAK,EAAE,0BAA0B;QACjC,WAAW,EAAE,wFAAwF;QACrG,WAAW,EAAE,gBAAgB;QAC7B,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,KAAK;SACrB;KACF;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,KAAK,EAAE,0BAA0B;QACjC,WAAW,EAAE,mFAAmF;QAChG,WAAW,EAAE,gBAAgB;QAC7B,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,KAAK;SACrB;KACF;CACF,CAAC;AAEF,MAAM,UAAU,gBAAgB;IAC9B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAmB,EACnB,OAAsB,EACtB,iBAAyB;IAEzB,IAAI,IAAI,KAAK,0BAA0B,EAAE,CAAC;QACxC,OAAO,UAAU,CAAC;YAChB,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,cAAc,EAAE,OAAO,CAAC,eAAe,IAAI,IAAI;YAC/C,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,MAAM,EAAE,yBAAyB,EAAE;SACpC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;QAChB,IAAI,EAAE,gBAAgB,CAAC,OAAO;QAC9B,KAAK,EAAE,gBAAgB,CAAC,KAAK;QAC7B,OAAO,EAAE,gBAAgB,CAAC,OAAO;QACjC,OAAO,EAAE,uBAAuB;QAChC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,QAAQ,EAAE;YACR,OAAO,EAAE,sBAAsB;YAC/B,OAAO,EAAE,sBAAsB;YAC/B,SAAS,EAAE,iBAAiB,IAAI,iCAAiC;SAClE;QACD,UAAU,EAAE;YACV,SAAS,EAAE,uBAAuB;YAClC,KAAK,EAAE,cAAc;SACtB;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,KAA8B;IAChD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACjE,iBAAiB,EAAE,KAAK;KACzB,CAAC;AACJ,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import process from 'node:process';
4
+ import { PROJECT_METADATA } from './project/metadata.js';
5
+ import { startBridge } from './server.js';
6
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
7
+ const help = `cloakbrowser-mcp ${pkg.version}
8
+
9
+ Playwright MCP bridge backed by CloakBrowser.
10
+
11
+ Usage:
12
+ cloakbrowser-mcp
13
+ cloakbrowser-mcp --help
14
+ cloakbrowser-mcp --version
15
+
16
+ Primary configuration is provided with PLAYWRIGHT_MCP_* environment variables.
17
+ Cloak-specific toggles use CLOAK_PLAYWRIGHT_MCP_*.
18
+
19
+ Common environment variables:
20
+ PLAYWRIGHT_MCP_BROWSER_ENGINE cloak | playwright (default: cloak)
21
+ PLAYWRIGHT_MCP_HEADLESS true | false (default: true)
22
+ PLAYWRIGHT_MCP_OUTPUT_DIR artifact directory (default: .playwright-mcp)
23
+ PLAYWRIGHT_MCP_OUTPUT_MODE stdout | file (default: stdout)
24
+ PLAYWRIGHT_MCP_VIEWPORT_SIZE WIDTHxHEIGHT
25
+ PLAYWRIGHT_MCP_TIMEOUT_ACTION action timeout ms (default: 5000)
26
+ PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION navigation timeout ms (default: 60000)
27
+ CLOAK_PLAYWRIGHT_MCP_CONSOLE_FALLBACK true | false (default: true)
28
+ CLOAK_PLAYWRIGHT_MCP_STEALTH_ARGS true | false (default: true)
29
+ CLOAK_PLAYWRIGHT_MCP_EXTRA_ARGS comma-separated or JSON array Chromium args
30
+ `;
31
+ async function main() {
32
+ const args = process.argv.slice(2);
33
+ if (args.includes('--help') || args.includes('-h')) {
34
+ process.stdout.write(help);
35
+ return;
36
+ }
37
+ if (args.includes('--version') || args.includes('-V')) {
38
+ process.stdout.write(`${pkg.version}\n`);
39
+ return;
40
+ }
41
+ const bridge = await startBridge({
42
+ serverInfo: {
43
+ name: PROJECT_METADATA.mcpName,
44
+ title: PROJECT_METADATA.title,
45
+ version: pkg.version,
46
+ description: PROJECT_METADATA.description,
47
+ websiteUrl: PROJECT_METADATA.websiteUrl,
48
+ icons: PROJECT_METADATA.icons,
49
+ },
50
+ });
51
+ for (const signal of ['SIGINT', 'SIGTERM']) {
52
+ process.once(signal, () => {
53
+ void bridge.dispose().finally(() => process.exit(0));
54
+ });
55
+ }
56
+ }
57
+ void main().catch((error) => {
58
+ process.stderr.write(`fatal: ${error.message}\n`);
59
+ process.exit(1);
60
+ });
61
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAEvF,CAAC;AAEF,MAAM,IAAI,GAAG,oBAAoB,GAAG,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;CAuB3C,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;QAC/B,UAAU,EAAE;YACV,IAAI,EAAE,gBAAgB,CAAC,OAAO;YAC9B,KAAK,EAAE,gBAAgB,CAAC,KAAK;YAC7B,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,WAAW,EAAE,gBAAgB,CAAC,WAAW;YACzC,UAAU,EAAE,gBAAgB,CAAC,UAAU;YACvC,KAAK,EAAE,gBAAgB,CAAC,KAAK;SAC9B;KACF,CAAC,CAAC;IAEH,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;YACxB,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAW,KAAe,CAAC,OAAO,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { PROJECT_METADATA } from './project/metadata.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { PROJECT_METADATA } from './project/metadata.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,20 @@
1
+ export declare const PLAYWRIGHT_MCP_PACKAGE = "@playwright/mcp";
2
+ export declare const PLAYWRIGHT_MCP_VERSION: string;
3
+ export declare const PLAYWRIGHT_MCP_BROWSER_TOOL_COUNT = 23;
4
+ export declare const CLOAKBROWSER_TOOL_COUNT = 2;
5
+ export declare const PROJECT_METADATA: Readonly<{
6
+ packageName: string;
7
+ version: string;
8
+ license: string;
9
+ mcpName: string;
10
+ title: "CloakBrowser MCP";
11
+ description: string;
12
+ websiteUrl: "https://swimmwatch.github.io/cloakbrowser-mcp/";
13
+ icons: {
14
+ src: string;
15
+ mimeType: string;
16
+ sizes: string[];
17
+ }[];
18
+ }>;
19
+ export declare const MCP_SERVER_INSTRUCTIONS: string;
20
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/project/metadata.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AACxD,eAAO,MAAM,sBAAsB,QAAgD,CAAC;AACpF,eAAO,MAAM,iCAAiC,KAAK,CAAC;AACpD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;EAiB3B,CAAC;AAEH,eAAO,MAAM,uBAAuB,QAIzB,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ const packageMetadata = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
4
+ const requireFromHere = createRequire(import.meta.url);
5
+ export const PLAYWRIGHT_MCP_PACKAGE = '@playwright/mcp';
6
+ export const PLAYWRIGHT_MCP_VERSION = readDependencyVersion(PLAYWRIGHT_MCP_PACKAGE);
7
+ export const PLAYWRIGHT_MCP_BROWSER_TOOL_COUNT = 23;
8
+ export const CLOAKBROWSER_TOOL_COUNT = 2;
9
+ export const PROJECT_METADATA = Object.freeze({
10
+ packageName: packageMetadata.name,
11
+ version: packageMetadata.version,
12
+ license: packageMetadata.license ?? 'MIT',
13
+ mcpName: packageMetadata.mcpName ?? 'io.github.swimmwatch/cloakbrowser-mcp',
14
+ title: 'CloakBrowser MCP',
15
+ description: packageMetadata.description ??
16
+ 'Playwright MCP bridge that runs upstream browser tools with the CloakBrowser Chromium binary.',
17
+ websiteUrl: 'https://swimmwatch.github.io/cloakbrowser-mcp/',
18
+ icons: [
19
+ {
20
+ src: 'https://swimmwatch.github.io/cloakbrowser-mcp/assets/brand/logo.svg',
21
+ mimeType: 'image/svg+xml',
22
+ sizes: ['any'],
23
+ },
24
+ ],
25
+ });
26
+ export const MCP_SERVER_INSTRUCTIONS = [
27
+ 'This server forwards upstream Playwright MCP browser tools and runs them with CloakBrowser by default.',
28
+ 'Use browser_snapshot before interacting with unfamiliar pages.',
29
+ 'Use cloakbrowser_bridge_info and cloakbrowser_binary_info for bridge diagnostics.',
30
+ ].join(' ');
31
+ function readDependencyVersion(packageName) {
32
+ const packageJson = requireFromHere.resolve(`${packageName}/package.json`);
33
+ const metadata = JSON.parse(readFileSync(packageJson, 'utf8'));
34
+ return metadata.version ?? 'unknown';
35
+ }
36
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/project/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAU5C,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAChC,YAAY,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAClD,CAAC;AACrB,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEvD,MAAM,CAAC,MAAM,sBAAsB,GAAG,iBAAiB,CAAC;AACxD,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAC,sBAAsB,CAAC,CAAC;AACpF,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AACpD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAEzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5C,WAAW,EAAE,eAAe,CAAC,IAAI;IACjC,OAAO,EAAE,eAAe,CAAC,OAAO;IAChC,OAAO,EAAE,eAAe,CAAC,OAAO,IAAI,KAAK;IACzC,OAAO,EAAE,eAAe,CAAC,OAAO,IAAI,uCAAuC;IAC3E,KAAK,EAAE,kBAAkB;IACzB,WAAW,EACT,eAAe,CAAC,WAAW;QAC3B,+FAA+F;IACjG,UAAU,EAAE,gDAAgD;IAC5D,KAAK,EAAE;QACL;YACE,GAAG,EAAE,qEAAqE;YAC1E,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,CAAC,KAAK,CAAC;SACf;KACF;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,wGAAwG;IACxG,gEAAgE;IAChE,mFAAmF;CACpF,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,SAAS,qBAAqB,CAAC,WAAmB;IAChD,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,WAAW,eAAe,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAyB,CAAC;IACvF,OAAO,QAAQ,CAAC,OAAO,IAAI,SAAS,CAAC;AACvC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const consoleFallbackInitScript = "(() => {\n const messages = [];\n Object.defineProperty(globalThis, '__cloakMcpConsoleMessages', {\n value: messages,\n configurable: true,\n });\n\n const methods = {\n debug: 'debug',\n error: 'error',\n info: 'info',\n log: 'log',\n warn: 'warning',\n };\n\n const formatValue = (value) => {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n };\n\n const sourceLocation = () => {\n const stack = new Error().stack ?? '';\n for (const line of stack.split('\\n')) {\n const match = /((?:https?|file):[^)\\s]+):(\\d+):(\\d+)/.exec(line);\n if (match) return { url: match[1], lineNumber: Math.max(Number(match[2]) - 1, 0) };\n }\n return { url: location.href, lineNumber: 0 };\n };\n\n for (const [method, type] of Object.entries(methods)) {\n const original = console[method]?.bind(console);\n if (!original) continue;\n console[method] = (...args) => {\n const entry = sourceLocation();\n messages.push({\n type,\n text: args.map(formatValue).join(' '),\n timestamp: Date.now(),\n url: entry.url,\n lineNumber: entry.lineNumber,\n });\n return original(...args);\n };\n }\n})();";
2
+ export declare function consoleFallbackPreloadScript(coreBundlePath: string): string;
3
+ //# sourceMappingURL=consoleFallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consoleFallback.d.ts","sourceRoot":"","sources":["../../src/runtime/consoleFallback.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,0xCAgDhC,CAAC;AAEP,wBAAgB,4BAA4B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CA2E3E"}
@@ -0,0 +1,126 @@
1
+ export const consoleFallbackInitScript = `(() => {
2
+ const messages = [];
3
+ Object.defineProperty(globalThis, '__cloakMcpConsoleMessages', {
4
+ value: messages,
5
+ configurable: true,
6
+ });
7
+
8
+ const methods = {
9
+ debug: 'debug',
10
+ error: 'error',
11
+ info: 'info',
12
+ log: 'log',
13
+ warn: 'warning',
14
+ };
15
+
16
+ const formatValue = (value) => {
17
+ if (typeof value === 'string') return value;
18
+ try {
19
+ return JSON.stringify(value);
20
+ } catch {
21
+ return String(value);
22
+ }
23
+ };
24
+
25
+ const sourceLocation = () => {
26
+ const stack = new Error().stack ?? '';
27
+ for (const line of stack.split('\\n')) {
28
+ const match = /((?:https?|file):[^)\\s]+):(\\d+):(\\d+)/.exec(line);
29
+ if (match) return { url: match[1], lineNumber: Math.max(Number(match[2]) - 1, 0) };
30
+ }
31
+ return { url: location.href, lineNumber: 0 };
32
+ };
33
+
34
+ for (const [method, type] of Object.entries(methods)) {
35
+ const original = console[method]?.bind(console);
36
+ if (!original) continue;
37
+ console[method] = (...args) => {
38
+ const entry = sourceLocation();
39
+ messages.push({
40
+ type,
41
+ text: args.map(formatValue).join(' '),
42
+ timestamp: Date.now(),
43
+ url: entry.url,
44
+ lineNumber: entry.lineNumber,
45
+ });
46
+ return original(...args);
47
+ };
48
+ }
49
+ })();`;
50
+ export function consoleFallbackPreloadScript(coreBundlePath) {
51
+ return `'use strict';
52
+
53
+ if (process.env.CLOAK_PLAYWRIGHT_MCP_CONSOLE_FALLBACK !== 'false') {
54
+ const { tools } = require(${JSON.stringify(coreBundlePath)});
55
+ const originalCount = tools.Tab.prototype.consoleMessageCount;
56
+ const originalMessages = tools.Tab.prototype.consoleMessages;
57
+ const originalClear = tools.Tab.prototype.clearConsoleMessages;
58
+
59
+ tools.Tab.prototype.consoleMessageCount = async function consoleMessageCount() {
60
+ const nativeCount = await originalCount.call(this);
61
+ if (nativeCount.total > 0) return nativeCount;
62
+ if (hasModalState(this)) return nativeCount;
63
+ const messages = await readMessages(this.page);
64
+ return {
65
+ total: messages.length,
66
+ errors: messages.filter((message) => message.type === 'error').length,
67
+ warnings: messages.filter((message) => message.type === 'warning').length,
68
+ };
69
+ };
70
+
71
+ tools.Tab.prototype.consoleMessages = async function consoleMessages(level, all) {
72
+ const nativeMessages = await originalMessages.call(this, level, all);
73
+ if (nativeMessages.length > 0) return nativeMessages;
74
+ if (hasModalState(this)) return nativeMessages;
75
+ const messages = await readMessages(this.page);
76
+ return messages.filter((message) => includesLevel(level, message.type)).map(toConsoleMessage);
77
+ };
78
+
79
+ tools.Tab.prototype.clearConsoleMessages = async function clearConsoleMessages() {
80
+ await originalClear.call(this);
81
+ await this.page
82
+ .evaluate(() => {
83
+ if (Array.isArray(globalThis.__cloakMcpConsoleMessages)) {
84
+ globalThis.__cloakMcpConsoleMessages.length = 0;
85
+ }
86
+ })
87
+ .catch(() => undefined);
88
+ };
89
+ }
90
+
91
+ function hasModalState(tab) {
92
+ return Array.isArray(tab._modalStates) && tab._modalStates.length > 0;
93
+ }
94
+
95
+ async function readMessages(page) {
96
+ return page
97
+ .evaluate(() => {
98
+ const messages = globalThis.__cloakMcpConsoleMessages;
99
+ return Array.isArray(messages) ? messages : [];
100
+ })
101
+ .catch(() => []);
102
+ }
103
+
104
+ function toConsoleMessage(message) {
105
+ return {
106
+ type: message.type,
107
+ timestamp: message.timestamp,
108
+ text: message.text,
109
+ toString: () => \`[\${message.type.toUpperCase()}] \${message.text} @ \${message.url}:\${message.lineNumber}\`,
110
+ };
111
+ }
112
+
113
+ function includesLevel(level, type) {
114
+ const levels = ['error', 'warning', 'info', 'debug'];
115
+ return levels.indexOf(levelForType(type)) <= levels.indexOf(level ?? 'info');
116
+ }
117
+
118
+ function levelForType(type) {
119
+ if (type === 'error' || type === 'assert') return 'error';
120
+ if (type === 'warning') return 'warning';
121
+ if (type === 'debug') return 'debug';
122
+ return 'info';
123
+ }
124
+ `;
125
+ }
126
+ //# sourceMappingURL=consoleFallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consoleFallback.js","sourceRoot":"","sources":["../../src/runtime/consoleFallback.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAgDnC,CAAC;AAEP,MAAM,UAAU,4BAA4B,CAAC,cAAsB;IACjE,OAAO;;;8BAGqB,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsE3D,CAAC;AACF,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
4
+ import { type Implementation } from '@modelcontextprotocol/sdk/types.js';
5
+ import { type BridgeRuntime } from './bridge/config.js';
6
+ export interface StartBridgeOptions {
7
+ serverInfo?: Partial<Implementation>;
8
+ }
9
+ export interface BridgeServerOptions extends StartBridgeOptions {
10
+ runtime?: BridgeRuntime;
11
+ upstreamClient?: Client;
12
+ }
13
+ export interface BridgeServer {
14
+ server: Server;
15
+ runtime: BridgeRuntime;
16
+ start(transport?: Transport): Promise<void>;
17
+ dispose(): Promise<void>;
18
+ }
19
+ export declare function startBridge(options?: StartBridgeOptions): Promise<BridgeServer>;
20
+ export declare function createBridgeServer(options?: BridgeServerOptions): Promise<BridgeServer>;
21
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAGL,KAAK,cAAc,EAIpB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAK9E,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC,CAIzF;AAED,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC,CAwCjG"}
package/dist/server.js ADDED
@@ -0,0 +1,90 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
6
+ import { prepareBridgeRuntime } from './bridge/config.js';
7
+ import { resolvePlaywrightMcpCliPath } from './bridge/paths.js';
8
+ import { callLocalTool, createLocalTools, isLocalTool } from './bridge/tools.js';
9
+ import { MCP_SERVER_INSTRUCTIONS, PROJECT_METADATA } from './project/metadata.js';
10
+ export async function startBridge(options = {}) {
11
+ const bridge = await createBridgeServer(options);
12
+ await bridge.start();
13
+ return bridge;
14
+ }
15
+ export async function createBridgeServer(options = {}) {
16
+ const runtime = options.runtime ?? (await prepareBridgeRuntime());
17
+ const upstreamClient = options.upstreamClient ?? (await connectUpstream(runtime));
18
+ const localTools = createLocalTools();
19
+ const upstreamToolCache = new Map();
20
+ let upstreamToolCount = 0;
21
+ const server = new Server(createServerInfo(options.serverInfo), {
22
+ capabilities: { tools: {} },
23
+ instructions: MCP_SERVER_INSTRUCTIONS,
24
+ });
25
+ server.setRequestHandler(ListToolsRequestSchema, async (request) => {
26
+ const upstream = await listUpstreamTools(upstreamClient, upstreamToolCache, request.params);
27
+ upstreamToolCount = Math.max(upstreamToolCount, upstream.tools.length);
28
+ if (request.params?.cursor)
29
+ return upstream;
30
+ return {
31
+ ...upstream,
32
+ tools: [...upstream.tools, ...localTools],
33
+ };
34
+ });
35
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
36
+ if (isLocalTool(request.params.name)) {
37
+ return callLocalTool(request.params.name, runtime, upstreamToolCount);
38
+ }
39
+ return (await upstreamClient.callTool(request.params));
40
+ });
41
+ return {
42
+ server,
43
+ runtime,
44
+ async start(transport) {
45
+ await server.connect(transport ?? new StdioServerTransport());
46
+ },
47
+ async dispose() {
48
+ await Promise.allSettled([upstreamClient.close(), server.close()]);
49
+ runtime.dispose();
50
+ },
51
+ };
52
+ }
53
+ function listUpstreamTools(upstreamClient, cache, params) {
54
+ const cacheKey = params?.cursor ?? '';
55
+ const cached = cache.get(cacheKey);
56
+ if (cached)
57
+ return cached;
58
+ const pending = upstreamClient.listTools(params).then((result) => result, (error) => {
59
+ cache.delete(cacheKey);
60
+ throw error;
61
+ });
62
+ cache.set(cacheKey, pending);
63
+ return pending;
64
+ }
65
+ async function connectUpstream(runtime) {
66
+ const transport = new StdioClientTransport({
67
+ command: process.execPath,
68
+ args: [resolvePlaywrightMcpCliPath(), '--config', runtime.configPath],
69
+ env: runtime.childEnv,
70
+ stderr: 'inherit',
71
+ });
72
+ const client = new Client({
73
+ name: `${PROJECT_METADATA.mcpName}-upstream-client`,
74
+ version: PROJECT_METADATA.version,
75
+ });
76
+ await client.connect(transport);
77
+ return client;
78
+ }
79
+ function createServerInfo(serverInfo) {
80
+ return {
81
+ name: PROJECT_METADATA.mcpName,
82
+ title: PROJECT_METADATA.title,
83
+ version: PROJECT_METADATA.version,
84
+ description: PROJECT_METADATA.description,
85
+ websiteUrl: PROJECT_METADATA.websiteUrl,
86
+ icons: PROJECT_METADATA.icons,
87
+ ...serverInfo,
88
+ };
89
+ }
90
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "cloakbrowser-mcp",
3
+ "version": "1.0.1",
4
+ "description": "Playwright MCP bridge that runs upstream browser tools with CloakBrowser.",
5
+ "mcpName": "io.github.swimmwatch/cloakbrowser-mcp",
6
+ "type": "module",
7
+ "packageManager": "npm@10.9.2",
8
+ "license": "MIT",
9
+ "author": "swimmwatch",
10
+ "sideEffects": false,
11
+ "engines": {
12
+ "node": ">=20.0.0"
13
+ },
14
+ "exports": {
15
+ "./package.json": "./package.json"
16
+ },
17
+ "bin": {
18
+ "cloakbrowser-mcp": "dist/cli.js"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "LICENSE",
24
+ "server.json"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "registry": "https://registry.npmjs.org/"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.build.json",
32
+ "typecheck": "tsc --noEmit",
33
+ "clean": "rm -rf dist coverage",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest",
36
+ "test:unit": "vitest run tests/unit",
37
+ "test:integration": "vitest run tests/integration",
38
+ "test:coverage": "vitest run --coverage",
39
+ "start": "node dist/cli.js",
40
+ "dev": "tsx src/cli.ts",
41
+ "lint": "eslint .",
42
+ "lint:fix": "eslint . --fix",
43
+ "format": "prettier --write .",
44
+ "format:check": "prettier --check .",
45
+ "docs:install": "python3 -m venv .venv-docs && .venv-docs/bin/pip install -r docs/requirements.txt",
46
+ "docs:assets": "node scripts/generate-brand-assets.mjs",
47
+ "docs:serve": ".venv-docs/bin/mkdocs serve",
48
+ "docs:build": ".venv-docs/bin/mkdocs build --strict",
49
+ "docs:seo:validate": "node scripts/validate-docs-seo.mjs",
50
+ "version:apply": "node scripts/apply-release-version.mjs",
51
+ "server:validate": "node scripts/validate-server-json.mjs",
52
+ "audit:prod": "npm audit --omit=dev --audit-level=high",
53
+ "package:verify": "npm run build && npm run server:validate && node scripts/verify-npm-package.mjs",
54
+ "docker:build": "docker buildx build --load -t cloakbrowser-mcp:dev --build-arg RELEASE_VERSION=$npm_package_version --build-arg RELEASE_VERSION_TAG=v$npm_package_version .",
55
+ "docker:smoke": "docker run --rm --init cloakbrowser-mcp:dev --help",
56
+ "bridge:compare": "node scripts/compare-playwright-mcp-bridge.mjs",
57
+ "upstream:check": "node scripts/check-playwright-mcp-updates.mjs",
58
+ "check": "npm run typecheck && npm run lint && npm run format:check && npm run server:validate && npm run build && npm test",
59
+ "check:ci": "npm run check && npm run audit:prod && npm run test:coverage"
60
+ },
61
+ "dependencies": {
62
+ "@modelcontextprotocol/sdk": "^1.18.0",
63
+ "@playwright/mcp": "^0.0.75",
64
+ "cloakbrowser": "^0.3.30"
65
+ },
66
+ "devDependencies": {
67
+ "@eslint/js": "^9.13.0",
68
+ "@types/node": "^20.14.0",
69
+ "@vitest/coverage-v8": "^2.1.9",
70
+ "ajv": "^8.17.1",
71
+ "ajv-formats": "^3.0.1",
72
+ "eslint": "^9.13.0",
73
+ "eslint-config-prettier": "^9.1.0",
74
+ "prettier": "^3.3.3",
75
+ "tsx": "^4.19.0",
76
+ "typescript": "^5.5.4",
77
+ "typescript-eslint": "^8.11.0",
78
+ "vitest": "^2.0.5"
79
+ },
80
+ "keywords": [
81
+ "mcp",
82
+ "model-context-protocol",
83
+ "cloakbrowser",
84
+ "browser-automation",
85
+ "playwright",
86
+ "stealth",
87
+ "ai-agent"
88
+ ],
89
+ "repository": {
90
+ "type": "git",
91
+ "url": "git+https://github.com/swimmwatch/cloakbrowser-mcp.git"
92
+ },
93
+ "homepage": "https://swimmwatch.github.io/cloakbrowser-mcp/",
94
+ "bugs": {
95
+ "url": "https://github.com/swimmwatch/cloakbrowser-mcp/issues"
96
+ }
97
+ }
package/server.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.swimmwatch/cloakbrowser-mcp",
4
+ "title": "CloakBrowser MCP",
5
+ "description": "Playwright MCP bridge that runs upstream browser tools with the CloakBrowser Chromium binary.",
6
+ "version": "1.0.1",
7
+ "websiteUrl": "https://swimmwatch.github.io/cloakbrowser-mcp/",
8
+ "repository": {
9
+ "url": "https://github.com/swimmwatch/cloakbrowser-mcp",
10
+ "source": "github"
11
+ },
12
+ "icons": [
13
+ {
14
+ "src": "https://swimmwatch.github.io/cloakbrowser-mcp/assets/brand/logo.svg",
15
+ "mimeType": "image/svg+xml",
16
+ "sizes": ["any"]
17
+ }
18
+ ],
19
+ "packages": [
20
+ {
21
+ "registryType": "npm",
22
+ "registryBaseUrl": "https://registry.npmjs.org",
23
+ "identifier": "cloakbrowser-mcp",
24
+ "version": "1.0.1",
25
+ "transport": {
26
+ "type": "stdio"
27
+ },
28
+ "environmentVariables": [
29
+ {
30
+ "name": "PLAYWRIGHT_MCP_BROWSER_ENGINE",
31
+ "description": "Bridge browser engine: cloak or playwright.",
32
+ "format": "string",
33
+ "default": "cloak"
34
+ },
35
+ {
36
+ "name": "PLAYWRIGHT_MCP_HEADLESS",
37
+ "description": "Run the browser in headless mode.",
38
+ "format": "boolean",
39
+ "default": "true"
40
+ },
41
+ {
42
+ "name": "PLAYWRIGHT_MCP_OUTPUT_DIR",
43
+ "description": "Directory where upstream Playwright MCP writes artifacts.",
44
+ "format": "filepath",
45
+ "default": ".playwright-mcp"
46
+ },
47
+ {
48
+ "name": "PLAYWRIGHT_MCP_OUTPUT_MODE",
49
+ "description": "Return snapshots, console logs, and network logs through stdout or files.",
50
+ "format": "string",
51
+ "default": "stdout"
52
+ },
53
+ {
54
+ "name": "CLOAK_PLAYWRIGHT_MCP_CONSOLE_FALLBACK",
55
+ "description": "Patch upstream console message collection for CloakBrowser compatibility.",
56
+ "format": "boolean",
57
+ "default": "true"
58
+ },
59
+ {
60
+ "name": "CLOAK_PLAYWRIGHT_MCP_EXTRA_ARGS",
61
+ "description": "Comma-separated or JSON array of extra Chromium launch arguments.",
62
+ "format": "string"
63
+ }
64
+ ]
65
+ },
66
+ {
67
+ "registryType": "oci",
68
+ "registryBaseUrl": "https://ghcr.io",
69
+ "identifier": "ghcr.io/swimmwatch/cloakbrowser-mcp:1.0.1",
70
+ "transport": {
71
+ "type": "stdio"
72
+ },
73
+ "environmentVariables": [
74
+ {
75
+ "name": "PLAYWRIGHT_MCP_BROWSER_ENGINE",
76
+ "description": "Bridge browser engine: cloak or playwright.",
77
+ "format": "string",
78
+ "default": "cloak"
79
+ },
80
+ {
81
+ "name": "PLAYWRIGHT_MCP_HEADLESS",
82
+ "description": "Run the browser in headless mode.",
83
+ "format": "boolean",
84
+ "default": "true"
85
+ },
86
+ {
87
+ "name": "PLAYWRIGHT_MCP_OUTPUT_DIR",
88
+ "description": "Directory inside the container where upstream Playwright MCP writes artifacts.",
89
+ "format": "filepath",
90
+ "default": "/data"
91
+ },
92
+ {
93
+ "name": "PLAYWRIGHT_MCP_OUTPUT_MODE",
94
+ "description": "Return snapshots, console logs, and network logs through stdout or files.",
95
+ "format": "string",
96
+ "default": "stdout"
97
+ },
98
+ {
99
+ "name": "CLOAK_PLAYWRIGHT_MCP_CONSOLE_FALLBACK",
100
+ "description": "Patch upstream console message collection for CloakBrowser compatibility.",
101
+ "format": "boolean",
102
+ "default": "true"
103
+ },
104
+ {
105
+ "name": "CLOAK_PLAYWRIGHT_MCP_EXTRA_ARGS",
106
+ "description": "Comma-separated or JSON array of extra Chromium launch arguments.",
107
+ "format": "string"
108
+ }
109
+ ]
110
+ }
111
+ ]
112
+ }