machine-bridge-mcp 0.4.0 → 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 CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.2 - 2026-07-10
4
+
5
+ ### Fixed
6
+
7
+ - Canonicalize the configured workspace and requested targets through the same asynchronous `realpath` path on Windows, while retaining a native synchronous initial value. This prevents Windows short-path/long-path aliases for the same temporary directory from being misclassified as a workspace escape.
8
+ - Keep workspace confinement unchanged: only the representation comparison changed, and targets outside the canonical workspace remain rejected in restricted profiles.
9
+
10
+ ### Verification
11
+
12
+ - Preserve the existing cross-platform path-boundary regression, which exposed the issue on the Windows GitHub Actions runner while Linux and macOS passed.
13
+ - Make the minimal-environment shell regression use a cross-platform Node command instead of POSIX parameter-expansion syntax under PowerShell.
14
+ - Convert test entry-point file URLs with `fileURLToPath()` so Windows does not interpret `/D:/...` URL paths as `C:\D:\...` filesystem paths.
15
+ - Launch Wrangler's JavaScript entry point through the active Node executable in integration tests instead of directly spawning the Windows `.cmd` shim.
16
+
17
+ ## 0.4.1 - 2026-07-10
18
+
19
+ ### Changed
20
+
21
+ - Make `full` the default policy for newly selected workspaces and generated client configurations, prioritizing immediate usability. The default now enables all tools, unrestricted direct filesystem paths, absolute path output, shell execution, process sessions, and the complete parent environment; existing saved workspace policies remain unchanged.
22
+ - Reframe stdio as an optional local transport rather than a model provider or a replacement for native Claude, Cursor, Codex, or ChatGPT Desktop tooling. Expand client documentation to distinguish the MCP host/model from the Machine Bridge tool server and explain when stdio is redundant or useful.
23
+ - Activate cross-platform GitHub Actions checks on Linux, macOS, and Windows, update official actions to the current major releases, disable checkout credential persistence, and retain production dependency/package auditing.
24
+
25
+ ### Tests and documentation
26
+
27
+ - Add regression coverage for the maximum-permission default and for `client-config` emitting `full` when no profile is supplied.
28
+ - Update architecture, operations, security, and client guidance for profile-dependent path display, filesystem scope, and environment inheritance.
29
+
3
30
  ## 0.4.0 - 2026-07-10
4
31
 
5
32
  ### Architecture and compatibility
package/README.md CHANGED
@@ -23,27 +23,25 @@ Local clients such as Claude Desktop, Cursor, and Codex CLI
23
23
 
24
24
  The remote Worker authenticates and relays calls. It cannot directly read local files or start local processes. File, Git, image, patch, and process operations execute in the local runtime.
25
25
 
26
- ## Security posture first
26
+ ## Default behavior and policy profiles
27
27
 
28
- A new workspace starts with the `review` profile:
28
+ A newly selected workspace starts with the maximum-permission `full` profile for low-friction operation:
29
29
 
30
- - read-only filesystem and Git tools;
31
- - no file mutation;
32
- - no process execution;
33
- - workspace-confined filesystem access;
34
- - relative paths in tool results;
35
- - no parent shell environment inherited by commands.
30
+ - all read, write, edit, patch, image, Git, direct-process, process-session, and shell tools are available;
31
+ - direct filesystem tools may use paths outside the selected workspace;
32
+ - tool results may return absolute paths;
33
+ - child processes inherit the complete parent environment.
36
34
 
37
- Existing pre-0.4 workspace profiles keep their saved permissions during upgrade. Select a profile explicitly to change them.
35
+ Existing workspace state keeps its saved policy during upgrade. Use `--profile full` to explicitly move an older workspace to the new maximum-permission behavior.
38
36
 
