machine-bridge-mcp 0.3.3 → 0.4.2
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 +61 -0
- package/README.md +158 -171
- package/SECURITY.md +92 -40
- package/docs/ARCHITECTURE.md +126 -59
- package/docs/CLIENTS.md +125 -0
- package/docs/OPERATIONS.md +83 -0
- package/docs/RELEASING.md +62 -0
- package/docs/TESTING.md +51 -0
- package/package.json +18 -8
- package/src/local/cli.mjs +152 -51
- package/src/local/daemon.mjs +620 -306
- package/src/local/patch.mjs +140 -0
- package/src/local/process-sessions.mjs +352 -0
- package/src/local/service.mjs +5 -16
- package/src/local/shell.mjs +22 -5
- package/src/local/state.mjs +1 -1
- package/src/local/stdio.mjs +194 -0
- package/src/local/tools.mjs +96 -0
- package/src/shared/tool-catalog.json +638 -0
- package/src/worker/index.ts +151 -162
- package/tsconfig.json +4 -2
- package/src/local/self-test.mjs +0 -227
package/SECURITY.md
CHANGED
|
@@ -2,83 +2,135 @@
|
|
|
2
2
|
|
|
3
3
|
## Supported versions
|
|
4
4
|
|
|
5
|
-
Security fixes are applied to the latest released version. Upgrade before reporting an issue
|
|
5
|
+
Security fixes are applied to the latest released version. Upgrade before reporting an issue already fixed on `main`.
|
|
6
6
|
|
|
7
7
|
## Reporting a vulnerability
|
|
8
8
|
|
|
9
|
-
Do not open a public issue for an undisclosed vulnerability. Use GitHub
|
|
9
|
+
Do not open a public issue for an undisclosed vulnerability. Use GitHub private vulnerability reporting for this repository. Include:
|
|
10
10
|
|
|
11
11
|
- affected version and operating system;
|
|
12
|
-
-
|
|
13
|
-
-
|
|
12
|
+
- remote or stdio transport;
|
|
13
|
+
- selected profile and relevant flags;
|
|
14
|
+
- minimal reproduction;
|
|
14
15
|
- expected and observed impact;
|
|
15
|
-
- whether credentials, filesystem data, or
|
|
16
|
+
- whether credentials, filesystem data, process authority, or network access were exposed.
|
|
16
17
|
|
|
17
|
-
Do not include live MCP passwords, daemon secrets, OAuth tokens, Cloudflare credentials, private keys, or unrelated local files. Rotate
|
|
18
|
+
Do not include live MCP passwords, daemon secrets, OAuth tokens, Cloudflare credentials, private keys, or unrelated local files. Rotate credentials used in a reproduction.
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
+
## Core trust model
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
Trusted components are:
|
|
22
23
|
|
|
23
|
-
1.
|
|
24
|
-
2.
|
|
25
|
-
3.
|
|
26
|
-
4.
|
|
24
|
+
1. the local OS user running the runtime;
|
|
25
|
+
2. the installed package/source checkout and its dependencies;
|
|
26
|
+
3. for remote mode, the user's Cloudflare account and deployed Worker;
|
|
27
|
+
4. an MCP client explicitly authorized through OAuth or launched locally through trusted stdio configuration.
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
An authorized client can invoke every tool exposed by the selected profile and receive its results.
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
The Worker is a remote authentication and relay boundary. The local runtime is the filesystem and process boundary. Stdio bypasses the Worker and relies on local process/configuration trust.
|
|
31
32
|
|
|
32
|
-
##
|
|
33
|
+
## Profiles are capability sets, not sandboxes
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
The default for newly selected workspaces is `full`, which prioritizes ease of use over least privilege.
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
- `full` exposes all tools, unrestricted direct filesystem paths, absolute path output, and the complete parent process environment.
|
|
38
|
+
- `agent` exposes file mutation, direct argv execution, and process sessions while keeping direct filesystem tools workspace-confined and the process environment isolated.
|
|
39
|
+
- `edit` exposes read and mutation tools without process execution.
|
|
40
|
+
- `review` exposes read-only workspace/Git/image tools.
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
`run_process` avoids shell parsing, which removes one injection class, but an executable such as `node`, `python`, a package manager, compiler, test runner, or repository script can execute arbitrary code. Direct mode therefore has effectively broad local-user authority once an attacker controls argv or executed code.
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
`exec_command` has both executable authority and shell expansion. Use `--no-exec` or `review`/`edit` when process execution is unnecessary.
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
For untrusted repositories or instructions, run the bridge inside a disposable VM/container or under a dedicated low-privilege OS account. On macOS and Windows, this external isolation is especially important. The project does not claim an in-process OS sandbox.
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
## Filesystem exposure
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
Direct filesystem scope is profile-dependent. The default `full` profile is unrestricted. The `agent`, `edit`, and `review` profiles confine direct filesystem tools to the canonical selected workspace, including symbolic-link resolution.
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
The default `full` profile returns absolute paths. Narrower profiles return workspace-relative paths to reduce username and directory-layout disclosure. Path display and access scope remain separate controls.
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
Sensitive-looking names are not blocked inside the workspace. Files such as `.env`, private keys, credentials, database dumps, and production configuration remain readable when they are in the selected workspace. Choose the narrowest practical workspace.
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
Processes are not confined by the filesystem-tool resolver. They can access paths, networks, processes, credential stores, and devices available to the local user.
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
## Mutation integrity
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
Writes are bounded, reject symbolic-link/non-regular destinations, use same-directory staging, and commit atomically per file. Create-only commit fails if a concurrent destination appears. Expected hashes and exact edits reduce accidental stale overwrites.
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
Patch operations prevalidate all paths/content, reject canonical collisions, recheck source hashes and destination absence, serialize bridge mutations, maintain backups, and roll back on ordinary commit errors.
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
These controls do not defend against a malicious process running under the same user racing filesystem metadata, nor do they make a multi-directory patch power-loss atomic.
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
## Credential exposure
|
|
67
|
+
|
|
68
|
+
Local state contains the MCP connection password and daemon secret. State, lock, temporary secret, runtime, and service-log files use owner-only permissions where supported. State writes are atomic. Logs recursively redact known credential fields and token formats and neutralize control characters.
|
|
69
|
+
|
|
70
|
+
First-run or explicit credential output can intentionally display the MCP password. Avoid shared terminal logs, shell recordings, screenshots, CI output, or support tickets. Use `--no-print-credentials` for recorded sessions.
|
|
71
|
+
|
|
72
|
+
The default `full` profile passes the complete parent environment. Narrower profiles replace HOME, temp, and common cache paths and do not pass arbitrary parent variables. The isolated mode reduces accidental environment-secret leakage; it does not prevent code from explicitly accessing known resources.
|
|
73
|
+
|
|
74
|
+
## OAuth and public endpoints
|
|
75
|
+
|
|
76
|
+
Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, token-version revocation, and bounded dynamic client registration.
|
|
77
|
+
|
|
78
|
+
The authorization page displays the validated client name and redirect URI. Enter the connection password only after initiating the connection and recognizing both values.
|
|
79
|
+
|
|
80
|
+
Password failures and registrations are limited by deployment-keyed HMAC source identity. Browser requests are same-origin unless an exact origin is listed in `MBM_ALLOWED_ORIGINS`; loopback OAuth redirect permission does not grant browser-origin access.
|
|
81
|
+
|
|
82
|
+
Public health and metadata do not expose live workspace or daemon status. The daemon handshake omits workspace path/name/hash and process ID.
|
|
83
|
+
|
|
84
|
+
## Relay and denial of service
|
|
85
|
+
|
|
86
|
+
Only one authenticated daemon is active. Candidates have a handshake deadline and cannot displace the current daemon before success. Pending calls are socket-bound, client-request-bound, concurrency-limited, size-limited, and timed out. Duplicate in-flight request IDs for one access token are rejected.
|
|
87
|
+
|
|
88
|
+
Request bodies, WebSocket messages, tool outputs, traversals, sessions, stdin writes, OAuth records, clients, codes, tokens, and failure identities are bounded. Disconnect/replacement terminates active child process trees.
|
|
89
|
+
|
|
90
|
+
These controls reduce accidental exhaustion and simple abuse. They do not replace Cloudflare account MFA, WAF/rate limits, usage alerts, or cost controls for an internet-facing Worker.
|
|
91
|
+
|
|
92
|
+
## Process sessions
|
|
93
|
+
|
|
94
|
+
Process-session IDs are random and valid only inside one runtime. Output buffers and session counts are bounded. Sessions are killed on runtime stop or remote connection loss/replacement and expire from retained state after exit.
|
|
95
|
+
|
|
96
|
+
Sessions use pipes, not a PTY. Do not assume terminal-oriented programs will behave safely or correctly. Process output may contain secrets; it is returned to the authorized client but is not intentionally written to operational logs.
|
|
97
|
+
|
|
98
|
+
## Images and rich content
|
|
99
|
+
|
|
100
|
+
`view_image` accepts only signature-validated PNG, JPEG, GIF, and WebP under the size cap. SVG is intentionally excluded because it is active document content rather than a simple raster image. Image bytes pass through the Worker in remote mode and are visible to the authorized MCP client.
|
|
101
|
+
|
|
102
|
+
## Logs and privacy
|
|
103
|
+
|
|
104
|
+
Operational logs record coarse metadata and error classes, not tool arguments, command text, stdin, file/patch contents, or outputs. Unexpected Worker exceptions are reduced to error classes. Git author email is omitted from `git_log` unless explicitly requested.
|
|
105
|
+
|
|
106
|
+
No logging policy can prevent data from being returned to a client that explicitly invokes an enabled tool. The Worker necessarily relays remote tool arguments and results; this is not end-to-end encryption against the user's Cloudflare execution environment.
|
|
63
107
|
|
|
64
108
|
## Hardening checklist
|
|
65
109
|
|
|
66
|
-
For
|
|
110
|
+
For sensitive review:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
machine-mcp --workspace /narrow/project --profile review --no-print-credentials
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For controlled editing without execution:
|
|
67
117
|
|
|
68
|
-
```
|
|
69
|
-
machine-mcp --workspace /narrow/project --
|
|
118
|
+
```sh
|
|
119
|
+
machine-mcp --workspace /narrow/project --profile edit --no-print-credentials
|
|
70
120
|
```
|
|
71
121
|
|
|
72
122
|
Also:
|
|
73
123
|
|
|
74
|
-
-
|
|
75
|
-
- enable MFA on
|
|
76
|
-
- do not
|
|
77
|
-
- avoid `--full-env
|
|
124
|
+
- patch the OS and use supported Node.js releases;
|
|
125
|
+
- enable MFA on Cloudflare, GitHub, and npm accounts;
|
|
126
|
+
- do not configure broad CORS origins;
|
|
127
|
+
- avoid `--full-env`, `--unrestricted-paths`, and `--absolute-paths` unless required;
|
|
128
|
+
- inspect client names and OAuth redirect URIs;
|
|
78
129
|
- rotate secrets after suspected disclosure;
|
|
79
|
-
- inspect `
|
|
80
|
-
-
|
|
130
|
+
- inspect `status`, `doctor`, and service status;
|
|
131
|
+
- remove the Worker and state when remote access is no longer needed;
|
|
132
|
+
- use external OS isolation for untrusted code.
|
|
81
133
|
|
|
82
134
|
## Out of scope
|
|
83
135
|
|
|
84
|
-
The project cannot prevent an authorized
|
|
136
|
+
The project cannot prevent an authorized client from requesting data accessible to enabled tools, make arbitrary local executables safe, identify all sensitive content, or neutralize model prompt injection. Operator approval and narrow capability selection remain primary controls.
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,102 +1,169 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
+
## Design goal
|
|
4
|
+
|
|
5
|
+
The system separates three questions that are often conflated:
|
|
6
|
+
|
|
7
|
+
1. **Who may request a tool?** Remote OAuth or local process ownership.
|
|
8
|
+
2. **Which tools exist?** The selected local policy profile.
|
|
9
|
+
3. **What authority does a tool have?** Canonical workspace boundaries for direct filesystem tools and local-user authority for processes.
|
|
10
|
+
|
|
11
|
+
No transport is treated as a sandbox. Both transports invoke the same local runtime.
|
|
12
|
+
|
|
3
13
|
## Components
|
|
4
14
|
|
|
5
|
-
###
|
|
15
|
+
### Shared tool catalog
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
`src/shared/tool-catalog.json` is the single source of truth for tool names, descriptions, input schemas, annotations, and policy availability. The Worker and stdio runtime import it directly. A catalog test rejects duplicate names, unknown availability classes, open schemas, missing annotations, profile drift, and a second hand-maintained Worker catalog.
|
|
8
18
|
|
|
9
|
-
###
|
|
19
|
+
### CLI and state layer
|
|
20
|
+
|
|
21
|
+
The CLI canonicalizes workspaces, resolves policy profiles, maintains per-workspace state and credentials, serializes startup/deploy/rotation with locks, deploys the Worker, installs optional platform-native autostart, and starts either remote daemon or stdio mode.
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
A canonical workspace receives an independent profile, Worker name, secret set, daemon/startup locks, and state file. State schema version 3 removes obsolete local API state and records the expanded policy model.
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
- the active daemon WebSocket and a minimal attachment containing connection time, enabled tools, and policy booleans;
|
|
15
|
-
- the bounded in-memory map of pending daemon calls.
|
|
25
|
+
### Local runtime
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
`LocalDaemon` is transport-independent despite its historical name. It owns:
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
- canonical path resolution and display-path privacy;
|
|
30
|
+
- file, text search, image, patch, and Git operations;
|
|
31
|
+
- direct and shell process execution;
|
|
32
|
+
- process-session buffers and stdin lifecycle;
|
|
33
|
+
- mutation serialization;
|
|
34
|
+
- child-process tracking and cancellation;
|
|
35
|
+
- output, traversal, concurrency, and time limits.
|
|
20
36
|
|
|
21
|
-
|
|
37
|
+
Remote mode attaches WebSocket connection/reconnect behavior. Stdio mode invokes the same runtime directly.
|
|
22
38
|
|
|
23
|
-
|
|
39
|
+
### Stdio MCP server
|
|
24
40
|
|
|
25
|
-
|
|
41
|
+
The stdio server implements newline-delimited JSON-RPC over stdin/stdout. It negotiates supported MCP versions, advertises policy-filtered tools, returns text plus structured content, supports native image content, maps cancellation notifications to runtime call IDs, and sends logs only to stderr.
|
|
42
|
+
|
|
43
|
+
### Cloudflare Worker and Durable Object
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
All requests for a deployed Worker route to one named Durable Object. It owns:
|
|
46
|
+
|
|
47
|
+
- OAuth clients, authorization codes, hashed access-token records, and throttling metadata;
|
|
48
|
+
- one active authenticated daemon WebSocket plus bounded candidate sockets;
|
|
49
|
+
- policy/tool metadata attached to the active socket;
|
|
50
|
+
- a bounded in-memory map of pending daemon calls.
|
|
51
|
+
|
|
52
|
+
The Worker verifies OAuth, validates MCP envelopes and optional protocol headers, converts `tools/call` into WebSocket messages, correlates cancellation by access-token hash and JSON-RPC ID, and formats text/structured/image results. It has no local filesystem or process API.
|
|
53
|
+
|
|
54
|
+
The daemon attachment deliberately omits workspace path/name/hash and process ID. Explicit authenticated tools may return workspace metadata according to local path-display policy.
|
|
55
|
+
|
|
56
|
+
### Autostart layer
|
|
57
|
+
|
|
58
|
+
The service layer emits launchd, systemd-user, or Windows Scheduled Task definitions. Credentials are not embedded in service definitions; the daemon loads owner-only state. The exact policy is stored in owner-only state; platform service definitions contain only the workspace and state-root selectors.
|
|
28
59
|
|
|
29
60
|
## Trust boundaries
|
|
30
61
|
|
|
31
62
|
```mermaid
|
|
32
63
|
flowchart LR
|
|
33
|
-
C[Remote MCP client] -->|HTTPS + OAuth bearer token| W[Worker]
|
|
34
|
-
W -->|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
C[Remote MCP client] -->|HTTPS + OAuth bearer token| W[Worker / Durable Object]
|
|
65
|
+
W -->|authenticated bounded WebSocket calls| R[Local runtime]
|
|
66
|
+
L[Local MCP client] -->|stdio JSON-RPC| R
|
|
67
|
+
R -->|canonical workspace tools| F[Selected workspace]
|
|
68
|
+
R -->|optional direct/shell processes| P[Local user / OS / network]
|
|
69
|
+
CLI[CLI + state] --> W
|
|
70
|
+
CLI --> R
|
|
71
|
+
CLI --> S[Autostart provider]
|
|
41
72
|
```
|
|
42
73
|
|
|
43
|
-
|
|
74
|
+
Remote OAuth determines which client may call tools. Local stdio access relies on the local process and configuration boundary. Policy determines which tools are advertised. Canonical resolution limits direct filesystem tools. Processes retain local-user authority and can escape workspace constraints through their own code or system calls.
|
|
44
75
|
|
|
45
|
-
##
|
|
76
|
+
## Remote request lifecycle
|
|
46
77
|
|
|
47
|
-
1. The MCP client discovers
|
|
78
|
+
1. The MCP client discovers protected-resource and authorization-server metadata.
|
|
48
79
|
2. It dynamically registers bounded redirect metadata.
|
|
49
|
-
3. The
|
|
50
|
-
4. The user verifies
|
|
51
|
-
5.
|
|
52
|
-
6. A valid verifier exchanges the code for
|
|
53
|
-
7.
|
|
54
|
-
8.
|
|
55
|
-
9.
|
|
80
|
+
3. The Worker validates authorization parameters before displaying a password form.
|
|
81
|
+
4. The user verifies client name and redirect URI and enters the connection password.
|
|
82
|
+
5. The Worker creates a five-minute code bound to client, redirect, resource, scope, and PKCE challenge.
|
|
83
|
+
6. A valid verifier exchanges the one-time code for an expiring bearer token; only its hash is stored.
|
|
84
|
+
7. The MCP client initializes and negotiates a supported protocol version.
|
|
85
|
+
8. `tools/list` is derived from the active daemon handshake; without a daemon, only `server_info` is advertised.
|
|
86
|
+
9. `tools/call` receives a random relay call ID and is bound to the current socket and authenticated client request key.
|
|
87
|
+
10. The runtime validates policy and arguments, executes the tool, and returns a bounded result.
|
|
88
|
+
11. The Durable Object accepts a result only from the socket that received that call.
|
|
89
|
+
12. A matching cancellation notification removes the pending call, tells the daemon to cancel, and terminates any child processes bound to it.
|
|
90
|
+
|
|
91
|
+
Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cancellation cannot ambiguously target multiple calls.
|
|
92
|
+
|
|
93
|
+
## Stdio request lifecycle
|
|
94
|
+
|
|
95
|
+
1. The local client launches `machine-mcp stdio` with a workspace and profile.
|
|
96
|
+
2. The server negotiates one of the supported MCP versions.
|
|
97
|
+
3. Tool discovery is generated from the same catalog and policy used by remote mode.
|
|
98
|
+
4. Each call receives an internal random call ID used only for cancellation and process tracking.
|
|
99
|
+
5. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
|
|
100
|
+
6. Duplicate in-flight request IDs are rejected.
|
|
101
|
+
7. Closing stdin cancels pending calls, terminates active processes, and removes the isolated runtime directory.
|
|
102
|
+
|
|
103
|
+
## Filesystem resolution and privacy
|
|
104
|
+
|
|
105
|
+
The workspace is canonicalized once. Existing targets use `realpath`; the result must remain inside the workspace unless unrestricted mode is explicit. New targets walk to the nearest existing ancestor and validate its canonical path.
|
|
106
|
+
|
|
107
|
+
Path behavior is profile-dependent. The default `full` profile permits unrestricted direct filesystem paths and returns absolute paths. The `agent`, `edit`, and `review` profiles enforce canonical workspace containment and return workspace-relative paths. Error strings redact canonical and common platform-alias forms of workspace, runtime, and home paths whenever absolute path display is disabled.
|
|
108
|
+
|
|
109
|
+
Symbolic-link destinations and non-regular write targets are rejected. Recursive walkers do not follow symbolic-link directories.
|
|
110
|
+
|
|
111
|
+
## Mutation model
|
|
112
|
+
|
|
113
|
+
All bridge mutations are serialized in one runtime queue.
|
|
114
|
+
|
|
115
|
+
### Whole-file write
|
|
116
|
+
|
|
117
|
+
A same-directory temporary file is created with restrictive permissions. Existing mode is preserved when replacing a regular file. Expected hashes are rechecked immediately before commit. Create-only uses a hard-link commit that fails atomically if the destination appeared concurrently.
|
|
118
|
+
|
|
119
|
+
### Exact edit
|
|
120
|
+
|
|
121
|
+
The current UTF-8 content is hashed. The old fragment must exist and, unless `replace_all` is set, occur exactly once. The resulting file is bounded and committed only if the original hash is unchanged.
|
|
122
|
+
|
|
123
|
+
### Patch transaction
|
|
124
|
+
|
|
125
|
+
The patch parser accepts a strict Begin/End envelope with add, update, move, and delete operations. It rejects malformed hunks, absent or ambiguous context, duplicate textual paths, and different paths that resolve to the same filesystem location.
|
|
126
|
+
|
|
127
|
+
Before commit, all sources and targets are resolved and validated, updated content is computed, and temporary files are staged. Source hashes and destination absence are rechecked. Existing sources are renamed to backups, staged files are committed, and any failure rolls back committed operations in reverse order. Backups are deleted only after full success.
|
|
56
128
|
|
|
57
|
-
|
|
129
|
+
This is a process-level transaction, not a filesystem-wide atomic transaction across multiple directories. Sudden power loss can still interrupt a multi-file commit; retained backup/temp naming makes recovery identifiable.
|
|
58
130
|
|
|
59
|
-
##
|
|
131
|
+
## Process model
|
|
60
132
|
|
|
61
|
-
|
|
133
|
+
`run_process` and process sessions use argv arrays and do not invoke a shell. `exec_command` invokes the platform shell and is available only in `shell` mode. Both inherit local-user OS authority.
|
|
62
134
|
|
|
63
|
-
|
|
135
|
+
The default `full` profile passes the complete parent environment. Isolated environment mode, used by the narrower named profiles unless overridden, creates private runtime HOME, temporary, and cache directories and passes only a small set of path/locale/platform variables. It reduces accidental credential inheritance but cannot prevent explicit access to known filesystem paths, credential stores, network services, or other user resources.
|
|
64
136
|
|
|
65
|
-
|
|
137
|
+
One-shot calls have bounded output and timeouts. Process sessions retain bounded byte buffers with monotonic offsets, accept bounded stdin, support short output/exit waits, and are capped per runtime. Valid UTF-8 is returned as text; byte slices that are not valid UTF-8 also include lossless base64 data. Session IDs are random. Sessions are memory-only and are killed on runtime stop, remote disconnect, or daemon replacement.
|
|
66
138
|
|
|
67
|
-
|
|
139
|
+
Child processes run in a separate process group where supported. Timeout, cancellation, disconnect, and replacement send termination to the process tree, with forced escalation for timeout/disconnect paths.
|
|
68
140
|
|
|
69
|
-
##
|
|
141
|
+
## Daemon reconnect and replacement
|
|
70
142
|
|
|
71
|
-
|
|
143
|
+
The daemon sends heartbeats and reconnects with bounded exponential backoff and jitter. Only one socket is active. A new connection is a bounded candidate until it authenticates and completes `hello`; a Durable Object alarm enforces the deadline across hibernation. Only a completed candidate replaces the old socket.
|
|
72
144
|
|
|
73
|
-
|
|
74
|
-
- OAuth body: 64 KiB.
|
|
75
|
-
- Local WebSocket message: 8 MiB.
|
|
76
|
-
- `write_file` content: 5 MiB.
|
|
77
|
-
- `exec_command` command text: 64 KiB.
|
|
78
|
-
- Direct directory results: 10,000 entries and 4 MiB of path metadata.
|
|
79
|
-
- Recursive walks: 200,000 visited entries; path-list results are capped at 4 MiB.
|
|
80
|
-
- Captured process output: 512 KiB per stream by default.
|
|
81
|
-
- Local simultaneous tool calls: 16.
|
|
82
|
-
- Worker pending daemon calls: 32.
|
|
83
|
-
- Command timeout: 1-600 seconds.
|
|
84
|
-
- Dynamic clients, redirect URIs, codes, tokens, failed login identities, and per-client records: bounded constants in Worker source.
|
|
145
|
+
Pending calls retain their originating socket reference. A stale socket cannot complete or cancel replacement-socket calls. Closing a socket rejects only calls assigned to it.
|
|
85
146
|
|
|
86
147
|
## Persistence
|
|
87
148
|
|
|
88
|
-
|
|
149
|
+
Local state and global config are owner-only, versioned, and written through temporary files, flushes, and atomic rename. Malformed state becomes a bounded `.corrupt-*` backup. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
89
150
|
|
|
90
|
-
|
|
151
|
+
Removal validates the state marker, canonical target, known contents, active locks, filesystem root/home/current/package/workspace/source exclusions, and Worker deletion outcome before recursive deletion.
|
|
91
152
|
|
|
92
|
-
|
|
153
|
+
OAuth metadata is pruned on access. Expired codes/tokens, old throttling records, and inactive clients without active credentials are removed. Source identities are deployment-keyed HMAC values, not stored source addresses or reversible unsalted hashes.
|
|
93
154
|
|
|
94
|
-
## Observability
|
|
155
|
+
## Observability
|
|
95
156
|
|
|
96
|
-
Public health
|
|
157
|
+
Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status. Local operational logs record tool identity, shortened random call ID, duration, outcome, and coarse error class, but not arguments or outputs. Worker unexpected-error logs contain endpoint and error class, not raw exception text.
|
|
97
158
|
|
|
98
|
-
|
|
159
|
+
Cloudflare sampling is size control rather than an audit log. The project intentionally does not claim complete forensic logging.
|
|
99
160
|
|
|
100
|
-
##
|
|
161
|
+
## Explicit non-goals
|
|
101
162
|
|
|
102
|
-
|
|
163
|
+
- operating-system sandboxing of arbitrary executables;
|
|
164
|
+
- preventing an authorized client from requesting data available to enabled tools;
|
|
165
|
+
- automatically deciding which workspace files are sensitive;
|
|
166
|
+
- surviving daemon restart with process sessions;
|
|
167
|
+
- PTY/terminal emulation;
|
|
168
|
+
- model-level prompt-injection prevention;
|
|
169
|
+
- multi-user tenancy in one Worker deployment.
|
package/docs/CLIENTS.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Client configuration and model ownership
|
|
2
|
+
|
|
3
|
+
## The central distinction
|
|
4
|
+
|
|
5
|
+
An MCP server is not a model provider. It exposes tools, resources, or prompts. The MCP host is the AI application that owns the conversation, chooses or supplies the model, decides when a tool should be called, sends the tool request, and adds the result back to the model context.
|
|
6
|
+
|
|
7
|
+
For this project:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
Claude Desktop / Cursor / Codex / ChatGPT Desktop / another MCP host
|
|
11
|
+
owns the model, account, conversation, approvals, and tool-selection loop
|
|
12
|
+
|
|
|
13
|
+
| stdio or Streamable HTTP
|
|
14
|
+
v
|
|
15
|
+
machine-bridge-mcp
|
|
16
|
+
exposes local file, Git, image, patch, and process tools
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A local stdio connection does **not** use the model running in a separate ChatGPT web conversation. It uses whatever model/session the local host is configured to use:
|
|
20
|
+
|
|
21
|
+
- Claude Desktop uses the Claude account/model available to that application.
|
|
22
|
+
- Cursor uses the model/provider configured in Cursor.
|
|
23
|
+
- Codex CLI and the Codex IDE integration use their own authenticated Codex/OpenAI configuration.
|
|
24
|
+
- ChatGPT Desktop can use its signed-in ChatGPT/Codex host session and can launch local stdio servers on supported hosts.
|
|
25
|
+
|
|
26
|
+
The MCP protocol itself intentionally does not dictate how the host obtains an LLM. The optional MCP sampling primitive can let a server ask a capable host for a completion, but `machine-bridge-mcp` does not depend on sampling and does not bundle a model SDK.
|
|
27
|
+
|
|
28
|
+
## Why keep stdio when coding clients already have tools?
|
|
29
|
+
|
|
30
|
+
For many users, the client's native filesystem, patch, terminal, and approval system is sufficient. In that case, adding Machine Bridge over stdio is redundant and should be skipped.
|
|
31
|
+
|
|
32
|
+
The stdio mode exists for narrower cases:
|
|
33
|
+
|
|
34
|
+
1. **One tool contract across several clients.** The same tool names, schemas, patch behavior, Git handling, process sessions, result formats, and logging work in Claude Desktop, Cursor, Codex, ChatGPT Desktop, and other MCP hosts.
|
|
35
|
+
2. **Transport parity.** The local client can use essentially the same Machine Bridge runtime that remote ChatGPT access uses, without maintaining a second implementation.
|
|
36
|
+
3. **No remote relay for same-machine use.** The host launches the server directly, so no Cloudflare Worker, public URL, OAuth flow, or network round trip is needed.
|
|
37
|
+
4. **Capabilities missing from a particular host.** Exact edit semantics, transactional multi-file patches, retained process sessions, image results, or the project's policy profiles may be useful even when the host has basic shell tools.
|
|
38
|
+
5. **Testing and interoperability.** stdio is a standard MCP transport and provides a direct way to validate the runtime independently of the Worker.
|
|
39
|
+
|
|
40
|
+
It is therefore an optional compatibility and reuse surface, not a replacement for the native agent tooling in Claude Desktop, Cursor, or Codex.
|
|
41
|
+
|
|
42
|
+
## Choosing a transport
|
|
43
|
+
|
|
44
|
+
| Situation | Recommended path |
|
|
45
|
+
|---|---|
|
|
46
|
+
| ChatGPT web or another service must reach this computer remotely | Remote Worker with HTTPS/OAuth |
|
|
47
|
+
| A local MCP host needs Machine Bridge-specific tools | stdio |
|
|
48
|
+
| The local coding client already provides everything needed | Use the client's native tools; do not configure Machine Bridge stdio |
|
|
49
|
+
| Several local clients should share identical tool semantics | stdio |
|
|
50
|
+
| Access is required from another device or hosted agent | Remote Worker |
|
|
51
|
+
|
|
52
|
+
The MCP specification defines stdio and Streamable HTTP as standard transports. In stdio, the host launches the server as a subprocess and exchanges newline-delimited JSON-RPC through stdin/stdout. Streamable HTTP runs the server independently behind an HTTP endpoint.
|
|
53
|
+
|
|
54
|
+
## Generate local configuration
|
|
55
|
+
|
|
56
|
+
The default profile is `full`:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
machine-mcp client-config --client all --workspace /path/to/project
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The generated command uses the current Node executable and installed entry script as absolute paths so it does not depend on a GUI application's `PATH`.
|
|
63
|
+
|
|
64
|
+
To reduce authority:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
machine-mcp client-config --client all --workspace /path/to/project --profile agent
|
|
68
|
+
machine-mcp client-config --client all --workspace /path/to/project --profile edit
|
|
69
|
+
machine-mcp client-config --client all --workspace /path/to/project --profile review
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## JSON stdio configuration
|
|
73
|
+
|
|
74
|
+
Claude Desktop, Cursor, and many generic clients use a shape similar to:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"machine-bridge": {
|
|
80
|
+
"command": "/absolute/path/to/node",
|
|
81
|
+
"args": [
|
|
82
|
+
"/absolute/path/to/machine-mcp.mjs",
|
|
83
|
+
"stdio",
|
|
84
|
+
"--workspace",
|
|
85
|
+
"/path/to/project",
|
|
86
|
+
"--profile",
|
|
87
|
+
"full"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Use the exact output of `client-config`; application-specific configuration locations and UI flows can change.
|
|
95
|
+
|
|
96
|
+
## Codex configuration
|
|
97
|
+
|
|
98
|
+
Codex supports both local stdio servers and Streamable HTTP servers. A local configuration is:
|
|
99
|
+
|
|
100
|
+
```toml
|
|
101
|
+
[mcp_servers.machine_bridge]
|
|
102
|
+
command = "/absolute/path/to/node"
|
|
103
|
+
args = ["/absolute/path/to/machine-mcp.mjs", "stdio", "--workspace", "/path/to/project", "--profile", "full"]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Codex CLI, the Codex IDE extension, and supported ChatGPT Desktop Codex hosts can share MCP configuration on the same Codex host. ChatGPT web does not read local Codex configuration files; web use requires a hosted plugin/connector or a reachable remote MCP endpoint.
|
|
107
|
+
|
|
108
|
+
## Remote ChatGPT connection
|
|
109
|
+
|
|
110
|
+
Run:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
machine-mcp --workspace /path/to/project
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Enter the printed `/mcp` URL in the remote MCP connector. During OAuth authorization, verify the displayed client name and redirect URI before entering the connection password.
|
|
117
|
+
|
|
118
|
+
## Profile guidance
|
|
119
|
+
|
|
120
|
+
- `full` is the default and prioritizes immediate usability. It exposes shell execution, unrestricted direct filesystem paths, absolute path output, and the full parent environment.
|
|
121
|
+
- `agent` retains file mutation and direct process execution but removes shell parsing, confines direct filesystem tools to the workspace, and isolates the process environment.
|
|
122
|
+
- `edit` permits deterministic file mutation without process execution.
|
|
123
|
+
- `review` is read-only and workspace-confined.
|
|
124
|
+
|
|
125
|
+
A client configuration is an authorization decision. Anyone who controls an authorized host can invoke every tool exposed by the selected profile.
|