smooth-operator-mcp 3.1.0 → 3.3.0
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/README.md +80 -91
- package/dist/smooth-operator.mjs +3293 -3190
- package/dist/smooth-operator.mjs.map +3 -3
- package/docs/STEALTH-GUIDE.md +31 -21
- package/docs/architecture.md +64 -0
- package/docs/config.md +54 -0
- package/docs/harnesses.md +9 -5
- package/docs/mcp-server.md +93 -49
- package/docs/security.md +58 -0
- package/docs/tools.md +90 -0
- package/package.json +10 -6
package/docs/STEALTH-GUIDE.md
CHANGED
|
@@ -10,28 +10,33 @@ deterministic input.
|
|
|
10
10
|
|
|
11
11
|
The installer asks exactly three questions: browser profile ownership, browser
|
|
12
12
|
display, and the Chromium executable. Managed mode owns one private persistent
|
|
13
|
-
profile below `SMOOTH_OPERATOR_DATA_DIR
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
profile below `SMOOTH_OPERATOR_DATA_DIR`. Wizard Personal Chrome launches a
|
|
14
|
+
dedicated debugging profile and does not take over daily Chrome. A profile is
|
|
15
|
+
not a privacy boundary from the websites it visits, so use a dedicated profile
|
|
16
|
+
and the narrowest policy that fits the task.
|
|
17
17
|
|
|
18
18
|
## Optional controls
|
|
19
19
|
|
|
20
|
-
`SMOOTH_OPERATOR_STEALTH_ENABLED=true` is retained as a compatibility
|
|
21
|
-
It
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
`SMOOTH_OPERATOR_STEALTH_ENABLED=true` is retained as a compatibility label.
|
|
21
|
+
It does not hide automation signals, inject a page script, or fabricate a user
|
|
22
|
+
agent, platform, browser version, language, client hints, WebGL, canvas, TLS,
|
|
23
|
+
or operating-system identity. An explicit viewport is applied through launch
|
|
24
|
+
`--window-size` and `page.setViewport`, independent of this flag.
|
|
25
25
|
|
|
26
26
|
`SMOOTH_OPERATOR_STEALTH_PROFILE=balanced` or `max` are accepted compatibility
|
|
27
|
-
labels
|
|
27
|
+
labels with no patch-set difference. `SMOOTH_OPERATOR_STEALTH_GPU=true`
|
|
28
28
|
adds GPU launch flags but is not an identity or coherence guarantee.
|
|
29
29
|
|
|
30
|
-
`SMOOTH_OPERATOR_BEHAVIOR_ENABLED` controls optional pointer
|
|
31
|
-
|
|
30
|
+
`SMOOTH_OPERATOR_BEHAVIOR_ENABLED` controls optional pointer and typing
|
|
31
|
+
timing wrappers. It defaults off for the fastest raw interaction
|
|
32
32
|
path. If enabled, timings are short, bounded, and cancellable; this is a
|
|
33
33
|
workflow choice, not a guarantee of human identity or site access.
|
|
34
34
|
|
|
35
|
+
Connect mode attaches to the configured `browserURL` / `wsEndpoint`. The
|
|
36
|
+
installer Personal Chrome helper uses a dedicated debugging profile
|
|
37
|
+
(`~/.smooth-operator/personal-chrome`) and does not take over daily Chrome.
|
|
38
|
+
Advanced `chrome://inspect` attach is documented in `mcp-server.md`.
|
|
39
|
+
|
|
35
40
|
## Bounded browser controls
|
|
36
41
|
|
|
37
42
|
`SMOOTH_OPERATOR_BROWSER_IDLE_TIMEOUT_MS` is disabled by default (`0`). When
|
|
@@ -51,22 +56,27 @@ are never captured by that journal.
|
|
|
51
56
|
animations, safe attributes, and shallow structure. It omits scripts, event
|
|
52
57
|
handler source, form values, password content, and arbitrary data attributes.
|
|
53
58
|
|
|
54
|
-
## Connected-
|
|
59
|
+
## Connected-harness challenge loop
|
|
55
60
|
|
|
56
61
|
`browser_challenge` is an evidence-only detector and is available by default.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
Title, text, HTML, frame-source, and visible-marker inputs have independent
|
|
63
|
+
limits so one oversized evidence category cannot hide later challenge markers.
|
|
64
|
+
`browser_solve_challenge` is one evidence cycle for the connected harness.
|
|
65
|
+
Each call collects a fresh challenge classification and bounded visual/state
|
|
66
|
+
evidence, including `attemptsRemaining`. The connected harness uses normal
|
|
67
|
+
browser actions and calls it again until the final classification explicitly
|
|
68
|
+
reports the challenge absent or `automation_exhausted`. The tool returns
|
|
69
|
+
screenshot data as MCP image content when requested. `present`, `unknown`,
|
|
70
|
+
or a failed probe is never success, and `browser_wait_for_human` is only an
|
|
64
71
|
explicit option after exhaustion.
|
|
65
72
|
|
|
66
73
|
`browser_wait_for_human` is an optional handoff for a person to complete a
|
|
67
74
|
visible challenge or sign-in step. It does not claim success without a fresh
|
|
68
75
|
final classification. The server does not rotate identities or open network,
|
|
69
|
-
file, or authentication permissions for challenge handling.
|
|
76
|
+
file, or authentication permissions for challenge handling. Launch args stay
|
|
77
|
+
native-identity: they do not set `navigator.webdriver` to false, invent
|
|
78
|
+
UA/platform/client-hint/WebGL/canvas strings, or advertise `HeadlessChrome` on
|
|
79
|
+
the headed path.
|
|
70
80
|
|
|
71
81
|
## Boundaries and responsible use
|
|
72
82
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
SmoothOperator is a standalone Node.js MCP browser server. The harness reasons.
|
|
4
|
+
The server validates, enforces policy twice, drives one Chromium profile, and
|
|
5
|
+
returns a bounded result envelope. There is no model SDK, planner loop,
|
|
6
|
+
packaged browser add-on, or arbitrary CDP/host-code tool.
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
MCP client
|
|
10
|
+
├─ stdio, or Streamable HTTP (`/mcp`) + sibling `/healthz`
|
|
11
|
+
└─ MCP registry (one catalog → tools, resources, prompts)
|
|
12
|
+
└─ ServerRuntime
|
|
13
|
+
├─ SecurityPolicy (edge + service, same implementation)
|
|
14
|
+
├─ BrowserService (exclusive queue + Puppeteer/CDP)
|
|
15
|
+
├─ ResearchService (bounded DuckDuckGo HTML)
|
|
16
|
+
└─ Logger (JSON stderr, redacted)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Module ownership
|
|
20
|
+
|
|
21
|
+
| Module | Owns |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `src/server/main.ts` | CLI dispatch: `server`, `install`, `doctor` |
|
|
24
|
+
| `src/server/cli.ts` | Help, version, doctor diagnostics |
|
|
25
|
+
| `src/server/http.ts` | Streamable HTTP, Host/Origin/bearer, `healthz`, shutdown bounds |
|
|
26
|
+
| `src/server/catalog.ts` | Locked 57-tool names, retired-name map, resources, prompts, instructions |
|
|
27
|
+
| `src/server/mcp.ts` | Registration and dispatch from the catalog |
|
|
28
|
+
| `src/server/envelope.ts` | One result envelope: bound, redact, explicit truncation |
|
|
29
|
+
| `src/server/contracts.ts` | Zod schemas shared by tools and `browser_batch` |
|
|
30
|
+
| `src/server/runtime.ts` | Lifecycle, exclusive profile lease, health |
|
|
31
|
+
| `src/server/policy.ts` | URL/DNS/file/eval gates used at the MCP edge and again at the service |
|
|
32
|
+
| `src/server/config.ts` | Env + JSON config, chmod-600 reads |
|
|
33
|
+
| `src/server/browser/service.ts` | Browser facade: execute, snapshot, tabs, doctor, four modes |
|
|
34
|
+
| `src/server/browser/queue.ts` | Exclusive/read lane, absolute queue deadline, abort recovery |
|
|
35
|
+
| `src/server/browser/challenges.ts` | Pure evidence → classification |
|
|
36
|
+
| `src/server/research.ts` | Bounded search, `redirect: "error"`, anti-bot reported not bypassed |
|
|
37
|
+
| `src/server/installer.ts` / `installer-wizard.ts` | Harness install, three-question wizard, atomic config IO |
|
|
38
|
+
| `src/server/errors.ts` / `logger.ts` / `security.ts` | AppError + recovery, JSON logs, untrusted wrappers |
|
|
39
|
+
|
|
40
|
+
## One of each
|
|
41
|
+
|
|
42
|
+
- **One catalog** feeds `tools/list` and handlers (`catalog.ts`).
|
|
43
|
+
- **One envelope** serializes success and error (`envelope.ts`). Text fallback equals structured content.
|
|
44
|
+
- **One policy implementation** (`SecurityPolicy`) is applied at the MCP edge (navigate/eval) and again inside `BrowserService` / research.
|
|
45
|
+
- **One exclusive profile lease** (`runtime.ts` lock file) plus one in-process operation queue (`browser/queue.ts`). A timed-out operation recovers the old lifecycle before the next waiter is released.
|
|
46
|
+
|
|
47
|
+
## Browser modes
|
|
48
|
+
|
|
49
|
+
| Mode | Process | Profile |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `managed` | Launch or reattach via `DevToolsActivePort` | `${DATA_DIR}/browser` |
|
|
52
|
+
| `connect` | Attach to configured `browserURL` / `wsEndpoint` | Wizard Personal Chrome uses `personal-chrome`; `chrome://inspect` daily-profile attach is advanced opt-in (`mcp-server.md`) |
|
|
53
|
+
| `launch` | Launch configured executable | Configured `userDataDir` |
|
|
54
|
+
| `disabled` | None | Browser tools fail `BROWSER_DISABLED`; policy still runs |
|
|
55
|
+
|
|
56
|
+
## Transports
|
|
57
|
+
|
|
58
|
+
Stdio is the default. HTTP binds loopback unless `SMOOTH_OPERATOR_ALLOW_REMOTE_HTTP=true` with a 32-character token. `GET/HEAD <path>/healthz` uses the same Host, Origin, and bearer checks as `/mcp` and returns `{ status, ready, server, transport, checks }` without page data.
|
|
59
|
+
|
|
60
|
+
## Verify
|
|
61
|
+
|
|
62
|
+
`npm run verify` is lint, typecheck, coverage tests, knip, and package smoke
|
|
63
|
+
(which builds). Coverage and `npm test` pass `--exclude tests/browser-live.test.ts`.
|
|
64
|
+
Live Chrome is only `npm run test:browser:live` (explicit `vitest run tests/browser-live.test.ts`) and is not inside verify.
|
package/docs/config.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Environment variables or `--config` JSON (`chmod 600`, no symlinks).
|
|
4
|
+
Comma-separated lists reject more than 128 entries before normalization.
|
|
5
|
+
|
|
6
|
+
| Variable | Default | Purpose |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `SMOOTH_OPERATOR_TRANSPORT` | `stdio` | `stdio` or `http` |
|
|
9
|
+
| `SMOOTH_OPERATOR_CONFIG` | auto `~/.smooth-operator/config.json` | Explicit JSON path |
|
|
10
|
+
| `SMOOTH_OPERATOR_DATA_DIR` | `~/.smooth-operator` | Data root |
|
|
11
|
+
| `SMOOTH_OPERATOR_BROWSER_MODE` | `managed` | `managed` / `connect` / `launch` / `disabled` |
|
|
12
|
+
| `SMOOTH_OPERATOR_BROWSER_URL` | `http://127.0.0.1:9222` | DevTools HTTP endpoint |
|
|
13
|
+
| `SMOOTH_OPERATOR_BROWSER_WS_ENDPOINT` | unset | DevTools WebSocket endpoint |
|
|
14
|
+
| `SMOOTH_OPERATOR_BROWSER_EXECUTABLE` | unset | Chromium binary (required for `launch`) |
|
|
15
|
+
| `SMOOTH_OPERATOR_BROWSER_USER_DATA_DIR` | `${DATA_DIR}/browser` | Persistent profile |
|
|
16
|
+
| `SMOOTH_OPERATOR_BROWSER_HEADLESS` | `false` | Headless Chrome |
|
|
17
|
+
| `SMOOTH_OPERATOR_BROWSER_VIEWPORT_WIDTH` / `_HEIGHT` | unset | Set both for an explicit viewport |
|
|
18
|
+
| `SMOOTH_OPERATOR_BROWSER_AUTO_LAUNCH` | `false` | Legacy connect-mode recovery launch |
|
|
19
|
+
| `SMOOTH_OPERATOR_BROWSER_TIMEOUT_MS` | `15000` | Action timeout |
|
|
20
|
+
| `SMOOTH_OPERATOR_BROWSER_CONNECT_TIMEOUT_MS` | `30000` | Connect timeout |
|
|
21
|
+
| `SMOOTH_OPERATOR_BROWSER_CDP_TIMEOUT_MS` | `30000` | CDP timeout |
|
|
22
|
+
| `SMOOTH_OPERATOR_BROWSER_IDLE_TIMEOUT_MS` | `0` | Idle cleanup; `0` disabled, max 24 hours |
|
|
23
|
+
| `SMOOTH_OPERATOR_MAX_SCREENSHOT_BYTES` | `8000000` | Screenshot cap |
|
|
24
|
+
| `SMOOTH_OPERATOR_MAX_HTML_CHARS` | `200000` | HTML cap |
|
|
25
|
+
| `SMOOTH_OPERATOR_ALLOWED_DOMAINS` | unset | Hostname allowlist (`*.` suffix ok) |
|
|
26
|
+
| `SMOOTH_OPERATOR_BLOCKED_DOMAINS` | unset | Hostname denylist |
|
|
27
|
+
| `SMOOTH_OPERATOR_ALLOWED_FILE_ROOTS` | `files`,`downloads` | Upload/PDF roots |
|
|
28
|
+
| `SMOOTH_OPERATOR_ALLOW_PRIVATE_NETWORK` | `false` | Non-loopback private targets |
|
|
29
|
+
| `SMOOTH_OPERATOR_ALLOW_EVAL` | `true` | Page JavaScript |
|
|
30
|
+
| `SMOOTH_OPERATOR_STEALTH_ENABLED` | `true` | Compatibility label; identity stays native |
|
|
31
|
+
| `SMOOTH_OPERATOR_STEALTH_PROFILE` | `balanced` | `balanced` or `max`; no patch-set difference |
|
|
32
|
+
| `SMOOTH_OPERATOR_STEALTH_GPU` | `false` | Opt-in GPU flags |
|
|
33
|
+
| `SMOOTH_OPERATOR_BEHAVIOR_ENABLED` | `false` | Opt-in timing wrappers |
|
|
34
|
+
| `SMOOTH_OPERATOR_HTTP_HOST` | `127.0.0.1` | HTTP bind host |
|
|
35
|
+
| `SMOOTH_OPERATOR_HTTP_PORT` | `3344` | HTTP bind port |
|
|
36
|
+
| `SMOOTH_OPERATOR_HTTP_PATH` | `/mcp` | MCP path; health is `<path>/healthz` |
|
|
37
|
+
| `SMOOTH_OPERATOR_HTTP_TOKEN` | unset | Bearer token |
|
|
38
|
+
| `SMOOTH_OPERATOR_ALLOW_REMOTE_HTTP` | `false` | Non-loopback HTTP |
|
|
39
|
+
| `SMOOTH_OPERATOR_HTTP_MAX_BODY_BYTES` | `2000000` | Body cap |
|
|
40
|
+
| `SMOOTH_OPERATOR_ALLOWED_HOSTS` / `_ORIGINS` | localhost, loopback | HTTP allowlists |
|
|
41
|
+
| `SMOOTH_OPERATOR_LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` |
|
|
42
|
+
|
|
43
|
+
Removed (fail closed if set): `SMOOTH_OPERATOR_BROWSER_PROFILE`,
|
|
44
|
+
`SMOOTH_OPERATOR_BROWSER_STEALTH`, `SMOOTH_OPERATOR_DEFAULT_MODE`,
|
|
45
|
+
`SMOOTH_OPERATOR_BROWSER_USER_AGENT`.
|
|
46
|
+
|
|
47
|
+
CLI: `smooth-operator [server] [--transport stdio|http] [--config path] [--host host] [--port port]`,
|
|
48
|
+
`smooth-operator install [harness] --yes`, `smooth-operator doctor`.
|
|
49
|
+
|
|
50
|
+
The wizard asks exactly three questions: browser profile ownership, browser
|
|
51
|
+
display, and the Chromium executable. `--yes` uses managed, headed, eval on,
|
|
52
|
+
native-identity compatibility, and deterministic input.
|
|
53
|
+
|
|
54
|
+
See `.env.example` for a copy-paste template.
|
package/docs/harnesses.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Interactive wizard (directly to your harness)
|
|
4
4
|
|
|
5
|
-
`smooth-operator install <harness>` is interactive by default — it asks exactly
|
|
5
|
+
`smooth-operator install <harness>` is interactive by default — it asks exactly three
|
|
6
6
|
curated questions (profile ownership, headed/headless display, and browser
|
|
7
7
|
executable) with recommended defaults in brackets.
|
|
8
8
|
Omitting `<harness>` is allowed too: on a TTY the installer prompts for the
|
|
@@ -21,7 +21,9 @@ When you pick “connected browser” (mode `connect`), the wizard finds Chromiu
|
|
|
21
21
|
`discovery.ts`, launches a dedicated debugging profile under
|
|
22
22
|
`~/.smooth-operator/personal-chrome` with port `9222`, and probes the loopback
|
|
23
23
|
endpoint until a valid DevTools version response is live (33 attempts within a
|
|
24
|
-
default 10-second deadline).
|
|
24
|
+
default 10-second deadline). If launch or readiness fails, the helper terminates
|
|
25
|
+
the child process on a best-effort basis instead of leaving it detached. On
|
|
26
|
+
success it writes
|
|
25
27
|
`SMOOTH_OPERATOR_BROWSER_MODE=connect` and
|
|
26
28
|
`SMOOTH_OPERATOR_BROWSER_URL=http://127.0.0.1:9222` for you. This does not
|
|
27
29
|
attach to or take ownership of an operator's daily browser profile. No manual
|
|
@@ -56,7 +58,7 @@ npm install -g .
|
|
|
56
58
|
smooth-operator install --help
|
|
57
59
|
```
|
|
58
60
|
|
|
59
|
-
After installation, restart the harness and confirm the
|
|
61
|
+
After installation, restart the harness and confirm the 57-tool registry is
|
|
60
62
|
visible. `server_health` reports the active capabilities; `browser_doctor`
|
|
61
63
|
reports whether the configured or discovered executable is launch-ready and
|
|
62
64
|
whether a managed DevTools endpoint is live. Optional idle cleanup is disabled
|
|
@@ -159,7 +161,8 @@ legacy shape, `enabled` defaults to `true`; `enabled: false` is likewise an
|
|
|
159
161
|
explicit conflict. If it has malformed `mcp` or malformed `mcp.servers`,
|
|
160
162
|
installation fails closed rather than replacing user data.
|
|
161
163
|
Comments and trailing commas are accepted as JSONC; a successful update writes
|
|
162
|
-
normalized JSON
|
|
164
|
+
normalized JSON through an owner-only temporary file, flushes it before atomic
|
|
165
|
+
replacement, and creates a unique owner-only backup first.
|
|
163
166
|
|
|
164
167
|
After editing, run `opencode mcp list` or restart OpenCode. OpenCode also
|
|
165
168
|
supports adding the server interactively with `opencode mcp add`; that is the
|
|
@@ -239,7 +242,8 @@ Official reference: <https://code.visualstudio.com/docs/copilot/chat/mcp-servers
|
|
|
239
242
|
## Cursor, Windsurf, and Claude Desktop
|
|
240
243
|
|
|
241
244
|
These clients use JSON configuration files, so the installer performs a
|
|
242
|
-
careful merge and writes
|
|
245
|
+
careful merge and writes through a flushed owner-only temporary file before
|
|
246
|
+
atomic replacement.
|
|
243
247
|
|
|
244
248
|
| Target | Default path |
|
|
245
249
|
| --- | --- |
|
package/docs/mcp-server.md
CHANGED
|
@@ -25,11 +25,15 @@ MCP client
|
|
|
25
25
|
└─ Logger and safe error boundary
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
`src/server/main.ts` owns
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`src/server/
|
|
28
|
+
`src/server/main.ts` owns CLI dispatch (`server`, `install`, `doctor`).
|
|
29
|
+
`src/server/http.ts` owns Streamable HTTP, `healthz`, and shutdown bounds.
|
|
30
|
+
`src/server/catalog.ts` is the locked public surface; `src/server/mcp.ts`
|
|
31
|
+
registers it. `src/server/envelope.ts` serializes one result envelope.
|
|
32
|
+
`src/server/runtime.ts` owns dependency lifecycle and the profile lease.
|
|
33
|
+
Browser operations are in `src/server/browser/service.ts` with the exclusive
|
|
34
|
+
queue in `src/server/browser/queue.ts`. Policy and configuration are in
|
|
35
|
+
`src/server/policy.ts` and `src/server/config.ts`. See
|
|
36
|
+
[architecture.md](architecture.md) for the module table.
|
|
33
37
|
|
|
34
38
|
## Install and start
|
|
35
39
|
|
|
@@ -43,15 +47,14 @@ smooth-operator --help
|
|
|
43
47
|
|
|
44
48
|
(The registry package is `smooth-operator-mcp`; plain `smooth-operator` is an unrelated library. You can also install straight from GitHub: `npm install -g github:Gitshop77/Smooth-Operator`.)
|
|
45
49
|
|
|
46
|
-
The interactive installer asks exactly three questions:
|
|
47
|
-
ownership,
|
|
48
|
-
|
|
50
|
+
The interactive installer asks exactly three questions: browser profile
|
|
51
|
+
ownership, headed or headless display, and which Chromium executable to use.
|
|
52
|
+
Its recommended defaults are a managed private persistent profile, headed
|
|
49
53
|
display, and the first detected Chromium executable. It also enables page eval
|
|
50
54
|
and the identity-preserving compatibility profile; behavioral timing is off
|
|
51
55
|
for fast deterministic input and can be enabled explicitly. Managed mode owns
|
|
52
|
-
its profile. Connected mode launches and attaches to
|
|
53
|
-
|
|
54
|
-
daily browser.
|
|
56
|
+
its profile. Connected mode launches and attaches to a dedicated debugging
|
|
57
|
+
profile and does not claim ownership of an operator's daily browser.
|
|
55
58
|
|
|
56
59
|
From a checkout:
|
|
57
60
|
|
|
@@ -132,7 +135,10 @@ and Origin validation and include `Authorization: Bearer <token>`. The token
|
|
|
132
135
|
is compared in constant time. Request bodies are bounded to 2,000,000 bytes by
|
|
133
136
|
default and concurrent requests are capped. The process drains in-flight work
|
|
134
137
|
for a short bounded period on SIGINT/SIGTERM, then closes the MCP handler,
|
|
135
|
-
browser, and HTTP server.
|
|
138
|
+
browser, and HTTP server. Native JSON rejections use no-store and nosniff
|
|
139
|
+
headers; overload responses also include `Retry-After: 1`. Preflight responses
|
|
140
|
+
advertise a fixed request-header allowlist rather than reflecting arbitrary
|
|
141
|
+
requested names.
|
|
136
142
|
|
|
137
143
|
Remote binding is deliberately guarded:
|
|
138
144
|
|
|
@@ -163,7 +169,8 @@ launches it with `${SMOOTH_OPERATOR_DATA_DIR}/browser` as a non-default profile,
|
|
|
163
169
|
and records its loopback DevTools endpoint for later reattachment. Sign in once
|
|
164
170
|
in the visible window; its sessions persist in that private profile. The
|
|
165
171
|
`browser_doctor` tool reports executable resolution and endpoint state without
|
|
166
|
-
evaluating page content.
|
|
172
|
+
evaluating page content. Failed personal-Chrome helper launches are terminated
|
|
173
|
+
on a best-effort basis.
|
|
167
174
|
|
|
168
175
|
The managed browser is headed by default for sign-in and human handoff. On CI or a
|
|
169
176
|
displayless host, explicitly set `SMOOTH_OPERATOR_BROWSER_HEADLESS=true` or use
|
|
@@ -323,8 +330,8 @@ variables include:
|
|
|
323
330
|
| `SMOOTH_OPERATOR_ALLOWED_FILE_ROOTS` | data `files`, `downloads` | Explicit roots replace defaults |
|
|
324
331
|
| `SMOOTH_OPERATOR_ALLOW_PRIVATE_NETWORK` | `false` | Allows non-loopback private targets when true |
|
|
325
332
|
| `SMOOTH_OPERATOR_ALLOW_EVAL` | `true` | Set `false` to disable page JavaScript |
|
|
326
|
-
| `SMOOTH_OPERATOR_STEALTH_ENABLED` | `true` |
|
|
327
|
-
| `SMOOTH_OPERATOR_STEALTH_PROFILE` | `balanced` | `balanced` or `max
|
|
333
|
+
| `SMOOTH_OPERATOR_STEALTH_ENABLED` | `true` | Compatibility label; identity stays native |
|
|
334
|
+
| `SMOOTH_OPERATOR_STEALTH_PROFILE` | `balanced` | `balanced` or `max`; no patch-set difference |
|
|
328
335
|
| `SMOOTH_OPERATOR_STEALTH_GPU` | `false` | Adds opt-in GPU launch flags |
|
|
329
336
|
| `SMOOTH_OPERATOR_BEHAVIOR_ENABLED` | `false` | Opt-in timing wrappers |
|
|
330
337
|
| `SMOOTH_OPERATOR_HTTP_HOST` | `127.0.0.1` | HTTP bind host |
|
|
@@ -356,8 +363,9 @@ loop is introduced.
|
|
|
356
363
|
Raw MCP/tool-call speed is not the main bot-detection vector. Sites can score
|
|
357
364
|
network and browser identity, IP reputation, session history, and interaction
|
|
358
365
|
timing independently. A faster call does not bypass a challenge or make an
|
|
359
|
-
automated session legitimate
|
|
360
|
-
only where the target
|
|
366
|
+
automated session legitimate. The connected harness loops
|
|
367
|
+
`browser_solve_challenge` / `browser_wait_for_human` only where the target
|
|
368
|
+
permits automation.
|
|
361
369
|
|
|
362
370
|
## Security enforcement layers
|
|
363
371
|
|
|
@@ -392,9 +400,10 @@ are always applied, with explicit opt-ins where documented:
|
|
|
392
400
|
- Page text, HTML, titles, attributes, search snippets, cookies, and logs are
|
|
393
401
|
treated as untrusted data, normalized, bounded, and redacted before output.
|
|
394
402
|
- Challenge and anti-bot markers are reported from bounded evidence. The server
|
|
395
|
-
does not rotate identities or silently bypass challenges. The connected
|
|
396
|
-
|
|
397
|
-
|
|
403
|
+
does not rotate identities or silently bypass challenges. The connected harness
|
|
404
|
+
loops `browser_solve_challenge` / `browser_wait_for_human`: each solve call
|
|
405
|
+
collects fresh classification and visual/state evidence, allows ordinary
|
|
406
|
+
browser actions, and verifies with a subsequent call.
|
|
398
407
|
|
|
399
408
|
Run the server with a dedicated browser profile and the smallest domain and
|
|
400
409
|
file-root allowlists that fit the task. Browser automation can still perform
|
|
@@ -405,32 +414,37 @@ for confirming destructive calls.
|
|
|
405
414
|
|
|
406
415
|
### Tools
|
|
407
416
|
|
|
408
|
-
The registry exposes
|
|
409
|
-
individual descriptions and limits are returned by `tools/list`.
|
|
417
|
+
The registry exposes 57 public tools in these groups. Every input is schema-validated;
|
|
418
|
+
individual descriptions and limits are returned by `tools/list`. Each listed
|
|
419
|
+
name does one job. Cookies: `get`/`set`/`delete`. Storage and resource-blocking:
|
|
420
|
+
`get`/`set`/`clear`. Network and console logs:
|
|
421
|
+
`enable`/`disable`/`read`/`clear`/`read_and_clear`. Dialogs:
|
|
422
|
+
`get_text`/`accept`/`dismiss`/`send_keys`.
|
|
410
423
|
|
|
411
424
|
**Observation and extraction:** `browser_snapshot`, `browser_tabs`,
|
|
412
|
-
`
|
|
425
|
+
`browser_list_sessions`,
|
|
413
426
|
`browser_page_info`, `browser_interactive`, `browser_frames`,
|
|
414
|
-
`browser_accessibility_snapshot`, `browser_extract`,
|
|
427
|
+
`browser_accessibility_snapshot`, `browser_extract`,
|
|
415
428
|
`browser_find_text`, `browser_search_page`, `browser_find_elements`,
|
|
416
429
|
`browser_dropdown_options`, `browser_computed_style`, `browser_page_next`,
|
|
417
430
|
`browser_get_html`, `browser_search_network_log`, `browser_inspect_element`,
|
|
418
431
|
`browser_challenge`, `browser_doctor`, and `server_health`.
|
|
419
432
|
|
|
420
433
|
**Navigation and interaction:** `browser_navigate`, `browser_back`,
|
|
421
|
-
`
|
|
434
|
+
`browser_forward`, `browser_reload`, `browser_switch_tab`,
|
|
422
435
|
`browser_close_tab`, `browser_click`, `browser_input`, `browser_select`,
|
|
423
436
|
`browser_scroll`, `browser_scroll_to_bottom`, `browser_key`,
|
|
424
|
-
`browser_wait`, `browser_wait_for_element`, `browser_wait_for_text`,
|
|
425
|
-
`
|
|
426
|
-
`
|
|
437
|
+
`browser_wait`, `browser_wait_for_element`, `browser_wait_for_text`,
|
|
438
|
+
`browser_wait_for_url`, `browser_wait_for_network_idle`,
|
|
439
|
+
`browser_hover`, `browser_move`, `browser_press_and_hold`,
|
|
440
|
+
and `browser_close`.
|
|
427
441
|
|
|
428
442
|
**Available local capabilities:** `browser_screenshot`, `browser_pdf`,
|
|
429
443
|
`browser_upload`, `browser_downloads`, `browser_network_log`,
|
|
430
|
-
`
|
|
444
|
+
`browser_console_log`, `browser_resource_blocking`,
|
|
431
445
|
`browser_dialog`, `browser_cookies`, `browser_storage`,
|
|
432
|
-
`browser_batch`, `
|
|
433
|
-
|
|
446
|
+
`browser_batch`, `browser_solve_challenge`, `browser_wait_for_human`,
|
|
447
|
+
and all other browser tools are available by default.
|
|
434
448
|
`browser_close_session` remains a local lifecycle control and does not change
|
|
435
449
|
browser permissions. Page evaluation is available by default and can be
|
|
436
450
|
disabled explicitly with `SMOOTH_OPERATOR_ALLOW_EVAL=false`.
|
|
@@ -445,17 +459,20 @@ acquire a profile lease; the next browser operation retries acquisition.
|
|
|
445
459
|
`ready` reports readiness based on the runtime's current ownership state.
|
|
446
460
|
|
|
447
461
|
`browser_evaluate` is page JavaScript and is available by default (set
|
|
448
|
-
`SMOOTH_OPERATOR_ALLOW_EVAL=false` when it is not wanted). `
|
|
449
|
-
accepts only a
|
|
462
|
+
`SMOOTH_OPERATOR_ALLOW_EVAL=false` when it is not wanted). `browser_batch`
|
|
463
|
+
accepts only a validated array of browser actions; it is not a shell,
|
|
450
464
|
Python, or arbitrary code runner. Its explicit `evaluate` action still follows
|
|
451
|
-
the page-evaluation policy
|
|
452
|
-
|
|
465
|
+
the page-evaluation policy, and its optional `timeoutMs` is the whole-batch
|
|
466
|
+
deadline (120,000 ms default, 600,000 ms maximum).
|
|
467
|
+
Destructive batch actions require explicit confirmation. There is no generic
|
|
468
|
+
arbitrary CDP command or host-code execution
|
|
453
469
|
tool. `browser_wait_for_human` pauses for an operator to complete a
|
|
454
|
-
visible sign-in or challenge.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
challenge absent; present and exhausted
|
|
458
|
-
budget and `automation_exhausted` is
|
|
470
|
+
visible sign-in or challenge. The connected harness loops
|
|
471
|
+
`browser_solve_challenge` (one evidence cycle per call) and, after
|
|
472
|
+
exhaustion, `browser_wait_for_human`. Success requires a fresh final
|
|
473
|
+
classification that reports the challenge absent; present and exhausted
|
|
474
|
+
cycles report the remaining attempt budget and `automation_exhausted` is
|
|
475
|
+
the final non-success state.
|
|
459
476
|
`browser_close_session`
|
|
460
477
|
closes the one native browser session by its explicit session identifier.
|
|
461
478
|
|
|
@@ -467,6 +484,21 @@ revision. A snapshot failure is reported as `snapshot: null` with a bounded
|
|
|
467
484
|
accepts the same option at the top level and captures only one snapshot after
|
|
468
485
|
the final action.
|
|
469
486
|
|
|
487
|
+
Element-targeting tools accept exactly one of `target`, `ref` (`e5` or
|
|
488
|
+
`ref:e5`), CSS `selector`, or zero-based `index`. They do not take
|
|
489
|
+
`operation`. `browser_click`, `browser_move`, and `browser_press_and_hold`
|
|
490
|
+
also accept `coordinateX`/`coordinateY`; hold also accepts start/end
|
|
491
|
+
coordinates or a path. `browser_challenge` is detect-only; solve is
|
|
492
|
+
`browser_solve_challenge`.
|
|
493
|
+
`browser_select` additionally requires exactly one of `optionValue` or
|
|
494
|
+
`optionValues`; `browser_upload` requires exactly one target form and exactly
|
|
495
|
+
one of `filePath` or `filePaths`. Snapshot refs and indexes are invalidated by
|
|
496
|
+
navigation and DOM-changing actions, so refresh the snapshot before reuse.
|
|
497
|
+
Prefer canonical tools (`browser_tabs`, `browser_snapshot`, `browser_input`,
|
|
498
|
+
`browser_back`, `browser_close`, `browser_extract`). Compatibility aliases
|
|
499
|
+
and retired `browser_logs` are unlisted catalog maps; `tools/list` is exactly
|
|
500
|
+
the 57 names.
|
|
501
|
+
|
|
470
502
|
The current-page observation tools `browser_interactive`, `browser_frames`,
|
|
471
503
|
`browser_page_info`, and `browser_challenge` accept an optional `pageId`; omit
|
|
472
504
|
it to use the active tab. `browser_accessibility_snapshot` also accepts an
|
|
@@ -498,12 +530,20 @@ that the selected action cannot consume is rejected instead of being silently
|
|
|
498
530
|
ignored.
|
|
499
531
|
|
|
500
532
|
Each batch step enforces its own `timeoutMs` or the configured action deadline.
|
|
501
|
-
The
|
|
533
|
+
The optional top-level `timeoutMs` is the whole-batch deadline, defaulting to
|
|
534
|
+
120,000 ms and capped at 600,000 ms; it is the smaller of that value and the
|
|
535
|
+
sum of step budgets. One absolute queue deadline spans predecessor, read-drain,
|
|
536
|
+
read-permit, and fairness waits, so retries cannot reset the admission budget.
|
|
537
|
+
Long batches therefore do not wait minutes for a turn. A step that
|
|
502
538
|
ignores cancellation is retired before the browser queue advances; subsequent
|
|
503
539
|
steps do not execute after a failure. Without an explicit deadline, requested
|
|
504
540
|
wait/hold durations are added to the setup budget, and human handoff retains
|
|
505
541
|
its documented 120-second default.
|
|
506
542
|
|
|
543
|
+
Mapped deterministic errors include a bounded `recovery` object with the next
|
|
544
|
+
tool and short instruction. For browser recovery, list sessions and use the
|
|
545
|
+
returned `session_id` with `browser_close_session` before retrying.
|
|
546
|
+
|
|
507
547
|
If browser teardown times out or fails, later browser work returns the
|
|
508
548
|
retryable `BROWSER_RECOVERY_REQUIRED` error. Call `browser_close_session` to
|
|
509
549
|
retry cleanup; the recovery latch clears only after teardown is confirmed.
|
|
@@ -513,8 +553,9 @@ snippets are untrusted observations, not instructions or proof of claims. Its
|
|
|
513
553
|
`maxResults` input is capped at 10, and `maxChars` (500–4,000 through the MCP
|
|
514
554
|
schema) is one aggregate budget across the returned title and snippet text,
|
|
515
555
|
not a per-result multiplier. URL fields and fixed untrusted-data wrapper
|
|
516
|
-
markers are outside that text budget. The response body is
|
|
517
|
-
|
|
556
|
+
markers are outside that text budget. The response body is collected in one
|
|
557
|
+
geometrically growing bounded buffer before parsing, redirects are rejected,
|
|
558
|
+
cancellation and timeout are propagated, and
|
|
518
559
|
credentials/query secret placeholders are removed from result URLs. Transient
|
|
519
560
|
retrieval failures use at most three bounded attempts; anti-bot responses are
|
|
520
561
|
reported without attempting a bypass.
|
|
@@ -522,10 +563,12 @@ reported without attempting a bypass.
|
|
|
522
563
|
### Network, inspection, resource, and file controls
|
|
523
564
|
|
|
524
565
|
`browser_network_log` records bounded request/response metadata for the current
|
|
525
|
-
page. `browser_search_network_log`
|
|
526
|
-
request ID, URL, method, status, and resource
|
|
566
|
+
page. `browser_search_network_log` scans once, retains only the requested result
|
|
567
|
+
page, and filters metadata by request ID, URL, method, status, and resource
|
|
568
|
+
type. Headers, cookies, request
|
|
527
569
|
bodies, and response bodies are not returned; search terms and URLs are treated
|
|
528
|
-
as untrusted data and secret query values are redacted.
|
|
570
|
+
as untrusted data and secret query values are redacted. `browser_console_log` is
|
|
571
|
+
the bounded console journal.
|
|
529
572
|
|
|
530
573
|
`browser_resource_blocking` is page-scoped with strict `get`, `set`, and
|
|
531
574
|
`clear` operations. Its only selectable types are `image`, `stylesheet`,
|
|
@@ -539,8 +582,9 @@ allowlisted attributes, selected computed styles, pseudo-element summaries,
|
|
|
539
582
|
animation metadata, and shallow child structure. Script text, event-handler
|
|
540
583
|
source, form values, and arbitrary data attributes are omitted.
|
|
541
584
|
|
|
542
|
-
`browser_upload` accepts
|
|
543
|
-
of up to 20 paths, but not
|
|
585
|
+
`browser_upload` accepts `target`, `ref`, `selector`, or `index`, plus the
|
|
586
|
+
existing single `filePath` or a `filePaths` array of up to 20 paths, but not
|
|
587
|
+
both. Every source must pass the allowed-root,
|
|
544
588
|
no-follow, and file-identity checks. Each file is at most 50 MiB and the
|
|
545
589
|
aggregate is at most 100 MiB; multiple paths require a target input with the
|
|
546
590
|
`multiple` attribute. Sources are staged sequentially and every staging path is
|
|
@@ -567,7 +611,7 @@ Resource output is bounded and follows the same redaction and policy rules as
|
|
|
567
611
|
tool output. The capabilities resource also reports the native defaults and
|
|
568
612
|
effective feature flags for local browser tools, page evaluation, stealth, and
|
|
569
613
|
behavioral timing. Its `limits` metadata publishes the page-text, action-plan,
|
|
570
|
-
research, and upload budgets used by the public boundary. Its challenge
|
|
614
|
+
batch-timeout, research, and upload budgets used by the public boundary. Its challenge
|
|
571
615
|
metadata includes the default and maximum connected-AI attempt budgets and
|
|
572
616
|
states that success requires an explicit absent classification.
|
|
573
617
|
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Fail closed. Bound and redact untrusted page, search, and log data. Dual-enforce
|
|
4
|
+
the same `SecurityPolicy` at the MCP edge and again at the browser/research
|
|
5
|
+
service.
|
|
6
|
+
|
|
7
|
+
## Network
|
|
8
|
+
|
|
9
|
+
- HTTP(S) only. Credentials in URLs are rejected. Untrusted URL credentials are redacted in output.
|
|
10
|
+
- Redirects are re-checked with the same policy and fail closed. Research fetch uses `redirect: "error"`.
|
|
11
|
+
- Private, link-local, and multicast addresses are blocked unless `SMOOTH_OPERATOR_ALLOW_PRIVATE_NETWORK=true`. Loopback is allowed.
|
|
12
|
+
- DNS is a best-effort preflight (deny-only cache). The browser resolver is not pinned.
|
|
13
|
+
- Resource blocking is page-scoped to image, stylesheet, font, media, and script. Document/navigation cannot be selected.
|
|
14
|
+
|
|
15
|
+
## Files
|
|
16
|
+
|
|
17
|
+
- Allowed roots are canonicalized once. Filesystem-root roots and regular-file roots are rejected.
|
|
18
|
+
- Unresolved symlink escapes fail closed. Blocked-path errors include only configured-root metadata.
|
|
19
|
+
- Uploads: at most 20 files, 50 MiB each, 100 MiB aggregate; every staging path is cleaned on all outcomes. Multiple files require `multiple` on the input.
|
|
20
|
+
- Config files and backups are bounded, owner-only (`chmod 600`), regular, and symlink-safe. Writes use a temp file, flush, then atomic rename.
|
|
21
|
+
|
|
22
|
+
## Page data
|
|
23
|
+
|
|
24
|
+
Returned HTML, text, accessibility, inspect, cookies, and storage omit:
|
|
25
|
+
|
|
26
|
+
- scripts and event-handler attributes
|
|
27
|
+
- form values (including accessibility descendants)
|
|
28
|
+
- cookie values on read
|
|
29
|
+
- secret placeholders, bearer-shaped strings, and secret query keys
|
|
30
|
+
|
|
31
|
+
Truncation is explicit (`truncated`, `mcpOutputTruncated`, `*Truncated`, omission counts). Silent omission is a bug.
|
|
32
|
+
|
|
33
|
+
Page evaluation is on by default and gated by `SecurityPolicy.assertEvalAllowed()` at the MCP edge and again in `BrowserService`. There is no generic CDP or host-code tool.
|
|
34
|
+
|
|
35
|
+
## HTTP
|
|
36
|
+
|
|
37
|
+
- Loopback bind unless remote HTTP is explicitly enabled.
|
|
38
|
+
- Remote HTTP requires a token of at least 32 printable ASCII characters.
|
|
39
|
+
- Host and Origin are parsed and allowlisted. Bearer comparison is constant-time.
|
|
40
|
+
- `healthz` uses the same Host/Origin/bearer policy as `/mcp`.
|
|
41
|
+
- Bodies are capped (default 2 MiB). Malformed or partial responses close the connection.
|
|
42
|
+
|
|
43
|
+
## Browser process
|
|
44
|
+
|
|
45
|
+
- One profile lease (`.smooth-operator-profile.lock`). Concurrent sessions fail with `BROWSER_PROFILE_IN_USE`.
|
|
46
|
+
- Managed and launch require a regular executable, a bounded 64 KiB `/json/version` probe, and target auto-attach acknowledgement.
|
|
47
|
+
- Connect mode attaches to the configured `browserURL` / `wsEndpoint`. The wizard Personal Chrome helper uses `~/.smooth-operator/personal-chrome` and does not take over daily Chrome. Advanced `chrome://inspect` attach to a daily profile is opt-in; see [mcp-server.md](mcp-server.md).
|
|
48
|
+
- After an uncooperative timeout the old lifecycle is retired before the queue advances. A late close cannot release another request's lock.
|
|
49
|
+
|
|
50
|
+
## Challenges and research
|
|
51
|
+
|
|
52
|
+
Challenges are classified from independently bounded title, text, HTML, frame, and visible-marker evidence. The connected harness loops `browser_solve_challenge` / `browser_wait_for_human`. Success requires a fresh classification that is explicitly **absent**. There is no solver, token injection, or anti-bot bypass. Identity stays native: launch args do not fabricate UA, platform, WebGL, canvas, client hints, or `navigator.webdriver`.
|
|
53
|
+
|
|
54
|
+
`web_search` is bounded DuckDuckGo HTML. Anti-bot responses are `SEARCH_BLOCKED`. The server does not complete challenges for the search provider.
|
|
55
|
+
|
|
56
|
+
## Logs
|
|
57
|
+
|
|
58
|
+
JSON lines on stderr. Secret keys and values are redacted. Logging failures never change protocol responses.
|