39
- | Profile | File edits | Direct argv processes | Shell commands | Intended use |
40
- |---|---:|---:|---:|---|
41
- | `review` | No | No | No | Inspection and review |
42
- | `edit` | Yes | No | No | Controlled file changes |
43
- | `agent` | Yes | Yes | No | Coding agents and test commands |
44
- | `full` | Yes | Yes | Yes | Deliberate full local automation |
37
+ | Profile | File edits | Direct argv processes | Shell commands | Filesystem scope | Process environment |
38
+ |---|---:|---:|---:|---|---|
39
+ | `full` | Yes | Yes | Yes | Unrestricted | Full parent environment |
40
+ | `agent` | Yes | Yes | No | Selected workspace | Isolated environment |
41
+ | `edit` | Yes | No | No | Selected workspace | Isolated environment |
42
+ | `review` | No | No | No | Selected workspace | Isolated environment |
45
43
 
46
- `run_process` and process sessions avoid command-shell parsing, but they are **not an operating-system sandbox**. An allowed executable can still access anything available to the local user. `exec_command` is more exposed because it additionally permits shell syntax and expansion. Use a container, VM, or dedicated low-privilege OS account for hostile repositories or untrusted instructions.
44
+ This default prioritizes usability, not least privilege. `run_process` and process sessions avoid command-shell parsing, but they are **not an operating-system sandbox**. `exec_command` additionally permits shell syntax and expansion. Use `--profile review`, `edit`, or `agent`, or use a container, VM, or dedicated low-privilege OS account when the client, repository, or instructions are not fully trusted.
47
45
 
48
46
  ## Install
49
47
 
@@ -66,7 +64,7 @@ npm install
66
64
  Start the bridge from the project directory or select a workspace explicitly:
67
65
 
68
66
  ```sh
69
- machine-mcp --workspace /path/to/project --profile review
67
+ machine-mcp --workspace /path/to/project
70
68
  ```
71
69
 
72
70
  On first remote start, the CLI:
@@ -88,30 +86,32 @@ MCP connection password: mcp_password_...
88
86
 
89
87
  The remote authorization flow uses an authorization code, PKCE S256, exact redirect/resource binding, expiring access tokens stored as hashes, and a token-version value for bulk revocation.
90
88
 
91
- ## Local stdio MCP for Claude, Cursor, Codex, and compatible clients
89
+ ## Optional local stdio MCP
90
+
91
+ stdio is a local transport, not a model provider. Claude Desktop, Cursor, Codex CLI, ChatGPT Desktop, or another MCP host supplies its own model/session and launches `machine-bridge-mcp` as a subprocess. The MCP server only exposes tools and returns their results; it does not borrow the model running in ChatGPT web.
92
+
93
+ Many coding clients already have strong native filesystem and terminal tools, so configuring this stdio server is optional. It is useful when you want the same Machine Bridge tool schemas, patch behavior, process sessions, policy profiles, and logs across several clients, or when you want local access without deploying Cloudflare. If the client's built-in tools already meet your needs, there is no reason to add this server.
92
94
 
93
95
  Generate ready-to-paste configuration:
94
96
 
95
97
  ```sh
96
- machine-mcp client-config --client all --workspace /path/to/project --profile agent
98
+ machine-mcp client-config --client all --workspace /path/to/project
97
99
  ```
98
100
 
99
101
  Or run stdio directly:
100
102
 
101
103
  ```sh
102
- machine-mcp stdio --workspace /path/to/project --profile agent
104
+ machine-mcp stdio --workspace /path/to/project
103
105
  ```
104
106
 
105
- The stdio server writes only JSON-RPC messages to stdout. Operational logs go to stderr. It supports MCP initialization/version negotiation, tool discovery, calls, cancellation, structured tool output, native image content, and process sessions.
106
-
107
- See [docs/CLIENTS.md](docs/CLIENTS.md) for client-specific configuration and remote/local trade-offs.
107
+ The stdio server writes only JSON-RPC messages to stdout and operational logs to stderr. See [docs/CLIENTS.md](docs/CLIENTS.md) for the host/model distinction and transport trade-offs.
108
108
 
109
109
  ## Policy controls
110
110
 
111
- Profiles can be narrowed with explicit flags:
111
+ The default is `full`. Narrow or customize it with explicit flags:
112
112
 
