machine-bridge-mcp 0.2.5 → 0.3.3
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/CHANGELOG.md +60 -0
- package/README.md +148 -176
- package/SECURITY.md +84 -0
- package/docs/ARCHITECTURE.md +102 -0
- package/package.json +20 -9
- package/src/local/cli.mjs +289 -298
- package/src/local/daemon.mjs +376 -84
- package/src/local/log.mjs +33 -11
- package/src/local/self-test.mjs +171 -200
- package/src/local/service.mjs +43 -5
- package/src/local/shell.mjs +68 -14
- package/src/local/state.mjs +240 -51
- package/src/worker/index.ts +663 -423
- package/wrangler.jsonc +2 -2
- package/src/local/api-server.mjs +0 -368
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.3 - 2026-07-10
|
|
4
|
+
|
|
5
|
+
### Security
|
|
6
|
+
|
|
7
|
+
- Add an exact-version npm `allowScripts` policy for the reviewed Wrangler runtime/build binaries (`esbuild@0.28.1`, `sharp@0.34.5`, and `workerd@1.20260708.1`) and explicitly deny the optional `fsevents` install script. Dependency upgrades therefore require renewed script approval.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Exclude the development-only Worker integration test from the published npm package while retaining it in the repository and CI.
|
|
12
|
+
|
|
13
|
+
## 0.3.2 - 2026-07-10
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Keep the healthy daemon active until a replacement connection completes its authenticated `hello` handshake; enforce the candidate deadline with a Durable Object alarm so it survives WebSocket hibernation, and reject stale or non-handshaking candidates without disrupting the active connection.
|
|
18
|
+
- Send the candidate acknowledgement before closing the previous daemon, expire failed acknowledgements, clean up duplicate authenticated sockets on subsequent handshakes, and reject pre-handshake messages without changing active tool metadata.
|
|
19
|
+
|
|
20
|
+
## 0.3.1 - 2026-07-10
|
|
21
|
+
|
|
22
|
+
### Security
|
|
23
|
+
|
|
24
|
+
- Replace reversible unsalted source-address hashes used for OAuth registration limits and password throttling with deployment-keyed HMAC identifiers; stop persisting User-Agent-derived identities, remove legacy unsalted identifiers during store migration, and fail closed if no identity key is configured.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Preserve timeout escalation after the direct child exits so a process-group `SIGKILL` still removes descendants that ignore `SIGTERM`; add a regression test for the orphan-process boundary.
|
|
29
|
+
- Replace legacy unsalted source-identity hashes with deployment-keyed HMAC identifiers, prune legacy stored identifiers, and add integration coverage for registration quotas and login throttling.
|
|
30
|
+
|
|
31
|
+
## 0.3.0 - 2026-07-10
|
|
32
|
+
|
|
33
|
+
### Security
|
|
34
|
+
|
|
35
|
+
- Confine filesystem tools to the canonical workspace by default; add explicit `--unrestricted-paths` opt-in and symbolic-link escape protection.
|
|
36
|
+
- Make `write_file` bounded and atomic, reject symbolic-link/non-regular destinations, and enforce optimistic hashes consistently.
|
|
37
|
+
- Add recursive log-field and token redaction, control-character neutralization, owner-only service logs, and bounded log trimming.
|
|
38
|
+
- Harden OAuth with strict PKCE/resource/client/redirect validation, validated client disclosure on the consent page, password-failure throttling, bounded dynamic registration, per-source and per-client limits, inactive-client cleanup, strict HTTP methods/UTF-8 handling, and security response headers.
|
|
39
|
+
- Minimize unauthenticated and relay metadata disclosure: remove workspace name/path hash/process ID from the daemon handshake, reduce default observability sampling, and bind pending calls to their originating daemon socket.
|
|
40
|
+
- Bound Worker/daemon request sizes and concurrency; serialize OAuth mutations; isolate WebSocket generations; serialize per-workspace startup/deploy/rotation operations; terminate timed-out or disconnected child processes and bound Wrangler subprocesses.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Raise the minimum Node.js version to 22 because the current Wrangler release no longer runs on Node 20; require the installed Wrangler dependency instead of falling back to an implicit network `npx` execution.
|
|
45
|
+
- Remove the experimental local OpenAI-compatible `/v1` API and MCP sampling proxy.
|
|
46
|
+
- Detect nested Git repositories for `git_status` and `git_diff`, while disabling repository-configured external diff, text conversion, and filesystem-monitor hooks for bridge Git operations.
|
|
47
|
+
- Use atomic local state/config writes, bounded corrupt-state backups, strict adoption/migration of dedicated state roots, and guarded state-root deletion during uninstall, including refusal while active daemon/startup locks remain.
|
|
48
|
+
- Reject unknown, duplicate, malformed, and command-inapplicable CLI options; fix boolean options consuming positional workspaces.
|
|
49
|
+
- Verify the expected Worker version before accepting a deployment hash.
|
|
50
|
+
- Reduce the default Worker request-body limit to 8 MiB; bound command length, directory results, recursive traversal, and path-result payloads; provide exact-origin CORS while rejecting implicit loopback browser origins.
|
|
51
|
+
- Update Wrangler to 4.110.x, TypeScript to 5.9.x, and Node type definitions to the supported Node 22 API line.
|
|
52
|
+
|
|
53
|
+
### Tests and documentation
|
|
54
|
+
|
|
55
|
+
- Expand self-tests across path boundaries, symlinks, writes, UTF-8, nested Git, environment isolation, locking, CLI parsing, state recovery/removal guards, logging, service logs, and Worker hardening invariants.
|
|
56
|
+
- Add a live local Worker OAuth/MCP integration test, verified Node 22/24 compatibility, a Worker build dry-run, dependency audit, security policy, and architecture document.
|
|
57
|
+
|
|
58
|
+
## 0.2.5
|
|
59
|
+
|
|
60
|
+
- Previous release.
|
package/README.md
CHANGED
|
@@ -1,154 +1,150 @@
|
|
|
1
1
|
# machine-bridge-mcp
|
|
2
2
|
|
|
3
|
-
`machine-bridge-mcp`
|
|
3
|
+
`machine-bridge-mcp` exposes a selected local workspace to Remote MCP clients through a Cloudflare Worker relay and an outbound-only local daemon.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```text
|
|
6
|
+
Remote MCP client -> HTTPS/OAuth -> Cloudflare Worker + Durable Object
|
|
7
|
+
^
|
|
8
|
+
| outbound WebSocket
|
|
9
|
+
|
|
|
10
|
+
local daemon -> workspace/files/shell
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
No inbound port is opened on the local machine. The Worker does not read local files or execute commands; it authenticates MCP clients and relays bounded tool calls to the daemon.
|
|
14
|
+
|
|
15
|
+
## Install and start
|
|
16
|
+
|
|
17
|
+
Recommended global installation:
|
|
6
18
|
|
|
7
19
|
```zsh
|
|
8
|
-
npm install -g machine-bridge-mcp@latest
|
|
20
|
+
npm install -g machine-bridge-mcp@latest
|
|
21
|
+
machine-mcp
|
|
9
22
|
```
|
|
10
23
|
|
|
11
|
-
|
|
24
|
+
Without a global installation:
|
|
12
25
|
|
|
13
26
|
```zsh
|
|
14
27
|
npx machine-bridge-mcp@latest
|
|
15
28
|
```
|
|
16
29
|
|
|
17
|
-
|
|
30
|
+
From a source checkout:
|
|
18
31
|
|
|
19
32
|
```zsh
|
|
33
|
+
npm install
|
|
20
34
|
./mbm # macOS/Linux
|
|
21
35
|
.\mbm.cmd # Windows cmd
|
|
22
36
|
```
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
On first run, the CLI:
|
|
25
39
|
|
|
26
|
-
1.
|
|
27
|
-
2.
|
|
28
|
-
3.
|
|
29
|
-
4.
|
|
30
|
-
5.
|
|
31
|
-
6.
|
|
32
|
-
7.
|
|
33
|
-
8. Prints MCP connection details on first run, plus local API settings:
|
|
40
|
+
1. Selects and remembers a workspace.
|
|
41
|
+
2. Generates an MCP connection password, daemon secret, and OAuth token version.
|
|
42
|
+
3. Authenticates Wrangler if required.
|
|
43
|
+
4. Deploys a per-workspace Worker.
|
|
44
|
+
5. Installs a login autostart entry unless `--no-autostart` is used.
|
|
45
|
+
6. Starts the outbound local daemon.
|
|
46
|
+
7. Prints the Remote MCP URL and connection password.
|
|
34
47
|
|
|
35
|
-
|
|
36
|
-
MCP Server URL: https://<worker>.<account>.workers.dev/mcp
|
|
37
|
-
MCP connection password: mcp_password_...
|
|
38
|
-
API Base URL: http://127.0.0.1:8765/v1
|
|
39
|
-
API key: local_api_key_...
|
|
40
|
-
```
|
|
48
|
+
Keep the foreground process running for the current session. The installed service handles later logins.
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
## Connect an MCP client
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
Use the values printed by `machine-mcp`:
|
|
45
53
|
|
|
46
|
-
```
|
|
47
|
-
|
|
54
|
+
```text
|
|
55
|
+
MCP Server URL: https://<worker>.<account>.workers.dev/mcp
|
|
56
|
+
MCP connection password: mcp_password_...
|
|
48
57
|
```
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
MCP connection details are printed on first run, after secret rotation, when the MCP URL changes, or when you explicitly pass `--print-mcp-credentials`. Routine runs print that MCP details are unchanged.
|
|
59
|
+
The password is shown on first setup, after secret rotation, when the MCP URL changes, or with `--print-mcp-credentials`. Use `--no-print-credentials` to redact it in terminal output.
|
|
53
60
|
|
|
54
|
-
|
|
61
|
+
The former experimental local OpenAI-compatible `/v1` API has been removed. Use the Remote MCP endpoint directly.
|
|
55
62
|
|
|
56
|
-
|
|
63
|
+
## Security defaults
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
- **Desktop clients such as Cherry Studio, Chatbox, or Continue:** use the optional local OpenAI-compatible `/v1` API. `POST /v1/chat/completions` is backed by MCP sampling: the local API asks the hosted Worker to send `sampling/createMessage` to the already-connected ChatGPT MCP client, then wraps the MCP sampling result as an OpenAI-compatible chat completion response.
|
|
65
|
+
Version 0.3.0 changes the default filesystem boundary:
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
- Relative paths are resolved from the selected workspace.
|
|
68
|
+
- Reads, writes, directory traversal, searches, and Git operations are confined to that workspace by default.
|
|
69
|
+
- Symbolic links cannot be used to escape the workspace boundary.
|
|
70
|
+
- `write_file` and `exec_command` remain enabled by default.
|
|
71
|
+
- Shell commands receive a minimal environment by default.
|
|
72
|
+
- `write_file` is limited to 5 MiB, writes atomically, and refuses to overwrite symbolic links.
|
|
73
|
+
- Files that look sensitive, including `.env` and key files, are readable when they are inside the selected workspace. The bridge does not infer sensitivity from filenames.
|
|
62
74
|
|
|
63
|
-
|
|
75
|
+
A narrower session:
|
|
64
76
|
|
|
65
77
|
```zsh
|
|
66
|
-
machine-mcp
|
|
78
|
+
machine-mcp --no-write --no-exec
|
|
67
79
|
```
|
|
68
80
|
|
|
69
|
-
|
|
81
|
+
Explicitly permit filesystem paths outside the workspace:
|
|
70
82
|
|
|
71
83
|
```zsh
|
|
72
|
-
machine-mcp
|
|
84
|
+
machine-mcp --unrestricted-paths
|
|
73
85
|
```
|
|
74
86
|
|
|
75
|
-
|
|
87
|
+
Pass the complete parent process environment to shell commands only when required:
|
|
76
88
|
|
|
77
89
|
```zsh
|
|
78
|
-
machine-mcp --
|
|
90
|
+
machine-mcp --full-env
|
|
79
91
|
```
|
|
80
92
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
```text
|
|
84
|
-
API Base URL: http://127.0.0.1:8765/v1
|
|
85
|
-
API key: local_api_key_...
|
|
86
|
-
Client type: OpenAI-compatible
|
|
87
|
-
Model: chatgpt-mcp
|
|
88
|
-
Backend: ChatGPT MCP sampling via the connected ChatGPT app
|
|
89
|
-
```
|
|
93
|
+
`--unrestricted-paths` and `--full-env` materially increase the data-exposure boundary. See [SECURITY.md](SECURITY.md) before enabling them.
|
|
90
94
|
|
|
91
|
-
|
|
95
|
+
## Commands
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
machine-mcp
|
|
97
|
-
machine-mcp
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
```zsh
|
|
103
|
-
machine-mcp api --port 8766
|
|
97
|
+
```text
|
|
98
|
+
machine-mcp [start options]
|
|
99
|
+
machine-mcp workspace show
|
|
100
|
+
machine-mcp workspace set [PATH]
|
|
101
|
+
machine-mcp service status|install|start|stop|uninstall
|
|
102
|
+
machine-mcp status
|
|
103
|
+
machine-mcp doctor
|
|
104
|
+
machine-mcp rotate-secrets
|
|
105
|
+
machine-mcp uninstall [--keep-worker] [--yes]
|
|
104
106
|
```
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Rotate the local desktop-client API key with:
|
|
108
|
+
Important start options:
|
|
109
109
|
|
|
110
|
-
```
|
|
111
|
-
|
|
110
|
+
```text
|
|
111
|
+
--workspace PATH
|
|
112
|
+
--worker-name NAME
|
|
113
|
+
--force-worker
|
|
114
|
+
--rotate-secrets
|
|
115
|
+
--daemon-only
|
|
116
|
+
--no-autostart
|
|
117
|
+
--no-print-credentials
|
|
118
|
+
--print-mcp-credentials
|
|
119
|
+
--no-write
|
|
120
|
+
--no-exec
|
|
121
|
+
--full-env
|
|
122
|
+
--unrestricted-paths
|
|
123
|
+
--state-dir DIR
|
|
124
|
+
--json
|
|
112
125
|
```
|
|
113
126
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
Supported local API routes:
|
|
127
|
+
Unknown, duplicate, malformed, and command-inapplicable options are rejected. Boolean options do not consume a following positional workspace path; use `--option=false` when an explicit false value is needed.
|
|
117
128
|
|
|
118
|
-
|
|
119
|
-
- `GET /v1/models` with `Authorization: Bearer <local_api_key>` or `x-api-key`
|
|
120
|
-
- `POST /v1/chat/completions`
|
|
121
|
-
|
|
122
|
-
`POST /v1/responses`, `POST /v1/completions`, and `POST /v1/embeddings` return `501 unsupported_endpoint`; MCP sampling is a chat-message path and does not expose embeddings or the full Responses API. Logs record route, status, latency, and safe configuration metadata; request and response bodies and API keys are not logged.
|
|
123
|
-
|
|
124
|
-
## Re-select workspace
|
|
129
|
+
## Workspace selection
|
|
125
130
|
|
|
126
131
|
```zsh
|
|
127
132
|
machine-mcp workspace set
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Or provide it directly:
|
|
131
|
-
|
|
132
|
-
```zsh
|
|
133
|
-
machine-mcp workspace set /path/to/new/default
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Show the remembered workspace:
|
|
137
|
-
|
|
138
|
-
```zsh
|
|
133
|
+
machine-mcp workspace set /path/to/project
|
|
139
134
|
machine-mcp workspace show
|
|
135
|
+
machine-mcp workspace reset
|
|
140
136
|
```
|
|
141
137
|
|
|
142
|
-
|
|
138
|
+
Each canonical workspace path receives an independent profile, Worker name, secret set, and daemon lock.
|
|
139
|
+
|
|
140
|
+
## Autostart
|
|
143
141
|
|
|
144
|
-
Supported
|
|
142
|
+
Supported providers:
|
|
145
143
|
|
|
146
144
|
- macOS: user LaunchAgent
|
|
147
|
-
- Linux: `systemd --user
|
|
145
|
+
- Linux: `systemd --user`, with best-effort user lingering
|
|
148
146
|
- Windows: Scheduled Task at logon
|
|
149
147
|
|
|
150
|
-
Commands:
|
|
151
|
-
|
|
152
148
|
```zsh
|
|
153
149
|
machine-mcp service status
|
|
154
150
|
machine-mcp service install
|
|
@@ -157,66 +153,16 @@ machine-mcp service stop
|
|
|
157
153
|
machine-mcp service uninstall
|
|
158
154
|
```
|
|
159
155
|
|
|
160
|
-
|
|
156
|
+
Autostart runs `--daemon-only --no-print-credentials --quiet`. The selected write, execution, environment, and path-boundary policies are persisted in the service definition. Service log files are owner-only where supported and are trimmed at daemon startup to prevent unbounded growth.
|
|
161
157
|
|
|
162
|
-
|
|
163
|
-
machine-mcp --no-autostart
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
Autostart runs the daemon with `--daemon-only --no-print-credentials`, so service logs do not contain the MCP connection password. If you start with `--no-write`, `--no-exec`, or `--full-env`, those policy flags are preserved in the autostart entry. macOS/Linux service definitions restart only on process failure; a normal duplicate-instance exit is not treated as a crash loop.
|
|
167
|
-
|
|
168
|
-
## Secrets rotation
|
|
158
|
+
## Secret rotation
|
|
169
159
|
|
|
170
160
|
```zsh
|
|
171
161
|
machine-mcp rotate-secrets
|
|
172
162
|
machine-mcp
|
|
173
163
|
```
|
|
174
164
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
## Uninstall
|
|
178
|
-
|
|
179
|
-
Delete known deployed Worker(s), remove autostart entries, and remove local state:
|
|
180
|
-
|
|
181
|
-
```zsh
|
|
182
|
-
machine-mcp uninstall
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Non-interactive:
|
|
186
|
-
|
|
187
|
-
```zsh
|
|
188
|
-
machine-mcp uninstall --yes
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
Keep the deployed Worker but remove local state/autostart:
|
|
192
|
-
|
|
193
|
-
```zsh
|
|
194
|
-
machine-mcp uninstall --keep-worker
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
If installed globally, remove the npm package afterwards:
|
|
198
|
-
|
|
199
|
-
```zsh
|
|
200
|
-
npm uninstall -g machine-bridge-mcp
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## Defaults and permissions
|
|
204
|
-
|
|
205
|
-
This project optimizes for easy use with official Remote MCP clients:
|
|
206
|
-
|
|
207
|
-
- `write_file` is enabled by default.
|
|
208
|
-
- `exec_command` is enabled by default.
|
|
209
|
-
- Absolute paths are allowed.
|
|
210
|
-
- Parent-directory paths such as `../other-project/file.ts` are allowed.
|
|
211
|
-
- Sensitive-looking files such as `.env`, private keys, token files, and dot-directories are not hidden by default.
|
|
212
|
-
- Relative paths use the selected workspace as cwd.
|
|
213
|
-
- Shell commands run with a minimal environment by default; use `--full-env` to pass the parent process environment.
|
|
214
|
-
|
|
215
|
-
Narrower session:
|
|
216
|
-
|
|
217
|
-
```zsh
|
|
218
|
-
machine-mcp --no-write --no-exec
|
|
219
|
-
```
|
|
165
|
+
The first command stops the installed autostart service, refuses to proceed if another foreground daemon remains active, and then rotates the MCP password, daemon secret, and OAuth token version in local state. The second redeploys the Worker; previously issued OAuth access tokens then fail validation.
|
|
220
166
|
|
|
221
167
|
## MCP tools
|
|
222
168
|
|
|
@@ -232,6 +178,8 @@ machine-mcp --no-write --no-exec
|
|
|
232
178
|
- `git_diff`
|
|
233
179
|
- `exec_command`
|
|
234
180
|
|
|
181
|
+
Tool availability reflects daemon policy after the daemon handshake. `git_status` and `git_diff` detect the repository containing the requested path, including nested repositories.
|
|
182
|
+
|
|
235
183
|
## State and logs
|
|
236
184
|
|
|
237
185
|
Default state roots:
|
|
@@ -240,54 +188,78 @@ Default state roots:
|
|
|
240
188
|
- Linux with `XDG_STATE_HOME`: `$XDG_STATE_HOME/machine-bridge-mcp`
|
|
241
189
|
- Windows: `%APPDATA%\machine-bridge-mcp`
|
|
242
190
|
|
|
243
|
-
|
|
191
|
+
Override the root with `--state-dir DIR`. A new custom root must be empty; a non-empty unmarked root must contain recognizable legacy Machine Bridge state before it is adopted. Legacy text markers are migrated to the current structured marker. Uninstall refuses to recursively delete filesystem roots, the home directory, the current/package directory, source trees, recorded workspaces, unrelated files, or an unrecognized state root.
|
|
192
|
+
|
|
193
|
+
State contains credentials and therefore uses owner-only permissions where supported. State/config writes use temporary files, `fsync`, and atomic rename. Corrupt state files are retained as bounded `.corrupt-*` backups rather than silently overwritten. Status and doctor output redact secrets and personal Wrangler output; structured logs fully redact credential-like fields and neutralize control characters.
|
|
194
|
+
|
|
195
|
+
Temporary Wrangler secret files are created under the owner-only profile directory and removed in a `finally` block. Stale files older than one hour are cleaned before deployment.
|
|
196
|
+
|
|
197
|
+
## Worker protections
|
|
198
|
+
|
|
199
|
+
The Worker currently enforces:
|
|
200
|
+
|
|
201
|
+
- OAuth authorization code flow with PKCE S256.
|
|
202
|
+
- Hashed access-token storage and token-version revocation.
|
|
203
|
+
- Exact resource, client, and redirect URI binding.
|
|
204
|
+
- Bounded registration metadata, clients, authorization codes, tokens, and failed-login records.
|
|
205
|
+
- Per-source dynamic-client registration limits and password-failure throttling.
|
|
206
|
+
- A consent page that displays the validated client and redirect URI.
|
|
207
|
+
- Request-body and concurrent daemon-call limits.
|
|
208
|
+
- Same-origin browser access by default, exact configured-origin CORS/preflight support, and rejection of unlisted cross-origin requests.
|
|
209
|
+
- `no-store`, content-type protection, CSP, frame denial, and referrer suppression on authorization responses.
|
|
210
|
+
- Minimal public health output; live daemon/workspace details require an authenticated MCP call.
|
|
211
|
+
|
|
212
|
+
Browser requests are same-origin by default. Additional browser origins can be listed exactly in `MBM_ALLOWED_ORIGINS`; the Worker then returns matching CORS preflight and response headers. Loopback OAuth redirect URIs do not implicitly authorize loopback browser origins. Do not use wildcards or `null`.
|
|
244
213
|
|
|
245
|
-
|
|
214
|
+
## Uninstall
|
|
246
215
|
|
|
247
|
-
|
|
216
|
+
Delete known Workers, autostart entries, and local state:
|
|
248
217
|
|
|
249
218
|
```zsh
|
|
250
|
-
machine-mcp
|
|
219
|
+
machine-mcp uninstall
|
|
251
220
|
```
|
|
252
221
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
C["ChatGPT / MCP client"] -- "HTTPS /mcp + OAuth" --> W["Hosted Worker relay"]
|
|
258
|
-
C -- "GET SSE stream for server-to-client MCP" --> W
|
|
259
|
-
W --> DO["Durable Object broker"]
|
|
260
|
-
D["Local daemon"] -- "outbound WebSocket" --> W
|
|
261
|
-
D --> M["Local filesystem and shell"]
|
|
262
|
-
API["Local /v1/chat/completions"] -- "POST /api/mcp/sampling" --> W
|
|
263
|
-
DO -- "sampling/createMessage" --> C
|
|
264
|
-
CLI["machine-mcp CLI"] --> API
|
|
265
|
-
CLI --> W
|
|
266
|
-
CLI --> D
|
|
267
|
-
CLI --> S["Autostart service"]
|
|
222
|
+
Non-interactive:
|
|
223
|
+
|
|
224
|
+
```zsh
|
|
225
|
+
machine-mcp uninstall --yes
|
|
268
226
|
```
|
|
269
227
|
|
|
270
|
-
|
|
228
|
+
Keep deployed Workers while removing local state and autostart:
|
|
271
229
|
|
|
272
|
-
|
|
273
|
-
-
|
|
274
|
-
|
|
275
|
-
- The Worker stores OAuth client/code/token metadata and relays tool calls.
|
|
276
|
-
- The local daemon is the only process touching files or executing commands.
|
|
277
|
-
- The local `/v1` API binds to loopback by default, starts automatically with the daemon, and can be disabled with `--no-api`.
|
|
278
|
-
- Autostart keeps the daemon and local API alive across logins without requiring MCP clients to change URLs.
|
|
230
|
+
```zsh
|
|
231
|
+
machine-mcp uninstall --keep-worker
|
|
232
|
+
```
|
|
279
233
|
|
|
280
|
-
|
|
234
|
+
Remove a global npm installation separately:
|
|
281
235
|
|
|
282
236
|
```zsh
|
|
283
|
-
npm
|
|
284
|
-
npm run check
|
|
237
|
+
npm uninstall -g machine-bridge-mcp
|
|
285
238
|
```
|
|
286
239
|
|
|
287
|
-
|
|
240
|
+
## Architecture and failure behavior
|
|
241
|
+
|
|
242
|
+
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for trust boundaries, request lifecycles, limits, reconnect behavior, and the rationale for the Worker/Durable Object/daemon split.
|
|
288
243
|
|
|
289
|
-
|
|
244
|
+
Operationally:
|
|
245
|
+
|
|
246
|
+
- A newer daemon connection replaces the older socket. The relay handshake does not upload the local workspace name, path hash, or process ID.
|
|
247
|
+
- Pending calls are bound to the socket that received them, so a stale socket cannot complete or cancel calls on the replacement connection; active child processes are terminated when that connection is lost or replaced.
|
|
248
|
+
- The daemon reconnects after transient Worker/network failures using bounded exponential backoff with jitter, and terminates active child processes when the active relay socket closes.
|
|
249
|
+
- Tool calls and Wrangler subprocesses have bounded execution time and output; timed-out commands terminate their process tree where the platform permits.
|
|
250
|
+
- Separate per-workspace startup and daemon locks prevent overlapping deploy/rotation operations and duplicate local daemons.
|
|
251
|
+
- Uninstall stops and removes autostart first, refuses to proceed while an active startup or daemon process owns a lock, and preserves local state when Worker deletion fails so the operation can be retried.
|
|
252
|
+
- Worker health checks verify the expected package/Worker version before a deployment hash is accepted.
|
|
253
|
+
|
|
254
|
+
## Development
|
|
290
255
|
|
|
291
256
|
```zsh
|
|
292
|
-
|
|
257
|
+
npm ci
|
|
258
|
+
npm run check
|
|
259
|
+
npm run worker:dry-run
|
|
260
|
+
npm audit --omit=dev --audit-level=high
|
|
293
261
|
```
|
|
262
|
+
|
|
263
|
+
`npm run check` generates Worker types, type-checks the Worker, checks all local JavaScript entry points, and runs regression tests for path confinement, symbolic-link escapes, atomic writes, UTF-8 handling, nested Git repositories, minimal environments, daemon locking, CLI parsing, state recovery/removal guards, log redaction, log trimming, Worker hardening guards, and a live local OAuth/MCP flow through Wrangler.
|
|
264
|
+
|
|
265
|
+
The release checks are validated on Node.js 22 and 24. Node.js 22 or newer is required by the current Wrangler toolchain. See [CHANGELOG.md](CHANGELOG.md) for release notes.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are applied to the latest released version. Upgrade before reporting an issue that is already fixed on `main`.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Do not open a public issue for an undisclosed vulnerability. Use GitHub's private vulnerability reporting feature for this repository. Include:
|
|
10
|
+
|
|
11
|
+
- affected version and operating system;
|
|
12
|
+
- deployment mode and relevant policy flags;
|
|
13
|
+
- a minimal reproduction;
|
|
14
|
+
- expected and observed impact;
|
|
15
|
+
- whether credentials, filesystem data, or command execution were exposed.
|
|
16
|
+
|
|
17
|
+
Do not include live MCP passwords, daemon secrets, OAuth tokens, Cloudflare credentials, private keys, or unrelated local files. Rotate any credential used in a reproduction.
|
|
18
|
+
|
|
19
|
+
## Trust model
|
|
20
|
+
|
|
21
|
+
The following components are trusted:
|
|
22
|
+
|
|
23
|
+
1. The local user account running the daemon.
|
|
24
|
+
2. The installed package/source checkout.
|
|
25
|
+
3. The user's Cloudflare account and deployed Worker.
|
|
26
|
+
4. An MCP client that has completed OAuth authorization with the connection password.
|
|
27
|
+
|
|
28
|
+
The Worker is a relay and authorization boundary. The local daemon is the filesystem and command-execution boundary. A successfully authorized MCP client can invoke every tool enabled by the daemon policy and can receive the resulting file contents and command output.
|
|
29
|
+
|
|
30
|
+
This project does not provide a sandbox for shell commands. `exec_command` has the operating-system permissions of the local user. Workspace path confinement limits filesystem tools, but it cannot constrain commands executed by the shell. Use `--no-exec` when shell access is unnecessary.
|
|
31
|
+
|
|
32
|
+
## Primary risks and controls
|
|
33
|
+
|
|
34
|
+
### Credential disclosure
|
|
35
|
+
|
|
36
|
+
Local state contains the MCP password and daemon secret. State, lock, temporary secret, and service-log files use owner-only permissions where the platform supports them. State writes are atomic. Logs recursively and fully redact common credential fields and known token formats.
|
|
37
|
+
|
|
38
|
+
Terminal output can intentionally display the MCP password. Avoid recording first-run output in shared shell history, CI logs, screen recordings, or support tickets. Use `--no-print-credentials` for recorded sessions.
|
|
39
|
+
|
|
40
|
+
### Filesystem exposure
|
|
41
|
+
|
|
42
|
+
Filesystem tools are confined to the canonical selected workspace by default, including symbolic-link resolution. `--unrestricted-paths` exposes absolute and parent paths and should be used only for a deliberate, short-lived session.
|
|
43
|
+
|
|
44
|
+
Sensitive filenames are not automatically blocked inside the workspace. Select the narrowest practical workspace and use `--no-write` for review-only sessions.
|
|
45
|
+
|
|
46
|
+
### Command execution
|
|
47
|
+
|
|
48
|
+
`exec_command` is enabled by default but can be disabled with `--no-exec`. Commands run with a minimal environment unless `--full-env` is supplied. Minimal environment handling reduces accidental environment-secret disclosure; it does not sandbox filesystem, network, process, or credential-store access available to the user account.
|
|
49
|
+
|
|
50
|
+
### OAuth and public endpoints
|
|
51
|
+
|
|
52
|
+
OAuth uses authorization codes, PKCE S256, exact redirect/resource binding, hashed access-token storage, expiring codes/tokens, and a version value that revokes existing tokens after rotation. Client registration, codes, tokens, pending calls, body sizes, and failed password attempts are bounded.
|
|
53
|
+
|
|
54
|
+
The authorization page displays the validated client name and redirect URI. Do not enter the connection password unless you initiated the connection and recognize both values. Browser-originated requests are same-origin by default; exact additional origins require `MBM_ALLOWED_ORIGINS` and receive explicit CORS headers. Loopback redirect URIs do not grant browser-origin access.
|
|
55
|
+
|
|
56
|
+
Public health and metadata endpoints do not expose live daemon policy or connection status. The daemon handshake does not upload the local workspace name, path hash, or process ID. Authenticated `server_info` exposes only bounded operational metadata needed to diagnose the relay.
|
|
57
|
+
|
|
58
|
+
### Relay and denial of service
|
|
59
|
+
|
|
60
|
+
Only one daemon socket is active. New connections replace old ones. Pending calls are socket-bound and concurrency-limited. Request and output sizes are bounded. Dynamic registration is limited globally and per keyed-HMAC source identity; the source IP is not stored directly and inactive clients expire.
|
|
61
|
+
|
|
62
|
+
These controls reduce accidental exhaustion and simple abuse. The CLI uses the package-installed Wrangler binary and does not fall back to downloading an unpinned command through `npx`. They do not replace Cloudflare account protections, rate-limiting/WAF rules, or cost alerts for an internet-facing deployment.
|
|
63
|
+
|
|
64
|
+
## Hardening checklist
|
|
65
|
+
|
|
66
|
+
For a high-sensitivity workspace:
|
|
67
|
+
|
|
68
|
+
```zsh
|
|
69
|
+
machine-mcp --workspace /narrow/project --no-write --no-exec --no-print-credentials
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Also:
|
|
73
|
+
|
|
74
|
+
- keep the local OS and Node.js supported and patched;
|
|
75
|
+
- enable MFA on the Cloudflare and GitHub accounts;
|
|
76
|
+
- do not set `MBM_ALLOWED_ORIGINS` broadly;
|
|
77
|
+
- avoid `--full-env` and `--unrestricted-paths`;
|
|
78
|
+
- rotate secrets after suspected disclosure;
|
|
79
|
+
- inspect `machine-mcp status` and `machine-mcp service status`;
|
|
80
|
+
- uninstall the Worker and local state when the bridge is no longer needed.
|
|
81
|
+
|
|
82
|
+
## Out of scope
|
|
83
|
+
|
|
84
|
+
The project cannot prevent an authorized MCP client from requesting sensitive data that the enabled tools can access. It also cannot make arbitrary shell commands safe. Model-level prompt injection and unsafe operator approval remain outside the transport's security guarantees; use narrow workspaces and restrictive policy flags as the primary controls.
|