smooth-operator-mcp 2.1.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/.env.example +45 -0
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/smooth-operator.mjs +8128 -0
- package/dist/smooth-operator.mjs.map +6 -0
- package/docs/harnesses.md +272 -0
- package/docs/mcp-server.md +427 -0
- package/package.json +69 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# SmoothOperator MCP server
|
|
2
|
+
|
|
3
|
+
This document is the operational reference for the standalone Node.js server.
|
|
4
|
+
It assumes that an MCP client is already installed and can launch a local
|
|
5
|
+
stdio process or connect to a Streamable HTTP endpoint.
|
|
6
|
+
|
|
7
|
+
## What the server owns
|
|
8
|
+
|
|
9
|
+
SmoothOperator is a protocol server, not an autonomous application. The client
|
|
10
|
+
decides when to call a tool and how to reason about its result. The server
|
|
11
|
+
validates each request, applies the same policy again at the browser and file
|
|
12
|
+
boundaries, performs the requested operation, and returns bounded MCP content.
|
|
13
|
+
It has no model credentials, model selection, or hidden planning cycle.
|
|
14
|
+
|
|
15
|
+
At runtime the composition is:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
MCP client
|
|
19
|
+
├─ stdio transport, or Streamable HTTP transport
|
|
20
|
+
└─ MCP registry (tools, resources, prompts)
|
|
21
|
+
└─ ServerRuntime
|
|
22
|
+
├─ SecurityPolicy (URL, DNS, file, and capability checks)
|
|
23
|
+
├─ BrowserService (Puppeteer over Chrome DevTools Protocol)
|
|
24
|
+
├─ ResearchService (bounded DuckDuckGo retrieval)
|
|
25
|
+
└─ Logger and safe error boundary
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`src/server/main.ts` owns transport startup, authentication, signal handling,
|
|
29
|
+
and graceful shutdown. `src/server/mcp.ts` registers the public protocol
|
|
30
|
+
surface. `src/server/runtime.ts` owns dependency lifecycle. Browser operations
|
|
31
|
+
are in `src/server/browser/service.ts`; policy and configuration are in
|
|
32
|
+
`src/server/policy.ts` and `src/server/config.ts`.
|
|
33
|
+
|
|
34
|
+
## Install and start
|
|
35
|
+
|
|
36
|
+
Use Node.js 22.23.2 and npm 10.9.8 for the reproducible project baseline. A
|
|
37
|
+
published package includes the built executable:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm install -g smooth-operator-mcp
|
|
41
|
+
smooth-operator --help
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
(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
|
+
|
|
46
|
+
From a checkout:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
npm ci
|
|
50
|
+
npm run build
|
|
51
|
+
node dist/smooth-operator.mjs --help
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The default transport is stdio. `npm start` runs the TypeScript source through
|
|
55
|
+
`tsx`; a published install runs `dist/smooth-operator.mjs` through its npm bin.
|
|
56
|
+
The process writes protocol messages to stdout and structured diagnostics to
|
|
57
|
+
stderr. Do not redirect ordinary logs into stdout while using stdio.
|
|
58
|
+
|
|
59
|
+
## Stdio transport
|
|
60
|
+
|
|
61
|
+
Stdio is the preferred local integration because no listening socket is
|
|
62
|
+
created. An MCP client launches the executable and speaks JSON-RPC over its
|
|
63
|
+
stdin/stdout pipes. A generic server entry looks like this:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"SmoothOperator": {
|
|
69
|
+
"command": "smooth-operator",
|
|
70
|
+
"args": []
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If the client starts with a restricted `PATH`, use an absolute Node executable
|
|
77
|
+
and absolute bundled entrypoint instead:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"SmoothOperator": {
|
|
83
|
+
"command": "/absolute/path/to/node",
|
|
84
|
+
"args": ["/absolute/path/to/dist/smooth-operator.mjs"]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The built-in installer uses this absolute form for GUI configuration files
|
|
91
|
+
when it is running from the published bundle. Command-line harnesses retain
|
|
92
|
+
their native CLI command and the portable `smooth-operator` name. See
|
|
93
|
+
[harnesses.md](harnesses.md) for each client.
|
|
94
|
+
|
|
95
|
+
Useful command-line forms:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
smooth-operator --version
|
|
99
|
+
smooth-operator --help
|
|
100
|
+
smooth-operator --transport stdio
|
|
101
|
+
smooth-operator --transport stdio --config /absolute/path/config.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`--config`, `--transport`, `--host`, and `--port` accept one value each. An
|
|
105
|
+
unknown option or duplicate option fails closed before the runtime starts.
|
|
106
|
+
|
|
107
|
+
## Streamable HTTP transport
|
|
108
|
+
|
|
109
|
+
HTTP is opt-in and uses the MCP Streamable HTTP adapter. Keep it on loopback
|
|
110
|
+
for local clients:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
SMOOTH_OPERATOR_TRANSPORT=http \
|
|
114
|
+
SMOOTH_OPERATOR_HTTP_HOST=127.0.0.1 \
|
|
115
|
+
SMOOTH_OPERATOR_HTTP_PORT=3344 \
|
|
116
|
+
SMOOTH_OPERATOR_HTTP_TOKEN="$(openssl rand -hex 32)" \
|
|
117
|
+
smooth-operator
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The endpoint is `/mcp` by default. Every request must pass the configured Host
|
|
121
|
+
and Origin validation and include `Authorization: Bearer <token>`. The token
|
|
122
|
+
is compared in constant time. Request bodies are bounded to 2,000,000 bytes by
|
|
123
|
+
default and concurrent requests are capped. The process drains in-flight work
|
|
124
|
+
for a short bounded period on SIGINT/SIGTERM, then closes the MCP handler,
|
|
125
|
+
browser, and HTTP server.
|
|
126
|
+
|
|
127
|
+
Remote binding is deliberately guarded:
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
SMOOTH_OPERATOR_TRANSPORT=http \
|
|
131
|
+
SMOOTH_OPERATOR_HTTP_HOST=0.0.0.0 \
|
|
132
|
+
SMOOTH_OPERATOR_ALLOW_REMOTE_HTTP=true \
|
|
133
|
+
SMOOTH_OPERATOR_HTTP_TOKEN="$(openssl rand -hex 32)" \
|
|
134
|
+
SMOOTH_OPERATOR_ALLOWED_HOSTS=example.internal \
|
|
135
|
+
SMOOTH_OPERATOR_ALLOWED_ORIGINS=https://example.internal \
|
|
136
|
+
smooth-operator
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Remote mode is rejected unless the token is at least 32 characters. Do not
|
|
140
|
+
use a token from a shell history, checked-in file, or shared log. A reverse
|
|
141
|
+
proxy can add TLS and network access controls, but it does not replace the
|
|
142
|
+
application token, Host/Origin allowlists, or request-size limit.
|
|
143
|
+
|
|
144
|
+
## Browser lifecycle
|
|
145
|
+
|
|
146
|
+
The server manages one headed, persistent private agent-Chrome session by
|
|
147
|
+
default. On the first browser tool call it discovers an installed Google Chrome,
|
|
148
|
+
launches it with `${SMOOTH_OPERATOR_DATA_DIR}/browser` as a non-default profile,
|
|
149
|
+
and records its loopback DevTools endpoint for later reattachment. Sign in once
|
|
150
|
+
in the visible window; its sessions persist in that private profile. The
|
|
151
|
+
`browser_doctor` tool reports executable resolution and endpoint state without
|
|
152
|
+
evaluating page content.
|
|
153
|
+
|
|
154
|
+
Managed Chrome is headed by default for sign-in and human handoff. On CI or a
|
|
155
|
+
displayless host, explicitly set `SMOOTH_OPERATOR_BROWSER_HEADLESS=true` or use
|
|
156
|
+
Xvfb. The server never adds fingerprint spoofing, CAPTCHA solving, proxy
|
|
157
|
+
rotation, or other evasion behavior.
|
|
158
|
+
|
|
159
|
+
### Managed mode (default)
|
|
160
|
+
|
|
161
|
+
`SMOOTH_OPERATOR_BROWSER_MODE=managed` needs no browser setup in ordinary installs.
|
|
162
|
+
It checks its private `DevToolsActivePort` file, reattaches only after a bounded
|
|
163
|
+
loopback probe succeeds, and otherwise discovers Chrome then launches it. Set
|
|
164
|
+
`SMOOTH_OPERATOR_BROWSER_EXECUTABLE` only to override discovery. A second
|
|
165
|
+
SmoothOperator process using the same private profile is rejected by the profile lease.
|
|
166
|
+
|
|
167
|
+
`SMOOTH_OPERATOR_BROWSER_AUTO_LAUNCH` is retained for backward compatibility but is
|
|
168
|
+
ignored in managed mode.
|
|
169
|
+
|
|
170
|
+
Browser actions share a bounded operation queue and deadline. Browser startup
|
|
171
|
+
uses one in-flight connection promise, so concurrent callers wait for the same
|
|
172
|
+
reattach/launch attempt instead of starting duplicate processes. Newly
|
|
173
|
+
auto-attached top-level targets are held at the CDP boundary until the
|
|
174
|
+
navigation policy guard is installed; targets whose attachment ownership
|
|
175
|
+
cannot be determined are blocked or closed. These controls reduce races but do
|
|
176
|
+
not make a host browser or network firewall trustworthy by themselves.
|
|
177
|
+
|
|
178
|
+
Managed and launch modes create owner-only data, files, downloads, and browser
|
|
179
|
+
profile directories below `SMOOTH_OPERATOR_DATA_DIR` (unless an explicit profile
|
|
180
|
+
path is supplied), reject unsafe symlink components, and hold a profile lease
|
|
181
|
+
for the runtime lifetime. A second owner receives `BROWSER_PROFILE_IN_USE`; an
|
|
182
|
+
incomplete shutdown retains the lock for explicit operator recovery.
|
|
183
|
+
|
|
184
|
+
### Connect mode
|
|
185
|
+
|
|
186
|
+
Connect mode remains available for advanced, externally managed browser setups.
|
|
187
|
+
Chrome 144+ also offers an opt-in route through the remote-debugging toggle at
|
|
188
|
+
`chrome://inspect`; attach with connect mode and its reported endpoint only when
|
|
189
|
+
you intentionally want to control that daily profile.
|
|
190
|
+
|
|
191
|
+
For the classic route, start a dedicated browser profile with remote debugging
|
|
192
|
+
enabled, then point the server at the endpoint:
|
|
193
|
+
|
|
194
|
+
```sh
|
|
195
|
+
google-chrome \
|
|
196
|
+
--remote-debugging-port=9222 \
|
|
197
|
+
--user-data-dir="$HOME/.smooth-operator/browser-profile"
|
|
198
|
+
SMOOTH_OPERATOR_BROWSER_MODE=connect \
|
|
199
|
+
SMOOTH_OPERATOR_BROWSER_URL=http://127.0.0.1:9222 \
|
|
200
|
+
smooth-operator
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`SMOOTH_OPERATOR_BROWSER_WS_ENDPOINT` can be used instead when a WebSocket endpoint
|
|
204
|
+
is already available. A connection mode server does not own or close an
|
|
205
|
+
externally managed browser process.
|
|
206
|
+
|
|
207
|
+
### Launch mode
|
|
208
|
+
|
|
209
|
+
Launch mode gives the server ownership of a private browser process. Supply an
|
|
210
|
+
explicit executable and use an isolated profile:
|
|
211
|
+
|
|
212
|
+
```sh
|
|
213
|
+
SMOOTH_OPERATOR_BROWSER_MODE=launch \
|
|
214
|
+
SMOOTH_OPERATOR_BROWSER_EXECUTABLE=/path/to/chrome-for-testing/chrome \
|
|
215
|
+
SMOOTH_OPERATOR_BROWSER_USER_DATA_DIR="$HOME/.smooth-operator/browser-profile" \
|
|
216
|
+
smooth-operator
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The default profile is `${SMOOTH_OPERATOR_DATA_DIR}/browser`. Do not point it at a
|
|
220
|
+
personal profile containing passwords, cookies, or active sessions. Launch mode
|
|
221
|
+
preserves its explicit executable requirement. `SMOOTH_OPERATOR_BROWSER_AUTO_LAUNCH=true`
|
|
222
|
+
remains an explicit connect-mode recovery option and still requires an executable.
|
|
223
|
+
|
|
224
|
+
### Disabled mode
|
|
225
|
+
|
|
226
|
+
`SMOOTH_OPERATOR_BROWSER_MODE=disabled` keeps the MCP process available for health,
|
|
227
|
+
search, protocol, and configuration checks without opening a browser. Browser
|
|
228
|
+
tools return a bounded disabled error until the process is restarted with a
|
|
229
|
+
browser mode.
|
|
230
|
+
|
|
231
|
+
## Configuration and precedence
|
|
232
|
+
|
|
233
|
+
Configuration can come from a JSON file selected by `--config` or
|
|
234
|
+
`SMOOTH_OPERATOR_CONFIG`, environment variables, and a small set of command-line
|
|
235
|
+
flags. The effective precedence is:
|
|
236
|
+
|
|
237
|
+
1. command-line values (`--config`, `--transport`, `--host`, `--port`);
|
|
238
|
+
2. environment variables;
|
|
239
|
+
3. values from the JSON file;
|
|
240
|
+
4. documented defaults.
|
|
241
|
+
|
|
242
|
+
The file is an object with nested `http`, `browser`, and `security` sections.
|
|
243
|
+
Unknown keys fail validation. Keep the file owner-readable only (`chmod 600`);
|
|
244
|
+
the loader rejects group/world-readable configuration files and rejects
|
|
245
|
+
symlinked data directories.
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"transport": "stdio",
|
|
252
|
+
"dataDir": "~/.smooth-operator",
|
|
253
|
+
"browser": {
|
|
254
|
+
"mode": "managed",
|
|
255
|
+
"actionTimeoutMs": 15000
|
|
256
|
+
},
|
|
257
|
+
"security": {
|
|
258
|
+
"allowedDomains": ["example.com", "*.example.org"],
|
|
259
|
+
"blockedDomains": ["admin.example.org"],
|
|
260
|
+
"allowPrivateNetwork": false,
|
|
261
|
+
"allowEval": false
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Environment names correspond to the fields in `.env.example`. Notable
|
|
267
|
+
variables include:
|
|
268
|
+
|
|
269
|
+
| Setting | Default | Notes |
|
|
270
|
+
| --- | --- | --- |
|
|
271
|
+
| `SMOOTH_OPERATOR_TRANSPORT` | `stdio` | `stdio` or `http` |
|
|
272
|
+
| `SMOOTH_OPERATOR_DATA_DIR` | `~/.smooth-operator` | Private data, file, and download roots |
|
|
273
|
+
| `SMOOTH_OPERATOR_BROWSER_MODE` | `managed` | `managed`, `disabled`, `connect`, or `launch` |
|
|
274
|
+
| `SMOOTH_OPERATOR_BROWSER_URL` | `http://127.0.0.1:9222` | DevTools HTTP endpoint |
|
|
275
|
+
| `SMOOTH_OPERATOR_BROWSER_EXECUTABLE` | unset | Managed-mode override; required for explicit launch mode |
|
|
276
|
+
| `SMOOTH_OPERATOR_BROWSER_USER_DATA_DIR` | `${SMOOTH_OPERATOR_DATA_DIR}/browser` | Dedicated persistent agent-Chrome profile |
|
|
277
|
+
| `SMOOTH_OPERATOR_BROWSER_HEADLESS` | `false` | Set `true` for CI/displayless managed or launch use |
|
|
278
|
+
| `SMOOTH_OPERATOR_ALLOWED_DOMAINS` | unset | Comma-separated allowlist |
|
|
279
|
+
| `SMOOTH_OPERATOR_BLOCKED_DOMAINS` | unset | Comma-separated denylist |
|
|
280
|
+
| `SMOOTH_OPERATOR_ALLOWED_FILE_ROOTS` | data `files`, `downloads` | Explicit roots replace defaults |
|
|
281
|
+
| `SMOOTH_OPERATOR_ALLOW_PRIVATE_NETWORK` | `false` | Allows non-loopback private targets when true |
|
|
282
|
+
| `SMOOTH_OPERATOR_ALLOW_EVAL` | `false` | Required, with full policy, for page JavaScript |
|
|
283
|
+
| `SMOOTH_OPERATOR_HTTP_TOKEN` | unset | Required for HTTP; 32+ chars for remote mode |
|
|
284
|
+
| `SMOOTH_OPERATOR_ALLOW_REMOTE_HTTP` | `false` | Allows non-loopback HTTP only with a strong token |
|
|
285
|
+
| `SMOOTH_OPERATOR_HTTP_MAX_BODY_BYTES` | `2000000` | Bounded HTTP request body |
|
|
286
|
+
| `SMOOTH_OPERATOR_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` |
|
|
287
|
+
|
|
288
|
+
## Security enforcement layers
|
|
289
|
+
|
|
290
|
+
There are no client-selectable permissiveness tiers. The following controls
|
|
291
|
+
are always applied, with explicit opt-ins where documented:
|
|
292
|
+
|
|
293
|
+
- HTTP binds to loopback unless remote mode is enabled and authenticated.
|
|
294
|
+
- Navigation is restricted to HTTP(S), rejects embedded credentials, applies
|
|
295
|
+
domain rules, and blocks private/link-local/multicast destinations by default.
|
|
296
|
+
- Hostname navigation performs a DNS/private-address preflight and rejects
|
|
297
|
+
redirects that leave policy. The browser's own later DNS resolution is not
|
|
298
|
+
fully controllable by this process; DNS rebinding is therefore a limitation,
|
|
299
|
+
not a guarantee that a network firewall can be omitted.
|
|
300
|
+
- Upload and PDF destinations must stay within configured file roots after
|
|
301
|
+
realpath and symlink checks. Download paths and generated files are bounded.
|
|
302
|
+
- Page JavaScript is disabled by default. It is available only when the full
|
|
303
|
+
security policy and `SMOOTH_OPERATOR_ALLOW_EVAL=true` are configured; enabling it
|
|
304
|
+
lets page code observe and mutate page state with the browser's privileges.
|
|
305
|
+
- Page text, HTML, titles, attributes, search snippets, cookies, and logs are
|
|
306
|
+
treated as untrusted data, normalized, bounded, and redacted before output.
|
|
307
|
+
- CAPTCHA and anti-bot markers are reported for human handoff. The server does
|
|
308
|
+
not bypass them, rotate identities, or solve challenges.
|
|
309
|
+
|
|
310
|
+
Run the server with a dedicated browser profile and the smallest domain and
|
|
311
|
+
file-root allowlists that fit the task. Browser automation can still perform
|
|
312
|
+
irreversible actions on a site; the MCP client and operator remain responsible
|
|
313
|
+
for confirming destructive calls.
|
|
314
|
+
|
|
315
|
+
## MCP capabilities
|
|
316
|
+
|
|
317
|
+
### Tools
|
|
318
|
+
|
|
319
|
+
The registry includes these groups of tools. Every input is schema-validated;
|
|
320
|
+
individual descriptions and limits are returned by `tools/list`.
|
|
321
|
+
|
|
322
|
+
**Observation and extraction:** `browser_snapshot`, `browser_tabs`,
|
|
323
|
+
`browser_list_tabs`, `browser_list_sessions`, `browser_get_state`,
|
|
324
|
+
`browser_page_info`, `browser_interactive`, `browser_frames`,
|
|
325
|
+
`browser_accessibility_snapshot`, `browser_extract`, `browser_extract_content`,
|
|
326
|
+
`browser_find_text`, `browser_search_page`, `browser_find_elements`,
|
|
327
|
+
`browser_dropdown_options`, `browser_computed_style`, `browser_page_next`,
|
|
328
|
+
`browser_get_html`, `browser_challenge`, `browser_doctor`, and `server_health`.
|
|
329
|
+
|
|
330
|
+
**Navigation and interaction:** `browser_navigate`, `browser_back`,
|
|
331
|
+
`browser_go_back`, `browser_forward`, `browser_reload`, `browser_switch_tab`,
|
|
332
|
+
`browser_close_tab`, `browser_click`, `browser_input`, `browser_select`,
|
|
333
|
+
`browser_scroll`, `browser_scroll_to_bottom`, `browser_key`,
|
|
334
|
+
`browser_wait`, `browser_wait_for_element`, `browser_wait_for_text`, `browser_wait_for_url`,
|
|
335
|
+
`browser_wait_for_network_idle`, `browser_hover`, `browser_press_and_hold`,
|
|
336
|
+
`browser_type`, `browser_close`, and `browser_close_all`.
|
|
337
|
+
|
|
338
|
+
**Explicitly gated capabilities:** `browser_screenshot`, `browser_pdf`,
|
|
339
|
+
`browser_upload`, `browser_downloads`, `browser_network_log`,
|
|
340
|
+
`browser_console_log`, `browser_dialog`, `browser_cookies`,
|
|
341
|
+
`browser_storage`, `browser_evaluate`, `browser_batch`,
|
|
342
|
+
`browser_exec`, `browser_wait_for_human`, `browser_close_session`, and
|
|
343
|
+
the explicit browser-session lifecycle controls.
|
|
344
|
+
|
|
345
|
+
`browser_evaluate` is page JavaScript and is disabled by default. `browser_exec`
|
|
346
|
+
accepts only a JSON array of validated browser actions; it is not a shell,
|
|
347
|
+
Python, or arbitrary code runner. Destructive batch actions require explicit
|
|
348
|
+
confirmation. `browser_wait_for_human` pauses for an operator to complete a
|
|
349
|
+
visible sign-in or challenge, and `browser_close_session` closes the one
|
|
350
|
+
native browser session by its explicit session identifier.
|
|
351
|
+
|
|
352
|
+
`web_search` performs bounded DuckDuckGo retrieval. Search titles, URLs, and
|
|
353
|
+
snippets are untrusted observations, not instructions or proof of claims. Its
|
|
354
|
+
`maxResults` input is capped at 10, and `maxChars` (500–4,000 through the MCP
|
|
355
|
+
schema) is one aggregate budget across the returned title and snippet text,
|
|
356
|
+
not a per-result multiplier. URL fields and fixed untrusted-data wrapper
|
|
357
|
+
markers are outside that text budget. The response body is bounded before
|
|
358
|
+
parsing, redirects are rejected, cancellation and timeout are propagated, and
|
|
359
|
+
credentials/query secret placeholders are removed from result URLs.
|
|
360
|
+
|
|
361
|
+
### Resources
|
|
362
|
+
|
|
363
|
+
The server publishes read-only resources:
|
|
364
|
+
|
|
365
|
+
- `smooth-operator://server/capabilities`
|
|
366
|
+
- `smooth-operator://browser/tabs`
|
|
367
|
+
- `smooth-operator://browser/page/current`
|
|
368
|
+
- `smooth-operator://browser/page/{pageId}`
|
|
369
|
+
- `smooth-operator://browser/downloads`
|
|
370
|
+
- `smooth-operator://browser/logs/network`
|
|
371
|
+
- `smooth-operator://browser/logs/console`
|
|
372
|
+
|
|
373
|
+
Resource output is bounded and follows the same redaction and policy rules as
|
|
374
|
+
tool output.
|
|
375
|
+
|
|
376
|
+
### Prompts
|
|
377
|
+
|
|
378
|
+
The user-facing prompt templates are `agent-chrome-setup`, `browser-workflow`,
|
|
379
|
+
`extract-page`, and `research-question`. They are short starting points for the
|
|
380
|
+
MCP client's own conversation; they are not hidden instructions or a planning
|
|
381
|
+
engine.
|
|
382
|
+
|
|
383
|
+
## Lifecycle and cleanup
|
|
384
|
+
|
|
385
|
+
At startup the process validates arguments and configuration, creates private
|
|
386
|
+
data directories, constructs the runtime, and registers the MCP surface. The
|
|
387
|
+
browser is connected or launched only when a browser operation requires it.
|
|
388
|
+
|
|
389
|
+
On SIGINT/SIGTERM, the server stops accepting HTTP requests, waits for active
|
|
390
|
+
requests up to a bounded grace period, closes the MCP transport, closes pages,
|
|
391
|
+
and terminates a browser process that it owns. A browser connected in `connect`
|
|
392
|
+
mode remains under the operator's ownership.
|
|
393
|
+
|
|
394
|
+
To clean up a local installation:
|
|
395
|
+
|
|
396
|
+
```sh
|
|
397
|
+
smooth-operator install claude-desktop # inspect config before removal
|
|
398
|
+
npm uninstall -g smooth-operator-mcp
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Remove the corresponding `SmoothOperator` entry from a harness config and delete
|
|
402
|
+
`SMOOTH_OPERATOR_DATA_DIR` only after preserving any downloads or PDFs you need.
|
|
403
|
+
The installer creates owner-only, uniquely named `.bak` files when changing an
|
|
404
|
+
existing JSON/JSONC harness config; retain or remove those backups according
|
|
405
|
+
to your local recovery policy.
|
|
406
|
+
|
|
407
|
+
## Troubleshooting
|
|
408
|
+
|
|
409
|
+
- **No browser tabs:** verify the DevTools URL, that the browser is running,
|
|
410
|
+
and that `SMOOTH_OPERATOR_BROWSER_MODE` is not `disabled`.
|
|
411
|
+
- **Launch fails:** provide an executable path and a new writable profile;
|
|
412
|
+
`puppeteer-core` does not download Chrome.
|
|
413
|
+
- **HTTP 401/403:** check the bearer token, Host/Origin allowlists, and that
|
|
414
|
+
remote mode was explicitly enabled for a non-loopback bind.
|
|
415
|
+
- **Private target blocked:** keep the default deny unless the target is an
|
|
416
|
+
intentional private service, then set `SMOOTH_OPERATOR_ALLOW_PRIVATE_NETWORK=true`
|
|
417
|
+
and use a narrow domain allowlist.
|
|
418
|
+
- **File rejected:** configure an allowed root and use a path beneath it;
|
|
419
|
+
symlink escapes are rejected after resolution.
|
|
420
|
+
- **MCP client shows no tools:** inspect stderr separately from stdout, run
|
|
421
|
+
`smooth-operator --version`, and perform a fresh client handshake after
|
|
422
|
+
changing the config.
|
|
423
|
+
- **GUI client cannot spawn the server:** use the absolute Node-plus-bundled
|
|
424
|
+
entrypoint form shown above; GUI applications often have a smaller PATH.
|
|
425
|
+
|
|
426
|
+
For harness-specific CLI syntax, config paths, and the OpenCode interactive
|
|
427
|
+
CLI limitation, read [harnesses.md](harnesses.md).
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "smooth-operator-mcp",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"packageManager": "npm@10.9.8",
|
|
6
|
+
"description": "A lightweight, production-grade MCP server for secure browser automation.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Gitshop77/Smooth-Operator.git"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"docs/mcp-server.md",
|
|
15
|
+
"docs/harnesses.md",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
".env.example"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"smooth-operator": "dist/smooth-operator.mjs"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"start": "tsx src/server/main.ts",
|
|
25
|
+
"dev": "tsx watch src/server/main.ts",
|
|
26
|
+
"build": "esbuild src/server/main.ts --bundle --platform=node --format=esm --packages=external --banner:js='#!/usr/bin/env node' --outfile=dist/smooth-operator.mjs --sourcemap --sources-content=false",
|
|
27
|
+
"postbuild": "node scripts/set-executable.mjs dist/smooth-operator.mjs",
|
|
28
|
+
"lint": "eslint .",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:browser:live": "node scripts/test-browser-live.mjs",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"test:coverage": "vitest run --coverage",
|
|
34
|
+
"dead-code": "knip --include files,exports,dependencies,unlisted --no-progress",
|
|
35
|
+
"mcp:stdio": "npm run start -- --transport stdio",
|
|
36
|
+
"mcp:http": "npm run start -- --transport http",
|
|
37
|
+
"benchmark:mcp": "node scripts/benchmark-mcp.mjs",
|
|
38
|
+
"benchmark:mcp:live": "node scripts/benchmark-mcp.mjs --live",
|
|
39
|
+
"benchmark:mcp:dist": "node scripts/benchmark-mcp.mjs --dist",
|
|
40
|
+
"package:smoke": "npm run build && node scripts/verify-package.mjs",
|
|
41
|
+
"package:smoke:install": "npm run build && node scripts/verify-package.mjs --install",
|
|
42
|
+
"release:smoke": "npm run package:smoke:install && npm run benchmark:mcp:dist",
|
|
43
|
+
"prepack": "npm run build",
|
|
44
|
+
"prepare": "npm run build"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@modelcontextprotocol/node": "^2.0.0",
|
|
48
|
+
"@modelcontextprotocol/server": "^2.0.0",
|
|
49
|
+
"puppeteer-core": "^25.8.0",
|
|
50
|
+
"zod": "^4.4.3"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
54
|
+
"@types/node": "^26.2.0",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
|
56
|
+
"@typescript-eslint/parser": "^8.67.0",
|
|
57
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
58
|
+
"esbuild": "^0.28.2",
|
|
59
|
+
"eslint": "^10.9.0",
|
|
60
|
+
"knip": "^6.32.2",
|
|
61
|
+
"tsx": "^4.23.12",
|
|
62
|
+
"typescript": "^6.0.3",
|
|
63
|
+
"vitest": "^4.1.11"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=22.23.2",
|
|
67
|
+
"npm": ">=10.9.8"
|
|
68
|
+
}
|
|
69
|
+
}
|