113
113
  ```text
114
- --profile review|edit|agent|full
114
+ --profile full|agent|edit|review
115
115
  --exec-mode off|direct|shell
116
116
  --no-write
117
117
  --no-exec
@@ -122,9 +122,10 @@ Profiles can be narrowed with explicit flags:
122
122
 
123
123
  Important distinctions:
124
124
 
125
- - `--unrestricted-paths` expands direct filesystem tools beyond the selected workspace.
126
- - `--absolute-paths` changes returned path metadata; it does not grant additional access.
127
- - `--full-env` passes the complete parent environment to processes. Without it, commands receive an isolated HOME, temp directory, and cache directories plus a small set of platform variables.
125
+ - The default `full` profile already enables unrestricted paths, absolute path output, and the complete parent environment.
126
+ - `--unrestricted-paths=false`, `--absolute-paths=false`, and `--full-env=false` can narrow those individual settings.
127
+ - `--absolute-paths` changes returned path metadata; it does not independently grant additional access.
128
+ - In isolated-environment profiles, commands receive private HOME, temp, and cache directories plus a small set of platform variables.
128
129
  - Files with sensitive-looking names are not automatically blocked inside the workspace. A workspace `.env` remains readable when read tools are enabled.
129
130
 
130
131
  ## Tools
@@ -170,11 +171,11 @@ Process sessions retain bounded stdout/stderr, support offsets and short waits,
170
171
 
171
172
  ## Path and write behavior
172
173
 
173
- By default, existing paths are resolved with `realpath` and must remain inside the canonical workspace. New write paths validate the nearest existing ancestor, preventing missing-path writes through escaping symbolic-link directories.
174
+ When workspace confinement is enabled (`agent`, `edit`, `review`, or an explicit override), existing paths are resolved with `realpath` and must remain inside the canonical workspace. New write paths validate the nearest existing ancestor, preventing missing-path writes through escaping symbolic-link directories. The default `full` profile permits direct filesystem paths outside the workspace.
174
175
 
175
176
  Writes use same-directory temporary files and atomic commit. Create-only writes use an atomic hard-link commit so a concurrent file cannot be silently overwritten. Patch operations are prevalidated, serialized, staged, rechecked, committed with backups, and rolled back on failure.
176
177
 
177
- Returned paths are workspace-relative by default. This reduces unnecessary disclosure of usernames and local directory layouts. Enable `--absolute-paths` only when a client genuinely needs absolute paths.
178
+ The default `full` profile returns absolute paths. The `agent`, `edit`, and `review` profiles return workspace-relative paths to reduce unnecessary disclosure of usernames and local directory layouts.
178
179
 
179
180
  ## Commands
180
181
 
@@ -200,7 +201,7 @@ Remote mode supports:
200
201
  - Linux `systemd --user`, with best-effort lingering;
201
202
  - Windows Scheduled Task at logon.
202
203
 
203
- The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and fails closed to the `review` profile if policy state is absent. Service logs are owner-only where supported and trimmed before daemon startup.
204
+ The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and uses the documented `full` default if policy state is absent. Service logs are owner-only where supported and trimmed before daemon startup.
204
205
 
205
206
  ## Secret rotation
206
207
 
@@ -233,7 +234,7 @@ npm audit --omit=dev --audit-level=high
233
234
  npm pack --dry-run
234
235
  ```
235
236
 
236
- `npm run check` covers generated Worker types, TypeScript, JavaScript syntax, the shared tool catalog, local path/write/process/state/log/service invariants, a live stdio MCP flow, and a live local OAuth/Worker/WebSocket/MCP flow. A ready-to-enable GitHub Actions template is included at `docs/examples/github-actions-ci.yml` for Linux, macOS, and Windows with supported Node versions. Activating it requires a GitHub credential authorized to modify workflow files.
237
+ `npm run check` covers generated Worker types, TypeScript, JavaScript syntax, the shared tool catalog, local path/write/process/state/log/service invariants, a live stdio MCP flow, and a live local OAuth/Worker/WebSocket/MCP flow. GitHub Actions runs the suite on Linux, macOS, and Windows with supported Node versions.
237
238
 
238
239
  See [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), and [SECURITY.md](SECURITY.md).
239
240
 
package/SECURITY.md CHANGED
@@ -32,10 +32,12 @@ The Worker is a remote authentication and relay boundary. The local runtime is t
32
32
 
