machine-bridge-mcp 0.9.0 → 0.10.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 +21 -0
- package/README.md +44 -23
- package/SECURITY.md +13 -1
- package/browser-extension/manifest.json +33 -0
- package/browser-extension/page-automation.js +246 -0
- package/browser-extension/pairing.js +19 -0
- package/browser-extension/service-worker.js +406 -0
- package/docs/AGENT_CONTEXT.md +65 -131
- package/docs/ARCHITECTURE.md +29 -9
- package/docs/CLIENTS.md +8 -0
- package/docs/ENGINEERING.md +5 -1
- package/docs/LOCAL_AUTOMATION.md +111 -0
- package/docs/LOGGING.md +9 -1
- package/docs/OPERATIONS.md +30 -0
- package/docs/PRIVACY.md +2 -1
- package/docs/RELEASING.md +2 -2
- package/docs/TESTING.md +6 -5
- package/package.json +13 -7
- package/scripts/sync-worker-version.mjs +27 -12
- package/src/local/agent-context.mjs +163 -9
- package/src/local/app-automation.mjs +400 -0
- package/src/local/browser-bridge.mjs +757 -0
- package/src/local/cli.mjs +85 -0
- package/src/local/runtime.mjs +114 -4
- package/src/local/state.mjs +1 -1
- package/src/local/stdio.mjs +12 -7
- package/src/shared/server-metadata.json +4 -1
- package/src/shared/tool-catalog.json +853 -0
- package/src/worker/index.ts +24 -4
package/docs/AGENT_CONTEXT.md
CHANGED
|
@@ -1,74 +1,67 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Session instructions, skills, commands, and capability discovery
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Machine Bridge provides a static MCP bootstrap surface for Codex-style instructions and filesystem skills. The catalog remains stable while local instructions, skills, commands, applications, and browser state are discovered at call time.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This approximates a local coding agent without pretending that the MCP server owns the model loop. The host can filter tools, require confirmation, truncate context, or decline calls before they reach Machine Bridge.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## MCP tools
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- `session_bootstrap` returns the current global/session instruction text and refresh fingerprint.
|
|
10
|
+
- `agent_context` returns the complete target-specific instruction chain, skill summaries, and command registry.
|
|
11
|
+
- `resolve_task_capabilities` rescans and ranks skills/commands for the current task, optionally loading the best skill; the runtime also adds application and browser capability metadata.
|
|
12
|
+
- `list_local_skills` searches discovered `SKILL.md` bundles.
|
|
13
|
+
- `load_local_skill` returns one skill entrypoint and bounded file inventory without execution.
|
|
14
|
+
- `list_local_commands` returns effective registered commands.
|
|
15
|
+
- `run_local_command` executes a registered direct-argv command when policy permits.
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
Both stdio and remote Worker connection initialization attempt `session_bootstrap`. Its instruction text is appended to the MCP `initialize` result. Because a host may reuse one MCP connection across conversations, the explicit tool and per-task `resolve_task_capabilities` call remain necessary to refresh and reapply instructions reliably.
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
## Global `model_instructions_file`
|
|
14
20
|
|
|
15
|
-
-
|
|
16
|
-
- `list_local_skills` searches discovered `SKILL.md` or `skill.md` bundles;
|
|
17
|
-
- `load_local_skill` returns one skill entrypoint plus a bounded relative file inventory;
|
|
18
|
-
- `list_local_commands` returns the effective command registry;
|
|
19
|
-
- `run_local_command` executes one registered command as a direct argv process.
|
|
21
|
+
The user-level configuration is:
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
For a target path, Machine Bridge chooses the nearest ancestor containing `.git` as the project scope root. If no Git marker is found, a target inside the configured workspace uses the workspace root; an unrestricted target outside the workspace uses the target directory.
|
|
23
|
+
```text
|
|
24
|
+
~/.config/machine-bridge-mcp/agent.json
|
|
25
|
+
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Example:
|
|
28
28
|
|
|
29
29
|
```json
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
30
|
+
{
|
|
31
|
+
"version": 1,
|
|
32
|
+
"model_instructions_file": "~/.config/machine-bridge-mcp/MODEL.md"
|
|
33
|
+
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
1. under unrestricted policy, read the first non-empty candidate in `CODEX_HOME` or `~/.codex`;
|
|
39
|
-
2. walk from the project scope root to the target directory;
|
|
40
|
-
3. in each directory, apply its optional `.machine-bridge/agent.json` first;
|
|
41
|
-
4. select only the first non-empty instruction candidate in that directory;
|
|
42
|
-
5. concatenate selected files from global to root to leaf, so later directories have higher precedence.
|
|
43
|
-
|
|
44
|
-
Empty candidates are skipped. Only one instruction file is selected per directory. This matches the important Codex override behavior: `AGENTS.override.md` suppresses `AGENTS.md` in the same directory, while deeper directories override broader guidance.
|
|
36
|
+
The file is loaded before repository guidance for every session and every target path. It must be a non-empty, regular, non-symbolic-link UTF-8 file and is bounded by the hard instruction-file limit. Relative values are resolved from the user's home directory.
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
`model_instructions_file` is global-only. A project `.machine-bridge/agent.json` cannot set or override it. It is read even under workspace-confined profiles because the user explicitly designated it as session configuration. Under those profiles, other user-manifest fields that would widen filesystem/skill/command scope are ignored; project instruction and command policy remains confined.
|
|
47
39
|
|
|
48
|
-
|
|
40
|
+
In remote mode, the selected instruction text necessarily traverses the user's Cloudflare Worker and the authorized MCP host as part of initialization. Do not put credentials or private data in an instruction file.
|
|
49
41
|
|
|
50
|
-
##
|
|
42
|
+
## Repository instruction precedence
|
|
51
43
|
|
|
52
|
-
|
|
44
|
+
For a target path, Machine Bridge chooses the nearest Git ancestor as scope root, falling back to the configured workspace or target directory. The default candidate priority in each directory is:
|
|
53
45
|
|
|
54
46
|
```json
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
"AGENTS.override.md",
|
|
60
|
-
"AGENTS.md",
|
|
61
|
-
".github/agent-guidance.md"
|
|
62
|
-
],
|
|
63
|
-
"instruction_max_bytes": 65536
|
|
64
|
-
}
|
|
47
|
+
[
|
|
48
|
+
"AGENTS.override.md",
|
|
49
|
+
"AGENTS.md"
|
|
50
|
+
]
|
|
65
51
|
```
|
|
66
52
|
|
|
67
|
-
|
|
53
|
+
Order:
|
|
54
|
+
|
|
55
|
+
1. `model_instructions_file`, when configured;
|
|
56
|
+
2. under unrestricted policy, the first non-empty candidate in `CODEX_HOME` or `~/.codex`;
|
|
57
|
+
3. project scope root through the target directory;
|
|
58
|
+
4. in each directory, apply `.machine-bridge/agent.json`, then select its first non-empty candidate.
|
|
59
|
+
|
|
60
|
+
Only one candidate contributes per directory. Deeper files have higher precedence. Empty files are skipped. The repository/global candidate budget defaults to 32 KiB and can be configured up to the hard 2 MiB ceiling. The separately designated model-instructions file retains its own file-size ceiling.
|
|
68
61
|
|
|
69
|
-
##
|
|
62
|
+
## Project manifest
|
|
70
63
|
|
|
71
|
-
A project
|
|
64
|
+
A project manifest lives at `.machine-bridge/agent.json`:
|
|
72
65
|
|
|
73
66
|
```json
|
|
74
67
|
{
|
|
@@ -85,122 +78,63 @@ A project configuration lives at `.machine-bridge/agent.json`. Relative skill ro
|
|
|
85
78
|
],
|
|
86
79
|
"commands": {
|
|
87
80
|
"check": {
|
|
88
|
-
"description": "Run
|
|
81
|
+
"description": "Run repository validation.",
|
|
89
82
|
"argv": ["npm", "run", "check"],
|
|
90
83
|
"cwd": ".",
|
|
91
84
|
"timeout_seconds": 600,
|
|
92
85
|
"allow_extra_args": false
|
|
93
|
-
},
|
|
94
|
-
"test-file": {
|
|
95
|
-
"description": "Run one test file selected by the caller.",
|
|
96
|
-
"argv": ["node", "--test"],
|
|
97
|
-
"cwd": ".",
|
|
98
|
-
"timeout_seconds": 120,
|
|
99
|
-
"allow_extra_args": true
|
|
100
86
|
}
|
|
101
87
|
}
|
|
102
88
|
}
|
|
103
89
|
```
|
|
104
90
|
|
|
105
|
-
Supported
|
|
106
|
-
|
|
107
|
-
- `version`: required and currently fixed at `1`;
|
|
108
|
-
- `instruction_files`: non-empty priority-ordered relative candidates;
|
|
109
|
-
- `instruction_max_bytes`: combined instruction budget from 1 KiB through 2 MiB;
|
|
110
|
-
- `skill_roots`: explicit skill directories; when present, this replaces inherited/default roots;
|
|
111
|
-
- `commands`: named command definitions merged with inherited commands.
|
|
112
|
-
|
|
113
|
-
Configuration is evaluated from global to project root to target directory. A deeper definition replaces the inherited value. A deeper command value of `null` removes that command:
|
|
114
|
-
|
|
115
|
-
```json
|
|
116
|
-
{
|
|
117
|
-
"version": 1,
|
|
118
|
-
"commands": {
|
|
119
|
-
"deploy": null
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Unknown fields are rejected so misspellings do not silently weaken the intended workflow.
|
|
91
|
+
Supported project fields are `version`, `instruction_files`, `instruction_max_bytes`, `skill_roots`, and `commands`. Unknown fields fail closed. Deeper manifests replace inherited instruction/skill settings, override commands by name, and can delete a command with `null`.
|
|
125
92
|
|
|
126
|
-
|
|
93
|
+
## Skill discovery and live refresh
|
|
127
94
|
|
|
128
|
-
|
|
95
|
+
Without explicit `skill_roots`, discovery scans:
|
|
129
96
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- `<target>/.agents/skills`;
|
|
133
|
-
- every ancestor's `.agents/skills` through the project root;
|
|
97
|
+
- target-to-root `.agents/skills` directories;
|
|
134
98
|
- under unrestricted policy, `~/.agents/skills`;
|
|
135
99
|
- under unrestricted policy on Unix-like systems, `/etc/codex/skills`.
|
|
136
100
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
Machine Bridge recursively finds directories containing `SKILL.md` or `skill.md`. The entrypoint must contain simple YAML front matter with non-empty `name` and `description` fields:
|
|
101
|
+
A skill directory contains `SKILL.md` or `skill.md` with simple front matter:
|
|
140
102
|
|
|
141
103
|
```markdown
|
|
142
104
|
---
|
|
143
105
|
name: release-review
|
|
144
106
|
description: Review a release without publishing it.
|
|
145
107
|
---
|
|
146
|
-
|
|
147
|
-
# Workflow
|
|
148
|
-
|
|
149
|
-
...
|
|
150
108
|
```
|
|
151
109
|
|
|
152
|
-
Invalid
|
|
153
|
-
|
|
154
|
-
Symlinked skill folders are followed, matching Codex behavior. Under workspace-confined profiles, the canonical symlink target must remain inside the workspace. Skill entrypoint files themselves may not be symbolic links. Directory traversal, cycles, entry count, depth, content, and returned file inventory are bounded.
|
|
155
|
-
|
|
156
|
-
## Progressive disclosure
|
|
110
|
+
The entrypoint requires non-empty `name` and `description`. Invalid bundles are skipped with bounded warnings. Symlinked skill directories are followed after canonical policy validation; symbolic-link entrypoint files are rejected. Traversal, depth, entries, summaries, content, and inventory are bounded.
|
|
157
111
|
|
|
158
|
-
`agent_context`
|
|
112
|
+
No persistent skill index is trusted as authoritative. `agent_context`, `list_local_skills`, and `resolve_task_capabilities` rescan the effective roots. A refresh fingerprint changes when instruction hashes, skill hashes, command definitions, or relevant configuration changes. Newly created or edited skills are therefore visible without restarting the daemon or changing the MCP tool catalog.
|
|
159
113
|
|
|
160
|
-
|
|
114
|
+
## Progressive disclosure and task selection
|
|
161
115
|
|
|
162
|
-
|
|
163
|
-
- metadata and a content hash;
|
|
164
|
-
- a bounded relative inventory of files in the skill directory.
|
|
116
|
+
`agent_context` returns bounded skill metadata. `load_local_skill` returns full instructions only for one selected bundle. `resolve_task_capabilities` tokenizes the current task, ranks skill names/descriptions and command names/descriptions/argv, returns matches with scores, and loads the leading skill only when its relevance threshold is met.
|
|
165
117
|
|
|
166
|
-
|
|
118
|
+
This ranking is deterministic local assistance, not semantic certainty. The model must still evaluate whether the selected skill applies. Machine Bridge does not execute skill scripts implicitly and does not fabricate a dynamically named MCP tool per skill.
|
|
167
119
|
|
|
168
|
-
## Registered
|
|
120
|
+
## Registered commands
|
|
169
121
|
|
|
170
|
-
`run_local_command`
|
|
122
|
+
`run_local_command` uses direct argv spawning rather than a shell. The manifest controls working directory, timeout ceiling, and whether caller arguments are accepted. A caller may reduce but not increase the timeout.
|
|
171
123
|
|
|
172
|
-
|
|
124
|
+
Registered commands are workflow aliases, not an approval boundary or sandbox. Package scripts, interpreters, compilers, and executables retain local-user authority. Use `review` or `edit`, or external VM/container isolation, for untrusted content.
|
|
173
125
|
|
|
174
|
-
|
|
175
|
-
- a caller can reduce the timeout but cannot increase it beyond `timeout_seconds`;
|
|
176
|
-
- the working directory is canonicalized and remains subject to active path policy;
|
|
177
|
-
- the command receives the isolated or full environment selected by the Machine Bridge profile.
|
|
126
|
+
## Recommended host workflow
|
|
178
127
|
|
|
179
|
-
|
|
128
|
+
1. consume MCP initialization instructions;
|
|
129
|
+
2. call `resolve_task_capabilities` with the complete user task and target path;
|
|
130
|
+
3. apply returned global/project instructions;
|
|
131
|
+
4. follow the selected skill only after checking relevance;
|
|
132
|
+
5. prefer registered commands for stable workflows;
|
|
133
|
+
6. use structured application/browser tools where applicable;
|
|
134
|
+
7. inspect before mutation or submission and report operations performed.
|
|
180
135
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
- `run_process` for direct argv execution;
|
|
184
|
-
- `exec_command` for shell syntax under shell-enabled policy;
|
|
185
|
-
- `start_job` for durable multi-step work;
|
|
186
|
-
- `stage_job` for local operator review and later approval.
|
|
187
|
-
|
|
188
|
-
## Recommended remote workflow
|
|
189
|
-
|
|
190
|
-
A remote coding task should normally follow this sequence:
|
|
191
|
-
|
|
192
|
-
1. call `agent_context` with the file or directory being changed;
|
|
193
|
-
2. apply returned instructions in precedence order;
|
|
194
|
-
3. load only skills relevant to the task;
|
|
195
|
-
4. inspect files before editing;
|
|
196
|
-
5. prefer a registered command for validation or a standard workflow;
|
|
197
|
-
6. use direct or shell execution only when the registry does not cover the operation;
|
|
198
|
-
7. report files changed and commands run.
|
|
199
|
-
|
|
200
|
-
This approximates local coding-agent context loading while keeping MCP transport, local policy, and host policy as explicit independent boundaries.
|
|
136
|
+
Machine Bridge can automatically discover, refresh, rank, and load capabilities. Actual invocation remains a host/model decision. This boundary cannot be removed by server architecture alone.
|
|
201
137
|
|
|
202
138
|
## Security and failure behavior
|
|
203
139
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
Instruction and skill contents are untrusted operational text. They may contain prompt injection or unsafe command guidance. Machine Bridge exposes the content; it does not certify or approve it.
|
|
140
|
+
Instruction and skill text is untrusted operational content and may contain prompt injection or destructive guidance. The bridge exposes it but does not certify it. Paths, files, roots, counts, byte budgets, argv, timeouts, and result sizes are bounded. Escaping paths, ambiguous skill names, malformed metadata, invalid configuration, and missing commands fail explicitly or produce bounded warnings.
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -32,20 +32,35 @@ A canonical workspace receives an independent profile, Worker name, secret set,
|
|
|
32
32
|
- process-session buffers and stdin lifecycle;
|
|
33
33
|
- layered fixed runtime diagnostics;
|
|
34
34
|
- local resource aliases and detached managed-job coordination;
|
|
35
|
-
-
|
|
35
|
+
- session/bootstrap instruction discovery, live capability ranking, and registered-command execution coordination;
|
|
36
|
+
- structured local application and existing-profile browser automation coordination;
|
|
36
37
|
- mutation serialization;
|
|
37
38
|
- child-process tracking and cancellation;
|
|
38
39
|
- output, traversal, concurrency, and time limits.
|
|
39
40
|
|
|
40
41
|
`RelayConnection` owns remote WebSocket transport, authenticated `hello_ack` readiness, heartbeat liveness, reconnect backoff, and outage logging. Stdio mode invokes `LocalRuntime` directly without that adapter.
|
|
41
42
|
|
|
42
|
-
### Agent context
|
|
43
|
+
### Agent context and capability resolver
|
|
43
44
|
|
|
44
|
-
`AgentContextManager`
|
|
45
|
+
`AgentContextManager` discovers the nearest Git/workspace scope, applies the user configuration and hierarchical `.machine-bridge/agent.json` files, selects global/root-to-target instructions, discovers bounded filesystem skills, and resolves registered commands. A global `model_instructions_file` is a separate user-designated session source and cannot be overridden by a project.
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
`session_bootstrap` is requested during both stdio and remote MCP initialization. The Worker delegates this read to the connected daemon with a short bounded timeout; failure falls back to static server instructions rather than blocking initialization indefinitely. `resolve_task_capabilities` performs a fresh deterministic scan and ranks skill/command metadata for the current task. Application and browser capability metadata is added by `LocalRuntime`.
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
The MCP catalog remains static: local skills and commands do not become dynamically named tools. This avoids stale host catalog caches and keeps Worker/stdio schema parity. Progressive disclosure separates discovery, instruction loading, and execution authority. A refresh fingerprint is descriptive rather than a cache-validity guarantee.
|
|
50
|
+
|
|
51
|
+
See [Session instructions, skills, commands, and capability discovery](AGENT_CONTEXT.md).
|
|
52
|
+
|
|
53
|
+
### Application automation manager
|
|
54
|
+
|
|
55
|
+
`AppAutomationManager` owns installed-application discovery, OS launching, and structured UI automation. macOS inspection/actions execute fixed JXA implementation code through `osascript` and expose only typed selectors/actions. The caller cannot provide script source. OS Accessibility/TCC remains an independent boundary.
|
|
56
|
+
|
|
57
|
+
### Browser extension and machine broker
|
|
58
|
+
|
|
59
|
+
`BrowserBridgeManager` owns a loopback HTTP/WebSocket broker. The first runtime for the machine-level state root becomes broker owner; additional workspaces and stdio runtimes authenticate to `/runtime` and proxy through the same extension socket. This preserves one extension pairing while allowing multiple local MCP runtimes.
|
|
60
|
+
|
|
61
|
+
The packaged Manifest V3 extension runs in the user's existing Chromium profile. Its service worker is transport/orchestration only; a fixed packaged `page-automation.js` module is injected into selected frames to expose DOM operations in the correct execution world. It supports accessible frames, open Shadow DOM roots, structured actions, multi-field forms, resource-backed file inputs, and screenshots. The extension never receives arbitrary caller-provided code. The broker validates loopback hostnames, extension origin, bearer subprotocols, message sizes, concurrency, and timeouts. Pairing material is owner-only and omitted from MCP/log output.
|
|
62
|
+
|
|
63
|
+
See [Local application and browser automation](LOCAL_AUTOMATION.md).
|
|
49
64
|
|
|
50
65
|
### Managed job runner
|
|
51
66
|
|
|
@@ -105,6 +120,9 @@ flowchart LR
|
|
|
105
120
|
L[Local MCP client] -->|stdio JSON-RPC| R
|
|
106
121
|
R -->|canonical workspace tools| F[Selected workspace]
|
|
107
122
|
R -->|optional direct/shell processes| P[Local user / OS / network]
|
|
123
|
+
R -->|structured Accessibility actions| A[Local applications]
|
|
124
|
+
R -->|authenticated loopback broker| B[Existing-profile browser extension]
|
|
125
|
+
B -->|DOM and visible UI authority| WB[Web pages and browser tabs]
|
|
108
126
|
R -->|durable accepted plan| J[Detached managed-job runner]
|
|
109
127
|
J -->|private copies| LR[Local resource files]
|
|
110
128
|
J -->|argv/stdin/env| P
|
|
@@ -124,7 +142,7 @@ Remote OAuth determines which client may call tools. Local stdio access relies o
|
|
|
124
142
|
4. The user verifies client name and redirect URI and enters the connection password.
|
|
125
143
|
5. The Worker creates a five-minute code bound to client, redirect, resource, scope, and PKCE challenge.
|
|
126
144
|
6. A valid verifier exchanges the one-time code for an expiring bearer token; only its hash is stored.
|
|
127
|
-
7. The MCP client initializes and negotiates a supported protocol version.
|
|
145
|
+
7. The MCP client initializes and negotiates a supported protocol version. When the daemon advertises `session_bootstrap`, the Worker requests bounded local instructions and appends them to the initialization result; failure degrades to static instructions.
|
|
128
146
|
8. `tools/list` is derived from the active daemon handshake; without a daemon, only `server_info` is advertised.
|
|
129
147
|
9. `tools/call` receives a random relay call ID and is bound to the current socket and authenticated client request key.
|
|
130
148
|
10. The runtime validates policy and arguments, executes the tool, and returns a bounded result.
|
|
@@ -137,7 +155,7 @@ Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cance
|
|
|
137
155
|
## Stdio request lifecycle
|
|
138
156
|
|
|
139
157
|
1. The local client launches `machine-mcp stdio` with a workspace and profile.
|
|
140
|
-
2. The server negotiates one of the supported MCP versions.
|
|
158
|
+
2. The server negotiates one of the supported MCP versions and appends bounded local `session_bootstrap` instructions when available.
|
|
141
159
|
3. Tool discovery is generated from the same catalog and policy used by remote mode.
|
|
142
160
|
4. Each call receives an internal random call ID used only for cancellation and process tracking.
|
|
143
161
|
5. Input is parsed as incrementally bounded newline-delimited JSON-RPC, so an oversized line is discarded before unbounded buffering and the next line can still be processed. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
|
|
@@ -194,7 +212,7 @@ The Worker also treats a new connection as a bounded candidate until it authenti
|
|
|
194
212
|
|
|
195
213
|
## Persistence
|
|
196
214
|
|
|
197
|
-
Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. State, managed-job manager, and detached runner commits share a bounded retrying atomic-replace primitive for transient Windows `EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY` sharing failures. Managed-job manager and detached runner use one shared no-follow, size-bounded regular-file reader; transition and recovery locks use one PID-lock primitive with bounded stale-lock reclamation and optional runner handoff. Malformed or oversized state becomes a bounded-count `.corrupt-*` backup. Resource paths are omitted from redacted status output. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
215
|
+
Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. Machine-level browser pairing state is owner-only and shared across workspace runtimes through the local broker; its bearer token is not part of workspace state responses. State, managed-job manager, and detached runner commits share a bounded retrying atomic-replace primitive for transient Windows `EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY` sharing failures. Managed-job manager and detached runner use one shared no-follow, size-bounded regular-file reader; transition and recovery locks use one PID-lock primitive with bounded stale-lock reclamation and optional runner handoff. Malformed or oversized state becomes a bounded-count `.corrupt-*` backup. Resource paths are omitted from redacted status output. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
198
216
|
|
|
199
217
|
Active managed jobs persist an owner-only plan, status, runner PID, and bounded runner diagnostics. Terminal jobs delete the full plan and retain only bounded status/redacted results for up to seven days. This balances crash cleanup with minimization of scripts, stdin, argv, environment overrides, and resource source paths.
|
|
200
218
|
|
|
@@ -219,5 +237,7 @@ Cloudflare sampling is size control rather than an audit log. The project intent
|
|
|
219
237
|
- guaranteed finally cleanup across permanent power, disk, credential, network, or endpoint-security failure;
|
|
220
238
|
- bypassing MCP-host, connector, operating-system, or endpoint-security policy;
|
|
221
239
|
- PTY/terminal emulation;
|
|
222
|
-
- model-level prompt-injection prevention;
|
|
240
|
+
- model-level prompt-injection prevention or semantic validation of browser/application actions;
|
|
241
|
+
- universal desktop UI automation beyond the implemented OS Accessibility backend;
|
|
242
|
+
- scripting browser-internal/enterprise-blocked pages or inaccessible cross-origin frames;
|
|
223
243
|
- multi-user tenancy in one Worker deployment.
|
package/docs/CLIENTS.md
CHANGED
|
@@ -52,6 +52,14 @@ It is therefore an optional compatibility and reuse surface, not a replacement f
|
|
|
52
52
|
|
|
53
53
|
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.
|
|
54
54
|
|
|
55
|
+
## Automatic capability selection
|
|
56
|
+
|
|
57
|
+
MCP server instructions and `resolve_task_capabilities` give the host a current view of global/project instructions, skills, registered commands, applications, and browser capability. The resolver rescans rather than relying on a stale dynamic tool list and can return the best matching skill instructions in one call.
|
|
58
|
+
|
|
59
|
+
The host still owns the agent loop. ChatGPT web may use the recommendation automatically, ask for confirmation, expose only part of the catalog, or ignore server instructions. No MCP implementation can guarantee automatic invocation from the server side. Machine Bridge models that limitation explicitly instead of treating a recommendation as execution.
|
|
60
|
+
|
|
61
|
+
For browser tasks, the remote host reaches the local extension through the Worker and daemon. The extension controls the existing Chromium profile; it is not a separate Playwright profile.
|
|
62
|
+
|
|
55
63
|
## Generate local configuration
|
|
56
64
|
|
|
57
65
|
The default profile is `full`:
|
package/docs/ENGINEERING.md
CHANGED
|
@@ -9,7 +9,10 @@ This document records project-wide decisions that must survive individual fixes,
|
|
|
9
9
|
3. **Machine Bridge authority and host authority are separate.** `full` removes Machine Bridge's own policy, path, shell, and environment restrictions. It cannot override an MCP host, connector gateway, operating system, endpoint-security product, cloud IAM, remote authentication, or `sudo`.
|
|
10
10
|
4. **Publication surfaces contain no real environment metadata.** Source, tests, fixtures, examples, release notes, filenames, package contents, tags, and release assets use synthetic identifiers and reserved example domains.
|
|
11
11
|
5. **Secrets are never operational log data.** Tool arguments, command text, stdin, stdout, stderr, file content, OAuth bodies, credentials, and local resource values are not logged.
|
|
12
|
-
6. **A release is one version across all surfaces.** Package metadata, Worker version, Git tag, GitHub Release, npm version, documentation, and deployed health version must agree before a release is considered complete.
|
|
12
|
+
6. **A release is one version across all surfaces.** Package metadata, Worker version, browser-extension version/name, Git tag, GitHub Release, npm version, documentation, and deployed health version must agree before a release is considered complete.
|
|
13
|
+
7. **Generic local automation is structured, not arbitrary evaluation.** Browser/application features may expose broad user authority under canonical `full`, but must not accept caller-provided JavaScript, AppleScript, JXA, or extension code.
|
|
14
|
+
8. **Daily-browser integration uses the existing profile.** The supported primary browser path is the packaged authenticated extension and machine-level loopback broker, preserving current tabs/login state; a separate automation profile is not an equivalent replacement.
|
|
15
|
+
9. **Pairing and resource secrets are not conversation or log data.** Tokens and injected local-resource values must not be returned, embedded in URLs, or written to operational logs.
|
|
13
16
|
|
|
14
17
|
A proposed change that conflicts with an invariant requires an explicit owner decision and corresponding documentation update. It must not be hidden inside an unrelated refactor.
|
|
15
18
|
|
|
@@ -158,6 +161,7 @@ A thorough review asks:
|
|
|
158
161
|
- Are all success, failure, cancellation, timeout, disconnect, restart, and recovery branches bounded and tested?
|
|
159
162
|
- Are logs actionable, rate-limited, and privacy-preserving?
|
|
160
163
|
- Are persistent files atomic, owner-only, size-bounded, and symlink-aware?
|
|
164
|
+
- Can browser/app automation be expressed without arbitrary evaluation, and are pairing/resource values absent from results and logs?
|
|
161
165
|
- Can a stale PID, stale socket, duplicate request, partial write, or ambiguous remote response violate integrity?
|
|
162
166
|
- Are package, CI, Worker, service, and release behavior tested on every supported platform?
|
|
163
167
|
- Does the complete diff contain any real identifier, path, host, account, or credential-shaped value?
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Local application and browser automation
|
|
2
|
+
|
|
3
|
+
Machine Bridge exposes two structured local-automation backends under the canonical `full` profile:
|
|
4
|
+
|
|
5
|
+
1. operating-system application discovery and macOS Accessibility actions;
|
|
6
|
+
2. a Chromium extension connected to a loopback broker for the user's existing browser profile, windows, tabs, cookies, and login state.
|
|
7
|
+
|
|
8
|
+
Neither backend accepts arbitrary caller-supplied AppleScript, JavaScript, or browser-extension code. The MCP surface is a bounded set of typed discovery, inspection, action, form, screenshot, and file-upload operations.
|
|
9
|
+
|
|
10
|
+
## Why the browser backend is an extension
|
|
11
|
+
|
|
12
|
+
Launching a separate automation browser is predictable but loses the user's ordinary profile, active tabs, existing login state, browser extensions, and familiar window. Machine Bridge therefore uses a packaged Manifest V3 extension as the primary backend. It is loaded once into the user's Chromium-family browser and paired with a loopback-only local broker.
|
|
13
|
+
|
|
14
|
+
The extension works with the current profile and supports:
|
|
15
|
+
|
|
16
|
+
- listing existing tabs;
|
|
17
|
+
- reading the current serialized DOM HTML from the main frame or accessible subframes;
|
|
18
|
+
- discovering interactive controls, labels, roles, placeholders, and field metadata across accessible frames and open Shadow DOM roots;
|
|
19
|
+
- structured navigation, click, focus, fill, select, check, uncheck, key press, and form submission;
|
|
20
|
+
- multi-field complex form filling in one operation, bounded to 200 fields and 4 MiB of aggregate text;
|
|
21
|
+
- populating file inputs from registered local resource files;
|
|
22
|
+
- visible-tab screenshots.
|
|
23
|
+
|
|
24
|
+
Current support targets Chrome, Chromium, Microsoft Edge, Brave, Vivaldi, and compatible Chromium browsers. After upgrading Machine Bridge, reload the unpacked extension from the browser extensions page so its packaged scripts match the new runtime. Browser-internal pages, extension stores, some PDF/plugin viewers, cross-origin frames denied to the extension, and pages restricted by enterprise policy may not be scriptable.
|
|
25
|
+
|
|
26
|
+
## One-time browser setup
|
|
27
|
+
|
|
28
|
+
Keep `machine-mcp` running, then use either the MCP tool `pair_browser_extension` or the local CLI:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
machine-mcp browser setup
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The command prints the packaged extension directory and opens the local pairing page. In the browser:
|
|
35
|
+
|
|
36
|
+
1. open the extensions page;
|
|
37
|
+
2. enable Developer mode;
|
|
38
|
+
3. choose **Load unpacked** and select the printed `browser-extension` directory;
|
|
39
|
+
4. return to the local pairing page.
|
|
40
|
+
|
|
41
|
+
The first pairing completes automatically. If the extension is already paired to different local state, the page asks for an explicit user gesture: click the Machine Bridge extension icon while the pairing page is active to confirm replacement. This prevents another localhost page from silently overwriting an established pairing. The extension badge shows `ON` when connected; clicking the icon from another page opens the saved local pairing/status page.
|
|
42
|
+
|
|
43
|
+
`Load unpacked` is the self-hosted/development installation path. A mass-market release should package the same extension source as a signed Chrome Web Store and/or Microsoft Edge Add-ons build, so ordinary users install it through the browser store without enabling Developer mode. This repository change does not publish a store listing.
|
|
44
|
+
|
|
45
|
+
The content script reads pairing material only from the loopback page and stores it in browser-local extension storage. Check status with:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
machine-mcp browser status
|
|
49
|
+
machine-mcp browser path
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The broker is machine-global rather than workspace-global. One local owner listens on the loopback port, and additional Machine Bridge runtimes authenticate to that broker and proxy requests through the same extension connection. This prevents multiple workspaces or stdio clients from repeatedly stealing the browser connection.
|
|
53
|
+
|
|
54
|
+
## Browser tools
|
|
55
|
+
|
|
56
|
+
- `browser_status` reports broker role, extension connection, supported operations, pairing URL, and extension path without returning the pairing token.
|
|
57
|
+
- `pair_browser_extension` opens the local pairing page and returns setup steps.
|
|
58
|
+
- `browser_list_tabs` lists current tabs.
|
|
59
|
+
- `browser_get_source` returns bounded current DOM HTML for selected frames.
|
|
60
|
+
- `browser_inspect_page` returns bounded interactive-element metadata.
|
|
61
|
+
- `browser_action` performs one structured navigation or DOM action. Navigation accepts absolute `http`, `https`, or `file` URLs; script/data schemes are rejected.
|
|
62
|
+
- `browser_fill_form` fills up to 200 fields and can submit once.
|
|
63
|
+
- `browser_upload_files` sets a file input from up to eight registered local resources.
|
|
64
|
+
- `browser_screenshot` returns native MCP image content.
|
|
65
|
+
|
|
66
|
+
Selectors can use CSS, ID, field name, label text, visible text, ARIA/implicit role, placeholder, and a zero-based match index. The fixed page module traverses open Shadow DOM roots; closed shadow roots remain inaccessible. For frame-specific work, inspect all frames first and then pass `frame_id` to an action.
|
|
67
|
+
|
|
68
|
+
## Sensitive form values and files
|
|
69
|
+
|
|
70
|
+
Ordinary `value` arguments travel through the MCP host, the Worker in remote mode, and the local daemon. For passwords, tokens, personal data, or uploaded files, register a local resource from the terminal:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
machine-mcp resource add account-password /path/to/owner-only/password.txt
|
|
74
|
+
machine-mcp resource add application-pdf /path/to/document.pdf
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then use `value_resource` for a text field or `resources` for `browser_upload_files`. The local daemon reads the resource only when executing the operation. Results report the alias and outcome, not the resource content. This reduces model-context and result exposure, but the destination page receives the value by design and can transmit it according to its own behavior.
|
|
78
|
+
|
|
79
|
+
## Application tools
|
|
80
|
+
|
|
81
|
+
- `list_local_applications` discovers installed applications or launchers.
|
|
82
|
+
- `open_local_application` opens an application, URL, or document through the OS launcher.
|
|
83
|
+
- `inspect_local_application` returns a bounded macOS Accessibility tree.
|
|
84
|
+
- `operate_local_application` performs a structured Accessibility action.
|
|
85
|
+
|
|
86
|
+
macOS UI inspection and actions require Accessibility permission for the Node/Machine Bridge process. Application discovery and opening are available on supported desktop platforms; structured UI inspection currently targets macOS. The application backend uses fixed JXA implementation code. The caller selects only an application, structured selector, action, and optional bounded value. `include_values` never returns values for secure-text roles or controls whose metadata indicates passwords, tokens, one-time codes, or payment-card secrets.
|
|
87
|
+
|
|
88
|
+
## Capability discovery and automatic selection
|
|
89
|
+
|
|
90
|
+
`resolve_task_capabilities` rescans instruction files, skills, registered commands, and relevant local automation metadata on every call. It ranks matching skills and commands, optionally loads the best skill, and recommends browser/application tools when the task indicates them.
|
|
91
|
+
|
|
92
|
+
This is the strongest reliable server-side automation boundary available through MCP: discovery, refresh, ranking, and progressive skill loading are automatic. The MCP host still owns the model loop and decides whether a recommended tool is exposed, approved, or invoked. Machine Bridge cannot force ChatGPT web or another host to make a call that the host declines.
|
|
93
|
+
|
|
94
|
+
## Security model
|
|
95
|
+
|
|
96
|
+
The browser extension has broad page access because generic source inspection and complex forms require it. An authorized `full` client can read pages visible to the extension and perform user-visible actions with the user's logged-in authority. A malicious webpage can contain prompt-injection text, deceptive controls, or actions with financial, legal, account, or privacy consequences.
|
|
97
|
+
|
|
98
|
+
The implementation reduces avoidable risk as follows:
|
|
99
|
+
|
|
100
|
+
- all browser/app tools are `full`-only;
|
|
101
|
+
- no arbitrary evaluation or caller-provided script source is accepted;
|
|
102
|
+
- loopback HTTP validates `Host`; extension WebSockets require a random bearer subprotocol and a `chrome-extension://` origin;
|
|
103
|
+
- runtime-to-broker connections require a separate authenticated subprotocol;
|
|
104
|
+
- the pairing token is stored owner-only, embedded only in the non-cacheable local pairing page, and omitted from MCP results and logs;
|
|
105
|
+
- an established extension pairing cannot be silently replaced by another localhost page; replacement requires clicking the extension action on the active pairing page;
|
|
106
|
+
- arguments, form values, page source, screenshots, and results are not operational log data;
|
|
107
|
+
- message, source, form-field, upload, traversal, concurrency, proxy-route, and timeout limits are enforced;
|
|
108
|
+
- MCP cancellation clears local and broker pending state and propagates a cancellation signal to the extension; an action already delivered to a page or application may still have completed;
|
|
109
|
+
- resource-backed text and files are not returned after injection.
|
|
110
|
+
|
|
111
|
+
These controls do not turn the browser into a sandbox and do not validate business intent. Inspect the page and final values before high-impact submissions. Host confirmation and narrow profiles remain independent controls.
|
package/docs/LOGGING.md
CHANGED
|
@@ -78,10 +78,18 @@ The implementation omits:
|
|
|
78
78
|
- file, patch, image, and temporary-file content;
|
|
79
79
|
- OAuth request bodies;
|
|
80
80
|
- connection passwords, daemon secrets, authorization codes, and access tokens;
|
|
81
|
-
- registered resource values and source paths
|
|
81
|
+
- registered resource values and source paths;
|
|
82
|
+
- browser pairing tokens, page URLs/source, DOM metadata, form values, uploaded file bytes, and screenshots;
|
|
83
|
+
- application names, Accessibility trees, selectors, and entered values.
|
|
82
84
|
|
|
83
85
|
Unexpected infrastructure 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 operational logs.
|
|
84
86
|
|
|
87
|
+
## Local automation events
|
|
88
|
+
|
|
89
|
+
Browser broker ownership, extension connection, and persistent unavailability are infrastructure state. Ordinary tab/source/action/form/upload/screenshot calls remain per-tool debug events and never log their arguments or results. Pairing tokens are excluded from all structured fields.
|
|
90
|
+
|
|
91
|
+
Application discovery and Accessibility operations follow the same rule: permission or runtime failures are classified, while app names, UI trees, selectors, and values are not operational log data.
|
|
92
|
+
|
|
85
93
|
## Bounding and redaction
|
|
86
94
|
|
|
87
95
|
Messages, strings, object depth, object key counts, array item counts, and serialized field payloads are bounded. Control characters and Unicode display controls are neutralized. Fields with secret-like names, path-like keys, and known token formats are recursively redacted.
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -30,6 +30,33 @@ A brief relay interruption is retried automatically and is visible only with `--
|
|
|
30
30
|
|
|
31
31
|
Use `--verbose` only when close codes, close reasons, heartbeat timeouts, and retry delays are needed for diagnosis. A close code of 1006 means the transport ended without a normal close handshake; it does not by itself identify the cause.
|
|
32
32
|
|
|
33
|
+
## Browser extension setup and diagnosis
|
|
34
|
+
|
|
35
|
+
The full-profile daemon starts the loopback browser broker automatically. Install/pair the packaged extension once:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
machine-mcp browser setup
|
|
39
|
+
machine-mcp browser status
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`browser setup` prints the unpacked-extension path and opens the local pairing page. Load that directory from the Chromium extensions page with Developer mode enabled. The pairing page contains the token only in non-cacheable loopback HTML; the CLI and MCP result do not print it. A first pairing is automatic. When local pairing state was reset or moved, click the extension icon on the active pairing page to authorize replacing the extension's previous broker configuration.
|
|
43
|
+
|
|
44
|
+
Diagnosis:
|
|
45
|
+
|
|
46
|
+
| Result | Interpretation |
|
|
47
|
+
|---|---|
|
|
48
|
+
| broker not reachable | no full-profile runtime owns the machine broker, state is stale, or a local security product blocked loopback listening |
|
|
49
|
+
| broker running, extension disconnected | extension not loaded/enabled, pairing not completed, browser profile not running, or MV3 worker reconnect failed |
|
|
50
|
+
| tab list works but page action fails | restricted browser-internal page, inaccessible frame, stale selector, page navigation, enterprise policy, or page script interference |
|
|
51
|
+
| `frame_id` action fails | frame was replaced or is not accessible; inspect frames again |
|
|
52
|
+
| resource-backed upload fails | resource unavailable/too large, input is not `type=file`, page replaced the input, or the site rejects the file |
|
|
53
|
+
|
|
54
|
+
The machine-level broker permits multiple workspace daemons/stdio clients to share one extension. A second runtime becomes an authenticated broker client rather than selecting a new port or replacing the extension. Its owner-only `browser-bridge.json` pairing record is a recognized state-root entry, so validated uninstall can remove the complete Machine Bridge state root without treating the pairing record as unrelated data. Restarting every runtime is not normally required after adding a skill or changing a form page; capability and DOM discovery are live.
|
|
55
|
+
|
|
56
|
+
## macOS application automation
|
|
57
|
+
|
|
58
|
+
Application UI inspection/actions require Accessibility permission for the Node/Machine Bridge process. Grant it in macOS Privacy & Security, then retry `inspect_local_application`. Opening applications does not require the same Accessibility authority. If an app changes UI hierarchy or localizes labels, inspect again and use role/identifier/index selectors instead of assuming a stale title.
|
|
59
|
+
|
|
33
60
|
## Logs
|
|
34
61
|
|
|
35
62
|
Remote autostart definitions prefer a stable PATH alias that resolves to the currently running Node executable and persist a sanitized absolute-only service `PATH` containing the Node/CLI directories, the installer's absolute PATH entries, and platform defaults. This avoids versioned Homebrew-style paths becoming invalid after upgrades and prevents launchd/systemd from falling back to a minimal system-only PATH. Re-run `machine-mcp service install` after changing Node installation families or PATH layout. A service-style `--daemon-only` start that finds the same workspace daemon already running is an idempotent no-op: it exits successfully without repeating warnings or readiness output; explicit policy/secret/change requests still report that changes were not applied. 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. On a logging-schema upgrade, bounded prior content is moved into `daemon.out.legacy.log` and `daemon.err.legacy.log`; use the active filenames when diagnosing current behavior.
|
|
@@ -131,6 +158,9 @@ Defense-in-depth limits include:
|
|
|
131
158
|
- managed-job timeout: 1–3,600 seconds per step;
|
|
132
159
|
- managed-job output: 64 KiB per stream and 256 KiB total captured across one job;
|
|
133
160
|
- registered resources: 64, 1 MiB each, 8 MiB referenced per job;
|
|
161
|
+
- browser source: 4 MiB; browser WebSocket messages: 8 MiB; pending browser requests: 32 per runtime;
|
|
162
|
+
- browser forms: 200 fields, 128 KiB per text value, and 4 MiB aggregate text; upload: eight resources and 5 MiB total;
|
|
163
|
+
- application Accessibility inspection: 500 elements and depth 12; action text: 4,000 characters;
|
|
134
164
|
- job-scoped temporary files: 16 files, 512 KiB total content.
|
|
135
165
|
|
|
136
166
|
## Upgrade behavior
|
package/docs/PRIVACY.md
CHANGED
|
@@ -34,7 +34,8 @@ Before committing or publishing:
|
|
|
34
34
|
- inspect the complete staged diff, including tests, snapshots, examples, release notes, and generated metadata;
|
|
35
35
|
- use reserved example domains and neutral aliases;
|
|
36
36
|
- run `npm run privacy:check`, `npm run check`, and `npm pack --dry-run`;
|
|
37
|
-
- treat paths, host aliases, usernames, and
|
|
37
|
+
- treat paths, host aliases, usernames, codenames, real browser URLs/page captures, application names tied to a user, and form data as private metadata even when they are not authentication secrets;
|
|
38
|
+
- keep browser pairing-state files and captured source/screenshots out of fixtures, documentation, support logs, and release assets.
|
|
38
39
|
|
|
39
40
|
The scanner is heuristic. It cannot identify every personal or organizational name, transformed value, image, archive, binary fixture, or data already present in Git history.
|
|
40
41
|
|