machine-bridge-mcp 0.4.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/README.md +110 -14
- package/SECURITY.md +38 -6
- package/docs/ARCHITECTURE.md +50 -15
- package/docs/CLIENTS.md +18 -1
- package/docs/LOGGING.md +82 -0
- package/docs/MANAGED_JOBS.md +274 -0
- package/docs/OPERATIONS.md +80 -23
- package/docs/TESTING.md +13 -5
- package/package.json +8 -7
- package/src/local/cli.mjs +345 -80
- package/src/local/daemon.mjs +159 -42
- package/src/local/job-runner.mjs +470 -0
- package/src/local/log.mjs +56 -15
- package/src/local/managed-jobs.mjs +854 -0
- package/src/local/service.mjs +40 -21
- package/src/local/state.mjs +94 -26
- package/src/local/stdio.mjs +14 -38
- package/src/local/tools.mjs +28 -13
- package/src/shared/server-metadata.json +24 -0
- package/src/shared/tool-catalog.json +500 -2
- package/src/worker/index.ts +13 -11
package/docs/LOGGING.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Logging and observability
|
|
2
|
+
|
|
3
|
+
## Goals
|
|
4
|
+
|
|
5
|
+
Operational logs should answer four questions without becoming a transcript of user activity:
|
|
6
|
+
|
|
7
|
+
1. Did deployment and relay connection succeed?
|
|
8
|
+
2. Is the daemon reconnecting, superseded, or unhealthy?
|
|
9
|
+
3. Did a tool call fail, time out, or become unusually slow?
|
|
10
|
+
4. Which component and coarse error class should be investigated?
|
|
11
|
+
|
|
12
|
+
Logs are not a command history, content audit trail, or substitute for MCP client approvals.
|
|
13
|
+
|
|
14
|
+
## Levels
|
|
15
|
+
|
|
16
|
+
The CLI accepts:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
--log-level error|warn|info|debug
|
|
20
|
+
--quiet # alias for error
|
|
21
|
+
--verbose # alias for debug
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The default foreground level is `info`. Platform autostart services use `warn`.
|
|
25
|
+
|
|
26
|
+
| Level | Intended events |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `error` | Broken request handlers, relay transport errors, unrecoverable local failures |
|
|
29
|
+
| `warn` | Failed tool calls, disconnections, malformed/oversized relay messages, superseded daemons |
|
|
30
|
+
| `info` | Deployment/startup transitions, successful relay connection, successful calls slower than 30 seconds |
|
|
31
|
+
| `debug` | Routine successful calls, shortened correlation IDs, reconnect timing, cancellation correlation |
|
|
32
|
+
|
|
33
|
+
Routine successful calls are deliberately absent from `info`. A normal sequence of hundreds of tool invocations should not fill the user's terminal or service logs.
|
|
34
|
+
|
|
35
|
+
## Event fields
|
|
36
|
+
|
|
37
|
+
Default warning/error and slow-call events may include:
|
|
38
|
+
|
|
39
|
+
- component;
|
|
40
|
+
- tool name;
|
|
41
|
+
- duration in milliseconds;
|
|
42
|
+
- coarse error class;
|
|
43
|
+
- relay close code;
|
|
44
|
+
- bounded reconnect delay.
|
|
45
|
+
|
|
46
|
+
Shortened random call identifiers are debug-only. They are useful only for correlating adjacent local events and are not stable audit identifiers.
|
|
47
|
+
|
|
48
|
+
The implementation intentionally omits:
|
|
49
|
+
|
|
50
|
+
- tool arguments;
|
|
51
|
+
- command text or argv;
|
|
52
|
+
- stdin, stdout, and stderr;
|
|
53
|
+
- file, patch, or image content;
|
|
54
|
+
- OAuth request bodies;
|
|
55
|
+
- connection passwords, daemon secrets, authorization codes, and access tokens.
|
|
56
|
+
|
|
57
|
+
Unexpected daemon and Worker failures are reduced to coarse error classes in normal logs. Client-facing tool errors may contain more detail according to the active path-display policy, but those details are not copied into normal operational logs.
|
|
58
|
+
|
|
59
|
+
## Bounding and redaction
|
|
60
|
+
|
|
61
|
+
Messages, strings, object depth, object key counts, array item counts, and serialized field payloads are bounded. Control characters are neutralized. Fields with secret-like names and known token formats are recursively redacted.
|
|
62
|
+
|
|
63
|
+
This is defense in depth, not content classification. A previously unknown secret format embedded in an ordinary non-secret-named field may evade pattern redaction. For that reason, tool arguments and outputs are omitted rather than merely filtered.
|
|
64
|
+
|
|
65
|
+
## Files
|
|
66
|
+
|
|
67
|
+
Autostart logs are stored below the state root:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
logs/daemon.out.log
|
|
71
|
+
logs/daemon.err.log
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
They are owner-only where the platform supports Unix-style permissions. Existing files are tail-trimmed on UTF-8/line boundaries before daemon startup. Because background services use `warn`, normal successful tool traffic should not cause sustained log growth.
|
|
75
|
+
|
|
76
|
+
Each managed job also has owner-only `runner.out.log` and `runner.err.log` files for runner startup/crash diagnostics. Child-step stdout and stderr are captured into bounded job results according to `capture_output`; they are not copied into runner or daemon operational logs. Runner logs are retained with the bounded job directory and removed by job retention cleanup.
|
|
77
|
+
|
|
78
|
+
## MCP host boundary
|
|
79
|
+
|
|
80
|
+
Machine Bridge does not classify filenames as sensitive and does not block `.env`, password, key, or credential-looking names. The active local policy, local OS permissions, and endpoint-security controls determine what the server itself can read or execute.
|
|
81
|
+
|
|
82
|
+
An MCP host, model provider, desktop application, or platform execution layer may independently deny a request involving credentials or sensitive files. Such a denial occurs before or outside Machine Bridge's file resolver and cannot be disabled by selecting the local `full` profile. Use `server_info`, `machine-mcp status`, and `machine-mcp doctor` to distinguish the active local policy from host-side enforcement.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# Managed jobs and local resources
|
|
2
|
+
|
|
3
|
+
## Problem addressed
|
|
4
|
+
|
|
5
|
+
An MCP conversation is not a durable execution coordinator. A host or connector may reject a later tool call, an approval may be withdrawn, the network may disconnect, or local endpoint-security software may begin denying process creation. A sequence such as:
|
|
6
|
+
|
|
7
|
+
1. create a helper script;
|
|
8
|
+
2. connect to a remote server;
|
|
9
|
+
3. repair the service;
|
|
10
|
+
4. remove the helper;
|
|
11
|
+
|
|
12
|
+
can therefore stop after step 2 or 3 and leave local or remote temporary state behind.
|
|
13
|
+
|
|
14
|
+
Machine Bridge managed jobs reduce this failure mode by accepting the complete execution and cleanup plan in one call. After acceptance, an independent local runner owns the lifecycle. It does not depend on the MCP socket remaining connected.
|
|
15
|
+
|
|
16
|
+
This mechanism does **not** bypass host, operating-system, or endpoint-security policy. A job snapshots its execution policy/environment at acceptance; later profile changes affect new jobs, while accepted jobs continue until completion or explicit cancellation. The initial `start_job` request must still be permitted, and every local child process remains subject to local security controls.
|
|
17
|
+
|
|
18
|
+
## Diagnose the blocking layer
|
|
19
|
+
|
|
20
|
+
Use the MCP tool:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
diagnose_runtime
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
or the local terminal:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
machine-mcp doctor
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Interpretation:
|
|
33
|
+
|
|
34
|
+
| Observation | Likely boundary |
|
|
35
|
+
|---|---|
|
|
36
|
+
| The tool call is rejected before any structured response | MCP host, connector gateway, approval system, or transport |
|
|
37
|
+
| `diagnose_runtime` responds, but `local-process-spawn` fails | Local OS permissions, endpoint security, executable policy, or broken runtime |
|
|
38
|
+
| Process spawn passes but `local-shell` fails | Shell configuration or shell-specific local policy |
|
|
39
|
+
| Managed-job storage fails | State-root permissions, disk, filesystem policy, or endpoint security |
|
|
40
|
+
| A job was accepted and later MCP calls are rejected | The detached job continues; inspect it through local CLI |
|
|
41
|
+
|
|
42
|
+
A successful diagnostic response proves that particular request reached the local daemon. It cannot retrospectively identify a request that the host blocked before delivery.
|
|
43
|
+
|
|
44
|
+
## Register local resources
|
|
45
|
+
|
|
46
|
+
Secrets and other local-only files should be registered from the user's terminal, not read into the model context:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
chmod 600 ~/.ssh/racknerd_ed25519
|
|
50
|
+
machine-mcp resource add racknerd-key ~/.ssh/racknerd_ed25519
|
|
51
|
+
machine-mcp resource list
|
|
52
|
+
machine-mcp resource check racknerd-key
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Registration stores only canonical path and bounded metadata in owner-only state. It reads the file locally to validate accessibility and size but does not print or send its contents. New jobs see registry changes immediately; the daemon does not need a restart.
|
|
56
|
+
|
|
57
|
+
Unix-like systems reject group/other-readable resource files by default. `--allow-insecure-permissions` is an explicit override. Windows ACLs and extended Unix ACLs cannot be fully represented by the portable mode check, so operators must still inspect platform permissions.
|
|
58
|
+
|
|
59
|
+
Remove an alias with:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
machine-mcp resource remove racknerd-key
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Removing an alias affects new jobs. A job already accepted has an owner-only resource snapshot specification and continues independently.
|
|
66
|
+
|
|
67
|
+
## Resource injection modes
|
|
68
|
+
|
|
69
|
+
A managed step is argv-based and supports three local resource modes:
|
|
70
|
+
|
|
71
|
+
### File-path placeholder
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"argv": ["ssh", "-i", "{{resource:racknerd-key}}", "root@example", "true"]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The runner copies the resource into the private job runtime with mode `0600`, substitutes that copy's path, and removes the runtime after cleanup.
|
|
80
|
+
|
|
81
|
+
### Standard input
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"argv": ["some-program", "--password-stdin"],
|
|
86
|
+
"stdin_resource": "service-password",
|
|
87
|
+
"capture_output": "discard"
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Environment variable
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"argv": ["some-program"],
|
|
96
|
+
"env_resources": {
|
|
97
|
+
"SERVICE_TOKEN": "service-token"
|
|
98
|
+
},
|
|
99
|
+
"capture_output": "discard"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Environment injection is convenient but may expose a value to same-user process inspection or child processes. Prefer a file path or stdin when the target program supports it.
|
|
104
|
+
|
|
105
|
+
At job acceptance, referenced resources are reopened, bounded, hashed, and recorded in the owner-only active plan. The runner reopens and verifies the hash before copying. A changed resource causes the job to fail rather than silently using different content.
|
|
106
|
+
|
|
107
|
+
## Job-scoped temporary files
|
|
108
|
+
|
|
109
|
+
Do not create ad hoc helpers in the workspace when the file exists only for one operation. Include it in the job:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"temporary_files": [
|
|
114
|
+
{
|
|
115
|
+
"name": "repair.js",
|
|
116
|
+
"content": "console.log('repair')"
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"steps": [
|
|
120
|
+
{
|
|
121
|
+
"argv": ["node", "{{temp:repair.js}}"]
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Available placeholders are:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
{{temp:name}}
|
|
131
|
+
{{resource:name}}
|
|
132
|
+
{{job:runtime}}
|
|
133
|
+
{{job:workspace}}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Temporary files live only below the private job runtime and are removed in the runner's final cleanup path. This is preferable to writing into `Codex/daily/ops`, `/tmp`, or a remote home directory and relying on a later MCP call to delete the file.
|
|
137
|
+
|
|
138
|
+
For a remote shell program, avoid a remote helper entirely when possible:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"steps": [
|
|
143
|
+
{
|
|
144
|
+
"argv": [
|
|
145
|
+
"ssh",
|
|
146
|
+
"-i",
|
|
147
|
+
"{{resource:racknerd-key}}",
|
|
148
|
+
"root@example",
|
|
149
|
+
"sh",
|
|
150
|
+
"-s"
|
|
151
|
+
],
|
|
152
|
+
"stdin": "set -eu\n# repair commands here\n"
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The script travels through the SSH process stdin and is not stored as a remote file.
|
|
159
|
+
|
|
160
|
+
## Finally steps
|
|
161
|
+
|
|
162
|
+
A job can contain ordered main steps and ordered `finally_steps`:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"name": "remote maintenance",
|
|
167
|
+
"steps": [
|
|
168
|
+
{
|
|
169
|
+
"argv": ["ssh", "root@example", "perform-maintenance"]
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"finally_steps": [
|
|
173
|
+
{
|
|
174
|
+
"argv": ["ssh", "root@example", "rm", "-f", "/tmp/maintenance-helper"],
|
|
175
|
+
"allow_failure": true
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Finally steps are attempted after success, failure, timeout, or cancellation. Cancellation is delivered through an owner-only marker polled by the runner; the runner terminates the current child process tree and remains alive to execute cleanup. This avoids platform-specific signal behavior killing the coordinator itself. If a runner is interrupted, the next daemon start or local `machine-mcp job ...` command detects the dead runner, removes stale private resource copies, and runs the finally phase in recovery mode.
|
|
182
|
+
|
|
183
|
+
Automatic dead-runner recovery is attempted at most three times; persistent failure becomes `recovery_exhausted` to avoid an endless restart loop. Cleanup is best effort, not mathematically guaranteed. Power loss, disk failure, permanent account loss, or a local security product that denies the cleanup executable can still prevent it. Finally steps should therefore be idempotent and safe to run more than once.
|
|
184
|
+
|
|
185
|
+
## Two-phase local approval
|
|
186
|
+
|
|
187
|
+
If the MCP host rejects execution-class tools but still allows state changes, use:
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
stage_job
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This performs the same schema, cwd, resource, size, and permission validation as `start_job`, but records status `staged` and launches no process. The response includes the local approval command:
|
|
194
|
+
|
|
195
|
+
```sh
|
|
196
|
+
machine-mcp job inspect JOB_ID
|
|
197
|
+
machine-mcp job approve JOB_ID
|
|
198
|
+
# or after a separate review, non-interactively:
|
|
199
|
+
machine-mcp job approve JOB_ID --yes
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`job inspect` displays the reviewable plan, including argv, ordinary environment overrides, stdin, temporary helper content, and finally steps, while omitting registered resource source paths and per-resource hashes. The overall `plan_sha256` can be compared with the value returned by `stage_job`.
|
|
203
|
+
|
|
204
|
+
Local approval is a new operator authorization. It intentionally does not depend on the current MCP execution profile: a plan staged under a write-capable profile can be reviewed and approved from the terminal even when the connector is not allowed to execute. The plan retains the filesystem scope and environment mode captured when it was staged.
|
|
205
|
+
|
|
206
|
+
Before approval:
|
|
207
|
+
|
|
208
|
+
- no main step runs;
|
|
209
|
+
- no finally step runs;
|
|
210
|
+
- no resource is copied into a runtime directory;
|
|
211
|
+
- cancelling produces `cancelled_before_start` and deletes the plan.
|
|
212
|
+
|
|
213
|
+
Staged plans count toward the 50-item retention limit and expire with the seven-day job retention policy. They are not considered active processes and do not independently block uninstall after the normal uninstall confirmation.
|
|
214
|
+
|
|
215
|
+
## Submit and inspect
|
|
216
|
+
|
|
217
|
+
Through MCP:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
stage_job
|
|
221
|
+
start_job
|
|
222
|
+
list_jobs
|
|
223
|
+
read_job
|
|
224
|
+
cancel_job
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
From the local terminal, including after an MCP host blocks further execution calls:
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
machine-mcp job list
|
|
231
|
+
machine-mcp job inspect JOB_ID
|
|
232
|
+
machine-mcp job approve JOB_ID [--yes]
|
|
233
|
+
machine-mcp job cancel JOB_ID
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
A local JSON fallback is also available:
|
|
237
|
+
|
|
238
|
+
```sh
|
|
239
|
+
machine-mcp job submit plan.json
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The plan format is the same object accepted by `start_job`.
|
|
243
|
+
|
|
244
|
+
## Output and secret handling
|
|
245
|
+
|
|
246
|
+
Step output is bounded to 64 KiB per stream and a shared 256 KiB capture budget per job, then stored in owner-only job results. Exact registered resource paths and exact resource content are replaced before results are returned. Common exact base64 and hexadecimal forms are also replaced for bounded resources.
|
|
247
|
+
|
|
248
|
+
This redaction is defense in depth, not a data-loss-prevention guarantee. It cannot reliably detect:
|
|
249
|
+
|
|
250
|
+
- partial secrets;
|
|
251
|
+
- encrypted, compressed, hashed, or otherwise transformed values;
|
|
252
|
+
- values copied from the full inherited environment rather than registered resources;
|
|
253
|
+
- application-specific encodings.
|
|
254
|
+
|
|
255
|
+
Use:
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
{ "capture_output": "discard" }
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
for any step that may echo a credential. In discard mode stdout and stderr are consumed but not retained or returned.
|
|
262
|
+
|
|
263
|
+
Never place a secret directly in `argv`, `env`, `stdin`, a temporary file's `content`, or a plan JSON file. Use a registered resource alias.
|
|
264
|
+
|
|
265
|
+
## Persistence and privacy
|
|
266
|
+
|
|
267
|
+
Per-workspace jobs are stored below the owner-only profile directory. Active jobs retain an owner-only plan for crash recovery. After a terminal status is committed, the full plan is deleted, including argv, stdin, embedded temporary-file content, and resource source paths.
|
|
268
|
+
|
|
269
|
+
Retained public job data contains bounded status and redacted results. Up to 50 jobs are retained for up to seven days. Private runtime copies are removed after the finally phase. Runner stdout/stderr log files contain only runner-level diagnostics; step output is not written to those operational logs.
|
|
270
|
+
|
|
271
|
+
Process sessions and managed jobs have different semantics:
|
|
272
|
+
|
|
273
|
+
- process sessions are interactive, memory-only, and die with the daemon connection;
|
|
274
|
+
- managed jobs are non-interactive, persistent, detached, and designed to survive MCP disconnection.
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -8,30 +8,70 @@ machine-mcp doctor
|
|
|
8
8
|
machine-mcp service status
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
`status` prints redacted profile state and verifies the deployed Worker version. `doctor` checks Node.js, the package-installed Wrangler binary, Cloudflare login,
|
|
11
|
+
`status` prints redacted profile state and verifies the deployed Worker version. Resource source paths remain redacted. `doctor` checks Node.js, the package-installed Wrangler binary, Cloudflare login, Worker health, and the same fixed local filesystem/process/shell/job-storage/resource probes exposed by `diagnose_runtime`. Public `/healthz` output contains only server identity and version; daemon details require an authenticated `server_info` call.
|
|
12
|
+
|
|
13
|
+
### Blocking-layer decision table
|
|
14
|
+
|
|
15
|
+
| Result | Interpretation |
|
|
16
|
+
|---|---|
|
|
17
|
+
| No structured result because the host rejects the call | Host/connector approval or safety layer, or transport before daemon delivery |
|
|
18
|
+
| `mcp-host-to-daemon` passes but `local-filesystem` fails | Local state/runtime permissions, disk policy, sandbox, or endpoint security |
|
|
19
|
+
| Filesystem passes but `local-process-spawn` fails | Local executable policy, endpoint security, OS permissions, or damaged Node runtime |
|
|
20
|
+
| Direct spawn passes but `local-shell` fails | Shell path/profile/policy problem |
|
|
21
|
+
| `managed-job-storage` fails | Owner-only profile/job directory cannot be used |
|
|
22
|
+
| Registered resource is unavailable | File moved, permissions changed, size exceeded, or local access denied |
|
|
23
|
+
|
|
24
|
+
A successful diagnostic result applies only to that probe. An MCP host can still deny a later call based on its own request context.
|
|
12
25
|
|
|
13
26
|
## Logs
|
|
14
27
|
|
|
15
28
|
Remote autostart logs are stored under the state root in `logs/daemon.out.log` and `logs/daemon.err.log`. Files are owner-only where supported and tail-trimmed before daemon startup.
|
|
16
29
|
|
|
17
|
-
|
|
30
|
+
Logging is level-based:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
error unrecoverable local/transport failures
|
|
34
|
+
warn failed calls, disconnects, malformed relay events
|
|
35
|
+
info startup/deploy/connect transitions and calls slower than 30 seconds
|
|
36
|
+
debug routine successful calls, shortened correlation IDs, cancellation/reconnect details
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Foreground mode defaults to `info`; autostart uses `warn`. Use `--verbose` or `--log-level debug` only for diagnosis. `--quiet` is an alias for `--log-level error`.
|
|
40
|
+
|
|
41
|
+
Normal logs intentionally omit tool arguments, file/patch/image content, command text and argv, stdin/stdout/stderr, OAuth request bodies, connection credentials, authorization codes, and tokens. Unexpected daemon and Worker failures use coarse error classes rather than raw exception messages. Messages and structured fields are bounded and secret-like fields/token formats are redacted.
|
|
18
42
|
|
|
19
|
-
-
|
|
20
|
-
- tool name;
|
|
21
|
-
- shortened random call identifier;
|
|
22
|
-
- duration and success/failure;
|
|
23
|
-
- coarse error class;
|
|
24
|
-
- connection/reconnect status.
|
|
43
|
+
See [LOGGING.md](LOGGING.md) for the event contract and MCP-host boundary. Cloudflare observability is sampled and is not a complete audit log.
|
|
25
44
|
|
|
26
|
-
|
|
45
|
+
## Managed jobs and local recovery
|
|
27
46
|
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
47
|
+
Register local-only resources from the terminal:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
machine-mcp resource add NAME FILE_PATH
|
|
51
|
+
machine-mcp resource list
|
|
52
|
+
machine-mcp resource check NAME
|
|
53
|
+
machine-mcp resource remove NAME
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Inspect detached jobs even when the MCP host no longer permits execution tools:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
machine-mcp job list
|
|
60
|
+
machine-mcp job inspect JOB_ID
|
|
61
|
+
machine-mcp job approve JOB_ID [--yes]
|
|
62
|
+
machine-mcp job cancel JOB_ID
|
|
63
|
+
machine-mcp job submit plan.json
|
|
64
|
+
```
|
|
33
65
|
|
|
34
|
-
|
|
66
|
+
Registry changes apply to newly submitted jobs without restarting the daemon. Active jobs use the resource snapshot accepted with their plan.
|
|
67
|
+
|
|
68
|
+
Policy changes affect new direct submissions. Cancel accepted running jobs explicitly when revoking execution authority. A staged plan launches only after local `job approve`, which is an independent operator authorization. A managed job transitions through `queued`, `running`, `cleaning`, and a terminal status such as `succeeded`, `failed`, or `cancelled`. Cleanup-specific terminal variants report a failed finally phase. If a runner PID dies, the next daemon/job-CLI start marks the job interrupted, removes stale private runtime copies, and runs the finally phase in recovery mode. Automatic recovery is capped at three attempts; persistent failure becomes `recovery_exhausted`.
|
|
69
|
+
|
|
70
|
+
Use job-scoped `temporary_files` for local helpers. For remote maintenance, prefer `ssh ... sh -s` with the remote script in step `stdin`; this avoids remote temporary scripts. Explicit remote cleanup belongs in idempotent `finally_steps`.
|
|
71
|
+
|
|
72
|
+
Uninstall refuses to remove local state while any managed job remains active. Active plans are needed for recovery and are owner-only. Terminal jobs delete their full plans. Bounded redacted results and runner-level diagnostics remain for up to seven days/50 jobs. Step output is never copied to ordinary daemon logs.
|
|
73
|
+
|
|
74
|
+
See [MANAGED_JOBS.md](MANAGED_JOBS.md).
|
|
35
75
|
|
|
36
76
|
## Reconnect and replacement
|
|
37
77
|
|
|
@@ -58,26 +98,43 @@ Defense-in-depth limits include:
|
|
|
58
98
|
- command timeout: 1–600 seconds;
|
|
59
99
|
- process-session read wait: at most 30 seconds;
|
|
60
100
|
- direct directory result: 10,000 entries and 4 MiB of path metadata;
|
|
61
|
-
- recursive walk: 200,000 visited entries
|
|
101
|
+
- recursive walk: 200,000 visited entries;
|
|
102
|
+
- managed jobs: 50 retained, seven-day retention;
|
|
103
|
+
- managed-job steps: 16 main plus 16 finally;
|
|
104
|
+
- managed-job timeout: 1–3,600 seconds per step;
|
|
105
|
+
- managed-job output: 64 KiB per stream and 256 KiB total captured across one job;
|
|
106
|
+
- registered resources: 64, 1 MiB each, 8 MiB referenced per job;
|
|
107
|
+
- job-scoped temporary files: 16 files, 512 KiB total content.
|
|
62
108
|
|
|
63
109
|
## Upgrade behavior
|
|
64
110
|
|
|
65
|
-
Version 0.
|
|
111
|
+
Version 0.5 records policy origin and revision. A state entry matching the exact legacy implicit-default shape—write enabled, shell enabled, workspace-confined paths, isolated environment, and relative output—is migrated once to the current `full` default. Explicit named profiles and identified custom policies are preserved.
|
|
112
|
+
|
|
113
|
+
`full` enables writes, direct processes, process sessions, shell execution, unrestricted direct filesystem paths, absolute path output, and the complete parent environment. It does not override operating-system access controls or independent MCP-host/platform policy.
|
|
114
|
+
|
|
115
|
+
Inspect effective policy with:
|
|
66
116
|
|
|
67
117
|
```sh
|
|
68
|
-
machine-mcp
|
|
118
|
+
machine-mcp status
|
|
119
|
+
machine-mcp doctor
|
|
69
120
|
```
|
|
70
121
|
|
|
71
|
-
|
|
122
|
+
Select a policy explicitly with:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
machine-mcp --workspace /path/to/project --profile full
|
|
126
|
+
machine-mcp --workspace /path/to/project --profile agent
|
|
127
|
+
```
|
|
72
128
|
|
|
73
|
-
A remote policy change is saved locally, propagated in the daemon handshake, and
|
|
129
|
+
A remote policy change is saved locally, propagated in the daemon handshake, and loaded by autostart from owner-only state.
|
|
74
130
|
|
|
75
131
|
## Incident response
|
|
76
132
|
|
|
77
133
|
After suspected credential or client compromise:
|
|
78
134
|
|
|
79
135
|
1. stop foreground and autostart daemons;
|
|
80
|
-
2. run `machine-mcp rotate-secrets
|
|
136
|
+
2. run `machine-mcp rotate-secrets`;
|
|
81
137
|
3. restart without broad flags and redeploy;
|
|
82
|
-
4. inspect Cloudflare account access, Worker configuration, local state permissions, and service logs;
|
|
83
|
-
5.
|
|
138
|
+
4. inspect Cloudflare account access, Worker configuration, local state/resource permissions, managed-job results, and service logs;
|
|
139
|
+
5. cancel active managed jobs and remove compromised resource aliases;
|
|
140
|
+
6. remove the Worker and local state if continued remote access is unnecessary.
|
package/docs/TESTING.md
CHANGED
|
@@ -23,23 +23,28 @@ The suite includes:
|
|
|
23
23
|
- author-email privacy in `git_log`;
|
|
24
24
|
- isolated command HOME/temp/cache behavior;
|
|
25
25
|
- one-shot timeout, descendant termination, cancellation, and process-session interaction;
|
|
26
|
+
- layered fixed runtime diagnostics for filesystem, direct process, shell, managed-job storage, and resource availability;
|
|
27
|
+
- local resource CLI registration, permission checks, dynamic reload, state-path redaction, and content non-disclosure;
|
|
28
|
+
- managed-job staging/local approval/cancel-before-start, detachment, job-scoped temporary files, resource hash verification/redaction, discard capture, finally execution, cancellation escalation, plan scrubbing, and dead-runner recovery;
|
|
26
29
|
- daemon/startup locking and state corruption recovery;
|
|
27
|
-
- guarded state-root removal and legacy-
|
|
28
|
-
-
|
|
30
|
+
- guarded state-root removal, schema migration, policy-origin persistence, and legacy implicit-default migration;
|
|
31
|
+
- no filename-based sensitive-file denial under unrestricted policy;
|
|
32
|
+
- log redaction, control-character handling, message/field bounds, default success-log suppression, and service warning-level configuration;
|
|
29
33
|
- CLI parsing, policy profiles, and client configuration boundaries;
|
|
30
|
-
- live stdio MCP initialization, discovery, calls, rich content, sessions, cancellation, and
|
|
34
|
+
- live stdio MCP initialization, discovery, calls, rich content, sessions, cancellation, managed-job acceptance, and a detached job/finally phase that survives stdio shutdown;
|
|
31
35
|
- live local Worker OAuth registration, consent, PKCE, token replay rejection, throttling, CORS, protocol negotiation, dynamic tool advertisement, rich content, daemon replacement, and cancellation.
|
|
32
36
|
|
|
33
37
|
## Additional release checks
|
|
34
38
|
|
|
35
39
|
```sh
|
|
36
40
|
npm run worker:dry-run
|
|
41
|
+
npm audit --audit-level=high
|
|
37
42
|
npm audit --omit=dev --audit-level=high
|
|
38
43
|
npm pack --dry-run
|
|
39
44
|
npm run version:check
|
|
40
45
|
```
|
|
41
46
|
|
|
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
|
|
47
|
+
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 audits both the complete dependency graph and the production-only graph, then performs a dry-run package build. Dependency and GitHub Actions updates are monitored by Dependabot.
|
|
43
48
|
|
|
44
49
|
## Test design rules
|
|
45
50
|
|
|
@@ -48,4 +53,7 @@ GitHub Actions executes the main suite on Linux, macOS, and Windows. Node 22 and
|
|
|
48
53
|
- Every bounded resource needs an over-limit test.
|
|
49
54
|
- Every multi-stage mutation needs a no-partial-commit test.
|
|
50
55
|
- Every remote call correlation change needs daemon replacement and cancellation coverage.
|
|
51
|
-
-
|
|
56
|
+
- Every durable workflow needs disconnect, cancellation, cleanup-failure, dead-runner, and plan-scrubbing coverage.
|
|
57
|
+
- Secret-bearing resource tests must assert absence of raw, path, base64, and hex forms from MCP-visible results.
|
|
58
|
+
- Logs and public metadata should be tested for absence of sensitive fields, arguments, outputs, and routine success noise—not only presence of expected fields.
|
|
59
|
+
- Cross-platform tests must avoid shell syntax, URL-path conversion, and executable-shim assumptions specific to one operating system.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "machine-bridge-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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",
|
|
@@ -39,23 +39,24 @@
|
|
|
39
39
|
"prepublishOnly": "npm run check && npm run version:check && npm run release:check",
|
|
40
40
|
"worker:types": "wrangler types src/worker/worker-configuration.d.ts",
|
|
41
41
|
"typecheck": "npm run worker:types && tsc -p tsconfig.json --noEmit",
|
|
42
|
-
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/daemon.mjs && node --check src/local/patch.mjs && node --check src/local/process-sessions.mjs && node --check src/local/tools.mjs && node --check src/local/stdio.mjs && node --check src/local/state.mjs && node --check src/local/shell.mjs && node --check src/local/service.mjs && node --check src/local/log.mjs && node --check scripts/sync-worker-version.mjs && node --check scripts/github-release.mjs && node --check tests/worker-integration-test.mjs && node --check tests/stdio-integration-test.mjs && node --check tests/catalog-test.mjs && node --check tests/local-self-test.mjs && node --check tests/daemon-self-test.mjs",
|
|
43
|
-
"check": "npm run typecheck && npm run syntax && npm run catalog:test && npm run self-test && npm run stdio:integration-test && npm run worker:integration-test",
|
|
42
|
+
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/daemon.mjs && node --check src/local/patch.mjs && node --check src/local/process-sessions.mjs && node --check src/local/tools.mjs && node --check src/local/stdio.mjs && node --check src/local/state.mjs && node --check src/local/shell.mjs && node --check src/local/service.mjs && node --check src/local/log.mjs && node --check src/local/managed-jobs.mjs && node --check src/local/job-runner.mjs && node --check scripts/sync-worker-version.mjs && node --check scripts/github-release.mjs && node --check tests/worker-integration-test.mjs && node --check tests/stdio-integration-test.mjs && node --check tests/catalog-test.mjs && node --check tests/local-self-test.mjs && node --check tests/daemon-self-test.mjs && node --check tests/managed-jobs-test.mjs",
|
|
43
|
+
"check": "npm run typecheck && npm run syntax && npm run catalog:test && npm run self-test && npm run managed-jobs:test && npm run stdio:integration-test && npm run worker:integration-test",
|
|
44
44
|
"worker:dry-run": "wrangler deploy --dry-run",
|
|
45
45
|
"worker:integration-test": "node tests/worker-integration-test.mjs",
|
|
46
46
|
"release:check": "node scripts/github-release.mjs --check",
|
|
47
47
|
"release:publish": "node scripts/github-release.mjs --publish",
|
|
48
48
|
"release:backfill": "node scripts/github-release.mjs --backfill",
|
|
49
49
|
"stdio:integration-test": "node tests/stdio-integration-test.mjs",
|
|
50
|
-
"catalog:test": "node tests/catalog-test.mjs"
|
|
50
|
+
"catalog:test": "node tests/catalog-test.mjs",
|
|
51
|
+
"managed-jobs:test": "node tests/managed-jobs-test.mjs"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
|
-
"wrangler": "
|
|
54
|
-
"ws": "
|
|
54
|
+
"wrangler": "4.110.0",
|
|
55
|
+
"ws": "8.21.0"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@types/node": "^22.20.1",
|
|
58
|
-
"typescript": "^
|
|
59
|
+
"typescript": "^7.0.2"
|
|
59
60
|
},
|
|
60
61
|
"keywords": [
|
|
61
62
|
"mcp",
|