33
33
  ## Profiles are capability sets, not sandboxes
34
34
 
35
+ The default for newly selected workspaces is `full`, which prioritizes ease of use over least privilege.
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.
35
40
  - `review` exposes read-only workspace/Git/image tools.
36
- - `edit` additionally exposes write, exact edit, and patch tools.
37
- - `agent` additionally exposes direct argv execution and process sessions.
38
- - `full` additionally exposes arbitrary shell commands.
39
41
 
40
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.
41
43
 
@@ -45,9 +47,9 @@ For untrusted repositories or instructions, run the bridge inside a disposable V
45
47
 
46
48
  ## Filesystem exposure
47
49
 
48
- Direct filesystem tools are confined to the canonical selected workspace by default, including symbolic-link resolution. `--unrestricted-paths` expands this boundary and should be used only deliberately.
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.
49
51
 
50
- Returned paths are relative by default to reduce username and directory-layout disclosure. `--absolute-paths` changes metadata exposure without increasing direct access authority.
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.
51
53
 
52
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.
53
55
 
@@ -67,7 +69,7 @@ Local state contains the MCP connection password and daemon secret. State, lock,
67
69
 
68
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.
69
71
 
70
- Minimal process environment replaces HOME, temp, and common cache paths and does not pass arbitrary parent variables. It reduces accidental environment-secret leakage; it does not prevent code from explicitly accessing known resources. `--full-env` materially increases exposure.
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.
71
73
 
72
74
  ## OAuth and public endpoints
73
75
 
@@ -104,7 +104,7 @@ Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cance
104
104
 
105
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
106
 
107
- Results use workspace-relative paths by default. Error strings redact canonical and common platform-alias forms of workspace, runtime, and home paths. Absolute paths are available only through an explicit display policy or unrestricted mode.
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
108
 
109
109
  Symbolic-link destinations and non-regular write targets are rejected. Recursive walkers do not follow symbolic-link directories.
110
110
 
@@ -132,7 +132,7 @@ This is a process-level transaction, not a filesystem-wide atomic transaction ac
132
132
 
133
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.
134
134
 
135
- Minimal environment mode 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.
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.
136
136
 
137
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.
138
138
 
package/docs/CLIENTS.md CHANGED
@@ -1,24 +1,73 @@
1
- # Client configuration
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.
2
41
 
3
42
  ## Choosing a transport
4
43
 
5
- Use remote HTTPS/OAuth when the client cannot launch a local stdio process or must reach the machine from another device. Use stdio when the client runs on the same machine and supports local MCP processes.
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.
6
53
 
7
- | Client class | Recommended transport | Reason |
8
- |---|---|---|
9
- | ChatGPT remote connector / Developer Mode | Remote Worker | Public HTTPS endpoint and OAuth authorization |
10
- | Claude Desktop | stdio | Local process transport; no cloud relay required |
11
- | Cursor | stdio | Local process transport and direct workspace lifecycle |
12
- | Codex CLI | stdio | Local MCP server configuration |
13
- | Other MCP clients | stdio when local; remote when necessary | Minimize the trust and network boundary |
54
+ ## Generate local configuration
14
55
 
15
- ## Generate configuration
56
+ The default profile is `full`:
16
57
 
17
58
  ```sh
18
- machine-mcp client-config --client all --workspace /path/to/project --profile agent
59
+ machine-mcp client-config --client all --workspace /path/to/project
19
60
  ```
20
61
 
21
- The generated command uses the current Node executable and installed entry script as absolute paths so it does not depend on GUI application PATH configuration.
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
+ ```
22
71
 
23
72
  ## JSON stdio configuration
24
73
 
@@ -35,38 +84,42 @@ Claude Desktop, Cursor, and many generic clients use a shape similar to:
35
84
  "--workspace",
36
85
  "/path/to/project",
37
86
  "--profile",
38
- "agent"
87
+ "full"
39
88
  ]
40
89
  }
41
90
  }
42
91
  }
43
92
  ```
44
93
 
45
- Use the exact output of `client-config`; application-specific configuration locations change over time.
94
+ Use the exact output of `client-config`; application-specific configuration locations and UI flows can change.
46
95
 
47
- ## Codex CLI TOML
96
+ ## Codex configuration
97
+
98
+ Codex supports both local stdio servers and Streamable HTTP servers. A local configuration is:
48
99
 
49
100
  ```toml
50
101
  [mcp_servers.machine_bridge]
51
102
  command = "/absolute/path/to/node"
52
- args = ["/absolute/path/to/machine-mcp.mjs", "stdio", "--workspace", "/path/to/project", "--profile", "agent"]
103
+ args = ["/absolute/path/to/machine-mcp.mjs", "stdio", "--workspace", "/path/to/project", "--profile", "full"]
53
104
  ```
54
105
 
55
- ## ChatGPT remote connection
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
56
109
 
57
110
  Run:
58
111
 
59
112
  ```sh
60
- machine-mcp --workspace /path/to/project --profile review
113
+ machine-mcp --workspace /path/to/project
61
114
  ```
62
115
 
63
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.
64
117
 
65
118
  ## Profile guidance
66
119
 
67
- - Begin with `review` for an unfamiliar client or repository.
68
- - Use `edit` when the client needs deterministic file mutation but no process execution.
69
- - Use `agent` for normal coding work. It permits direct executables and process sessions, which remain powerful.
70
- - Use `full` only when shell syntax is required and the client/instructions are trusted.
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.
71
124
 
72
- A client configuration is an authorization decision. Anyone who controls an authorized client can invoke every tool exposed by its selected profile.
125
+ A client configuration is an authorization decision. Anyone who controls an authorized host can invoke every tool exposed by the selected profile.
@@ -62,12 +62,14 @@ Defense-in-depth limits include:
62
62
 
63
63
  ## Upgrade behavior
64
64
 
65
- Version 0.4 changes the default for newly selected workspaces to `review`. Existing state retains its previous write and execution settings. To intentionally migrate an old profile:
65
+ Version 0.4.1 changes the default for newly selected workspaces to the maximum-permission `full` profile. Existing state retains its saved policy. To intentionally migrate an older workspace to the new default:
66
66
 
67
67
  ```sh
68
- machine-mcp --workspace /path/to/project --profile agent
68
+ machine-mcp --workspace /path/to/project --profile full
69
69
  ```
70
70
 
71
+ `full` enables writes, direct processes, process sessions, shell execution, unrestricted direct filesystem paths, absolute path output, and the complete parent environment. Use `agent`, `edit`, or `review` when that authority is unnecessary.
72
+
71
73
  A remote policy change is saved locally, propagated in the daemon handshake, and persisted in the autostart definition.
72
74
 
73
75
  ## Incident response
package/docs/TESTING.md CHANGED
@@ -39,7 +39,7 @@ npm pack --dry-run
39
39
  npm run version:check
40
40
  ```
41
41
 
42
- The repository includes `docs/examples/github-actions-ci.yml`, a ready-to-enable matrix for Linux, macOS, and Windows. Node 22 and 24 are covered on Linux; Node 22 is covered on macOS and Windows. It must be copied to `.github/workflows/ci.yml` with a GitHub credential authorized to modify workflow files. Dependency and GitHub Actions updates are monitored by Dependabot after activation.
42
+ GitHub Actions executes the main suite on Linux, macOS, and Windows. Node 22 and 24 are covered on Linux; Node 22 is covered on macOS and Windows. A separate package-audit job runs production dependency auditing and a dry-run package build. Dependency and GitHub Actions updates are monitored by Dependabot.
43
43
 
44
44
  ## Test design rules
45
45
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "machine-bridge-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Cross-client MCP bridge for policy-controlled local files, Git, images, patches, and processes over stdio or OAuth-protected remote relay.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/local/cli.mjs CHANGED
@@ -351,7 +351,7 @@ const POLICY_PROFILES = Object.freeze({
351
351
  review: Object.freeze({ profile: "review", allowWrite: false, execMode: "off", unrestrictedPaths: false, minimalEnv: true, exposeAbsolutePaths: false }),
352
352
  edit: Object.freeze({ profile: "edit", allowWrite: true, execMode: "off", unrestrictedPaths: false, minimalEnv: true, exposeAbsolutePaths: false }),
353
353
  agent: Object.freeze({ profile: "agent", allowWrite: true, execMode: "direct", unrestrictedPaths: false, minimalEnv: true, exposeAbsolutePaths: false }),
354
- full: Object.freeze({ profile: "full", allowWrite: true, execMode: "shell", unrestrictedPaths: false, minimalEnv: true, exposeAbsolutePaths: false }),
354
+ full: Object.freeze({ profile: "full", allowWrite: true, execMode: "shell", unrestrictedPaths: true, minimalEnv: false, exposeAbsolutePaths: true }),
355
355
  });
356
356
 
357
357
  export function resolvePolicy(args = {}, stored = {}) {
@@ -368,7 +368,7 @@ export function resolvePolicy(args = {}, stored = {}) {
368
368
  } else if (hasStored) {
369
369
  base = normalizePolicy(stored);
370
370
  } else {
371
- base = { ...POLICY_PROFILES.review };
371
+ base = { ...POLICY_PROFILES.full };
372
372
  }
373
373
 
374
374
  if (!hasExplicit) return normalizePolicy(base);
@@ -404,7 +404,7 @@ async function clientConfigCommand(args) {
404
404
  const workspaceArgs = { ...args, _: [] };
405
405
  const workspace = await chooseWorkspace(workspaceArgs, { promptOnFirstRun: false, save: false, allowPositional: false });
406
406
  const requested = String(args.client || args._[0] || "all").trim().toLowerCase();
407
- const profile = String(args.profile || "agent").trim().toLowerCase();
407
+ const profile = String(args.profile || "full").trim().toLowerCase();
408
408
  if (!POLICY_PROFILES[profile]) throw new Error(`--profile must be one of: ${Object.keys(POLICY_PROFILES).join(", ")}`);
409
409
  if (!["all", "claude", "cursor", "codex", "generic"].includes(requested)) throw new Error("client must be all, claude, cursor, codex, or generic");
410
410
  const command = process.execPath;
@@ -940,7 +940,7 @@ Start options:
940
940
  --no-autostart Do not install login autostart during start
941
941
  --no-print-credentials Redact credentials in console output
942
942
  --print-mcp-credentials Print MCP URL/password again for reconnecting ChatGPT apps
943
- --profile NAME Policy profile: review (default for new workspaces), edit, agent, or full
943
+ --profile NAME Policy profile: full (default), agent, edit, or review
944
944
  --exec-mode MODE Command mode: off, direct argv, or full shell
945
945
  --no-write Disable write_file, edit_file, and apply_patch
946
946
  --no-exec Disable run_process and exec_command
@@ -25,7 +25,8 @@ export class LocalDaemon {
25
25
  if (this.workerUrl && (typeof secret !== "string" || secret.length < 16)) throw new Error("daemon secret is missing or too short");
26
26
  this.secret = secret || "";
27
27
  this.workspaceInput = resolve(workspace || process.cwd());
28
- this.workspace = realpathSync(this.workspaceInput);
28
+ this.workspace = realpathSync.native ? realpathSync.native(this.workspaceInput) : realpathSync(this.workspaceInput);
29
+ this.workspaceCanonicalPromise = null;
29
30
  this.policy = normalizePolicy(policy);
30
31
  this.logger = logger;
31
32
  this.onSuperseded = typeof onSuperseded === "function" ? onSuperseded : null;
@@ -751,28 +752,45 @@ export class LocalDaemon {
751
752
  resolvePath(inputPath = ".") {
752
753
  const raw = String(inputPath || ".");
753
754
  if (raw.includes("\0")) throw new Error("path contains a NUL byte");
754
- return isAbsolute(raw) ? resolve(raw) : resolve(this.workspace, raw);
755
+ return isAbsolute(raw) ? resolve(raw) : resolve(this.workspaceInput, raw);
756
+ }
757
+
758
+ async canonicalWorkspace() {
759
+ if (!this.workspaceCanonicalPromise) {
760
+ this.workspaceCanonicalPromise = realpath(this.workspaceInput).then((canonical) => {
761
+ this.workspace = canonical;
762
+ this.processSessionManager.workspace = canonical;
763
+ return canonical;
764
+ }).catch((error) => {
765
+ this.workspaceCanonicalPromise = null;
766
+ throw error;
767
+ });
768
+ }
769
+ return this.workspaceCanonicalPromise;
755
770
  }
756
771
 
757
772
  async resolveExistingPath(inputPath = ".") {
758
773
  const candidate = this.resolvePath(inputPath);
759
- const canonical = await realpath(candidate);
760
- if (!this.policy.unrestrictedPaths) assertContainedPath(this.workspace, canonical);
774
+ const [workspace, canonical] = await Promise.all([this.canonicalWorkspace(), realpath(candidate)]);
775
+ if (!this.policy.unrestrictedPaths) assertContainedPath(workspace, canonical);
761
776
  return canonical;
762
777
  }
763
778
 
764
779
  async resolveWritePath(inputPath = ".") {
765
780
  const candidate = this.resolvePath(inputPath);
766
781
  if (this.policy.unrestrictedPaths) return candidate;
782
+ const candidateInfo = await lstat(candidate).catch(() => null);
767
783
  let ancestor = candidate;
768
784
  while (!(await lstat(ancestor).catch(() => null))) {
769
785
  const parent = dirname(ancestor);
770
786
  if (parent === ancestor) break;
771
787
  ancestor = parent;
772
788
  }
773
- const canonicalAncestor = await realpath(ancestor);
774
- assertContainedPath(this.workspace, canonicalAncestor);
775
- return candidate;
789
+ const [workspace, canonicalAncestor] = await Promise.all([this.canonicalWorkspace(), realpath(ancestor)]);
790
+ assertContainedPath(workspace, canonicalAncestor);
791
+ if (candidateInfo?.isSymbolicLink()) throw new Error("refusing to overwrite a symbolic link");
792
+ const suffix = relative(ancestor, candidate);
793
+ return suffix ? resolve(canonicalAncestor, suffix) : canonicalAncestor;
776
794
  }
777
795
 
778
796
  displayPath(fullPath) {
@@ -2,7 +2,7 @@ import { DurableObject } from "cloudflare:workers";
2
2
  import toolCatalog from "../shared/tool-catalog.json";
3
3
 
4
4
  const SERVER_NAME = "machine-bridge-mcp";
5
- const SERVER_VERSION = "0.4.0";
5
+ const SERVER_VERSION = "0.4.2";
6
6
  const MCP_PROTOCOL_VERSION = "2025-11-25";
7
7
  const MCP_SUPPORTED_PROTOCOL_VERSIONS = ["2025-11-25", "2025-06-18", "2025-03-26"] as const;
8
8
  const JSONRPC_VERSION = "2.0";
@@ -1,53 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- workflow_dispatch:
8
-
9
- permissions:
10
- contents: read
11
-
12
- concurrency:
13
- group: ci-${{ github.workflow }}-${{ github.ref }}
14
- cancel-in-progress: true
15
-
16
- jobs:
17
- check:
18
- name: ${{ matrix.os }} / Node ${{ matrix.node }}
19
- runs-on: ${{ matrix.os }}
20
- timeout-minutes: 20
21
- strategy:
22
- fail-fast: false
23
- matrix:
24
- include:
25
- - os: ubuntu-latest
26
- node: 22
27
- - os: ubuntu-latest
28
- node: 24
29
- - os: macos-latest
30
- node: 22
31
- - os: windows-latest
32
- node: 22
33
- steps:
34
- - uses: actions/checkout@v4
35
- - uses: actions/setup-node@v4
36
- with:
37
- node-version: ${{ matrix.node }}
38
- cache: npm
39
- - run: npm ci
40
- - run: npm run check
41
-
42
- package-audit:
43
- runs-on: ubuntu-latest
44
- timeout-minutes: 10
45
- steps:
46
- - uses: actions/checkout@v4
47
- - uses: actions/setup-node@v4
48
- with:
49
- node-version: 22
50
- cache: npm
51
- - run: npm ci
52
- - run: npm audit --omit=dev --audit-level=high
53
- - run: npm pack --dry-run