machine-bridge-mcp 0.17.0 → 0.17.1
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 +13 -0
- package/README.md +11 -1
- package/SECURITY.md +9 -1
- package/browser-extension/manifest.json +2 -2
- package/docs/ARCHITECTURE.md +4 -2
- package/docs/CLIENTS.md +2 -0
- package/docs/GETTING_STARTED.md +400 -0
- package/docs/MULTI_ACCOUNT.md +406 -0
- package/package.json +1 -1
- package/src/worker/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.1 - 2026-07-14
|
|
4
|
+
|
|
5
|
+
### Installation and first use
|
|
6
|
+
|
|
7
|
+
- Add an end-to-end installation and first-use guide covering transport selection, authority profiles, prerequisites, released and source installation, first remote deployment, current ChatGPT developer-mode connection, stdio configuration, existing-profile browser pairing, verification, routine operation, multi-workspace use, upgrades, layered troubleshooting, and fail-closed removal.
|
|
8
|
+
- Link the detailed guide from the README while retaining the compact command reference, and clarify how to distinguish package, Cloudflare, daemon, MCP-host, operating-system, and browser failures.
|
|
9
|
+
|
|
10
|
+
### Multi-client and multi-account boundary
|
|
11
|
+
|
|
12
|
+
- Document that the existing Worker supports multiple OAuth client registrations and access tokens but does not provide isolated human/service accounts: all successful authorizations use one per-workspace connection password and share the same policy and daemon authority.
|
|
13
|
+
- Define a principal-aware evolution path that keeps OAuth clients separate from principals, memberships, and named grants; uses capability intersection and dual Worker/local enforcement; adds targeted revocation and per-principal quotas; and retains one bridge/Durable Object per workspace or trust domain.
|
|
14
|
+
- Make the security and architecture contracts explicit: mutually untrusted users require separate bridge instances and preferably separate low-privilege OS, container, or VM boundaries because application-level roles cannot isolate local process, shell, browser, or credential-store authority.
|
|
15
|
+
|
|
3
16
|
## 0.17.0 - 2026-07-13
|
|
4
17
|
|
|
5
18
|
### Project governance and contribution flow
|
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@ 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, process, diagnostic, and managed-job operations execute in the local runtime.
|
|
25
25
|
|
|
26
|
+
New users should follow the end-to-end [installation and first-use guide](docs/GETTING_STARTED.md). Before sharing a deployment or connecting several accounts, read [multi-client, multi-account, and tenancy architecture](docs/MULTI_ACCOUNT.md).
|
|
27
|
+
|
|
26
28
|
## Default behavior and policy profiles
|
|
27
29
|
|
|
28
30
|
A newly selected workspace starts with the maximum-permission `full` profile for low-friction operation:
|
|
@@ -45,6 +47,8 @@ This default prioritizes usability, not least privilege. `run_process` and proce
|
|
|
45
47
|
|
|
46
48
|
## Install
|
|
47
49
|
|
|
50
|
+
For prerequisites, first remote deployment, ChatGPT connection, stdio configuration, browser pairing, upgrades, troubleshooting, and removal, follow [Installation and first use](docs/GETTING_STARTED.md). The commands below are the compact installation reference.
|
|
51
|
+
|
|
48
52
|
Node.js 26 or newer and npm 12 or newer are required. The repository pins the active development versions in `.node-version`, `.nvmrc`, and `packageManager`; project installs fail on older Node runtimes.
|
|
49
53
|
|
|
50
54
|
Use a new temporary directory for the bootstrap. The existing `npm`/`npx` front-end may inspect nearby project metadata before it launches the requested npm version; starting from an empty directory prevents an unrelated legacy `devEngines` declaration from blocking the bootstrap. The actual installation is then executed by the pinned npm 12 CLI rather than whichever npm happens to be first on `PATH`.
|
|
@@ -126,6 +130,12 @@ MCP connection password: mcp_password_...
|
|
|
126
130
|
|
|
127
131
|
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. After password approval, the Worker constructs the registered callback with the URL API and returns `303 See Other`, so OAuth response parameters are encoded and the browser performs a GET to the callback.
|
|
128
132
|
|
|
133
|
+
### Multiple clients and accounts
|
|
134
|
+
|
|
135
|
+
One Worker can hold several OAuth client registrations and access tokens, so the same trusted owner or team can connect more than one MCP application or ChatGPT account. This is **multi-client access, not isolated multi-account tenancy**: the current release uses one workspace connection password, one workspace policy, and one active daemon authority. It has no principal records, per-account roles, targeted account revocation, or account-specific audit boundary.
|
|
136
|
+
|
|
137
|
+
Do not share a `full` bridge with mutually untrusted users. Use a separate bridge and preferably a separate low-privilege OS account, container, or VM for each trust domain. See [Multi-client, multi-account, and tenancy architecture](docs/MULTI_ACCOUNT.md) for the current support matrix and the proposed principal/grant design.
|
|
138
|
+
|
|
129
139
|
## Optional local stdio MCP
|
|
130
140
|
|
|
131
141
|
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.
|
|
@@ -480,7 +490,7 @@ npm pack --dry-run
|
|
|
480
490
|
|
|
481
491
|
`npm run check` covers privacy and release-impact gates, architecture/link invariants, generated Worker types, TypeScript, recursively discovered JavaScript syntax, catalog-to-runtime handler parity, deterministic relay lifecycle and secure-file tests, concurrent lock/atomic-replacement/PID-reuse fixtures, fail-closed service lifecycle tests, descendant process-tree termination, local path/write/state/log/service invariants, Ed25519/RSA generation and key-pair validation, real-machine `full` sandbox acceptance, a clean npm package-manifest/mode/sensitive-artifact check, managed-job integrity/redaction/finally/cancellation/recovery, 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 the pinned Node 26/npm 12 baseline; macOS and package-audit jobs also exercise the documented isolated global installation.
|
|
482
492
|
|
|
483
|
-
The cross-cutting 0.12.0 review, corrected failure modes, and residual OS-level limits are recorded in [docs/AUDIT.md](docs/AUDIT.md). See also [docs/AGENT_CONTEXT.md](docs/AGENT_CONTEXT.md), [docs/LOCAL_AUTOMATION.md](docs/LOCAL_AUTOMATION.md), [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/ENGINEERING.md](docs/ENGINEERING.md), [docs/PROJECT_STANDARDS.md](docs/PROJECT_STANDARDS.md), the generated [MCP tool reference](docs/TOOL_REFERENCE.md), and [SECURITY.md](SECURITY.md).
|
|
493
|
+
The cross-cutting 0.12.0 review, corrected failure modes, and residual OS-level limits are recorded in [docs/AUDIT.md](docs/AUDIT.md). See also [docs/GETTING_STARTED.md](docs/GETTING_STARTED.md), [docs/MULTI_ACCOUNT.md](docs/MULTI_ACCOUNT.md), [docs/AGENT_CONTEXT.md](docs/AGENT_CONTEXT.md), [docs/LOCAL_AUTOMATION.md](docs/LOCAL_AUTOMATION.md), [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/ENGINEERING.md](docs/ENGINEERING.md), [docs/PROJECT_STANDARDS.md](docs/PROJECT_STANDARDS.md), the generated [MCP tool reference](docs/TOOL_REFERENCE.md), and [SECURITY.md](SECURITY.md).
|
|
484
494
|
|
|
485
495
|
## Uninstall
|
|
486
496
|
|
package/SECURITY.md
CHANGED
|
@@ -36,6 +36,14 @@ An authorized client can invoke every tool exposed by the selected profile and r
|
|
|
36
36
|
|
|
37
37
|
The Worker is a remote authentication and relay boundary. The local runtime is the filesystem and process boundary. Stdio bypasses the Worker and relies on local process/configuration trust.
|
|
38
38
|
|
|
39
|
+
### Multiple clients are not isolated accounts
|
|
40
|
+
|
|
41
|
+
The current remote design accepts multiple OAuth client registrations and can issue multiple access tokens, but it has one per-workspace connection password and one workspace policy. An OAuth `client_id` identifies client software and redirect URIs; it is not a human or service principal. Different ChatGPT accounts or MCP applications that authorize with the same password therefore enter the same trust domain and receive the same Machine Bridge authority.
|
|
42
|
+
|
|
43
|
+
The current release has no per-account role, suspension, membership expiry, targeted principal revocation, or account-specific audit boundary. Secret rotation invalidates all outstanding access tokens for the workspace. Do not present this behavior as multi-user tenancy.
|
|
44
|
+
|
|
45
|
+
Mutually untrusted users must use separate bridge instances and external isolation appropriate to the authority exposed: a dedicated low-privilege OS account, container, or VM, a narrow workspace, an independent state root and Worker credential set, and the narrowest useful profile. Worker-side account records alone cannot isolate direct processes, shells, browser sessions, Accessibility actions, credential stores, or network authority inherited from the local OS user. See [Multi-client, multi-account, and tenancy architecture](docs/MULTI_ACCOUNT.md).
|
|
46
|
+
|
|
39
47
|
## Profiles are capability sets, not sandboxes
|
|
40
48
|
|
|
41
49
|
The default for newly selected workspaces is `full`, which prioritizes ease of use over least privilege. Named profiles are canonical capability contracts. A stored `full` label is repaired on load to the complete maximum-permission field set and tool catalog; a deliberate per-capability override is represented as `custom`, not as a partially restricted `full`.
|
|
@@ -152,7 +160,7 @@ Runner diagnostic logs are owner-only and do not receive child stdout/stderr. St
|
|
|
152
160
|
|
|
153
161
|
## OAuth and public endpoints
|
|
154
162
|
|
|
155
|
-
Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, token-version revocation, and bounded dynamic client registration. Successful consent constructs the registered callback through the URL API and returns `303 See Other`; response parameters are encoded rather than concatenated into an unchecked header string.
|
|
163
|
+
Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, token-version revocation, and bounded dynamic client registration. Client registrations and tokens may coexist, but all authorizations currently share one workspace credential and authority; there is no principal-aware account model. Successful consent constructs the registered callback through the URL API and returns `303 See Other`; response parameters are encoded rather than concatenated into an unchecked header string.
|
|
156
164
|
|
|
157
165
|
The authorization page displays the validated client name and redirect URI. Enter the connection password only after initiating the connection and recognizing both values.
|
|
158
166
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Machine Bridge Browser",
|
|
4
|
-
"version": "0.17.
|
|
4
|
+
"version": "0.17.1",
|
|
5
5
|
"description": "Connects the current Chromium browser profile to the local Machine Bridge runtime for user-authorized automation.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"tabs",
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"action": {
|
|
31
31
|
"default_title": "Machine Bridge Browser"
|
|
32
32
|
},
|
|
33
|
-
"version_name": "0.17.
|
|
33
|
+
"version_name": "0.17.1"
|
|
34
34
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -109,6 +109,8 @@ All requests for a deployed Worker route to one named Durable Object. It owns:
|
|
|
109
109
|
|
|
110
110
|
The Worker verifies OAuth, validates MCP envelopes and optional protocol headers, converts `tools/call` into WebSocket messages, correlates cancellation by access-token hash and JSON-RPC ID, and formats text/structured/image results. It has no local filesystem or process API.
|
|
111
111
|
|
|
112
|
+
The current OAuth store is multi-client but not principal-aware. `client_id` identifies an MCP application/installation; it does not identify a human or service account. Every successful authorization uses the same per-workspace connection password and receives the same workspace policy ceiling. Isolated account support therefore requires explicit principals, memberships, named grants, targeted revocation, and dual Worker/local enforcement rather than treating client registrations as users. The recommended design retains one bridge-specific Durable Object and one local runtime per workspace/trust domain; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
|
|
113
|
+
|
|
112
114
|
The daemon attachment deliberately omits workspace path/name/hash and process ID. Explicit authenticated tools may return workspace metadata according to local path-display policy.
|
|
113
115
|
|
|
114
116
|
### Autostart layer
|
|
@@ -138,7 +140,7 @@ flowchart LR
|
|
|
138
140
|
CLI --> S[Autostart provider]
|
|
139
141
|
```
|
|
140
142
|
|
|
141
|
-
Remote OAuth determines which client may call tools. Local stdio access relies on the local process and configuration boundary. Policy determines which tools the local daemon and relay advertise. A connector host can independently present a smaller tool subset to a session; this post-relay filtering is outside the protocol state visible to Machine Bridge. Canonical resolution limits direct filesystem tools. Processes retain local-user authority and can escape workspace constraints through their own code or system calls.
|
|
143
|
+
Remote OAuth currently determines which registered client token may call tools; it does not distinguish independently authorized human principals. Local stdio access relies on the local process and configuration boundary. Policy determines which tools the local daemon and relay advertise. A connector host can independently present a smaller tool subset to a session; this post-relay filtering is outside the protocol state visible to Machine Bridge. Canonical resolution limits direct filesystem tools. Processes retain local-user authority and can escape workspace constraints through their own code or system calls.
|
|
142
144
|
|
|
143
145
|
## Remote request lifecycle
|
|
144
146
|
|
|
@@ -258,4 +260,4 @@ Third-party workflow actions are pinned to immutable commit SHAs. Dependabot pro
|
|
|
258
260
|
- model-level prompt-injection prevention or semantic validation of browser/application actions;
|
|
259
261
|
- universal desktop UI automation beyond the implemented OS Accessibility backend;
|
|
260
262
|
- scripting browser-internal/enterprise-blocked pages or inaccessible cross-origin frames;
|
|
261
|
-
- multi-user tenancy in one Worker deployment.
|
|
263
|
+
- isolated multi-user tenancy or per-principal authorization in one Worker deployment; multiple OAuth client registrations currently share one workspace authority.
|
package/docs/CLIENTS.md
CHANGED
|
@@ -124,6 +124,8 @@ machine-mcp --workspace /path/to/project
|
|
|
124
124
|
|
|
125
125
|
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.
|
|
126
126
|
|
|
127
|
+
Several OAuth clients and access tokens can coexist, but the current release does not model separate human/service principals. Every authorization that uses the shared workspace password receives the same workspace authority. Do not treat separate ChatGPT accounts as isolated Machine Bridge accounts; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
|
|
128
|
+
|
|
127
129
|
## Profile guidance
|
|
128
130
|
|
|
129
131
|
- `full` is the default and prioritizes immediate usability. It is a canonical contract exposing every catalog tool, shell execution, unrestricted direct filesystem paths, absolute path output, and the full parent environment. Any individual narrowing is represented as `custom`.
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# Installation and first-use guide
|
|
2
|
+
|
|
3
|
+
This guide covers a clean installation, the first remote ChatGPT connection, local stdio clients, browser setup, routine operation, upgrades, troubleshooting, and removal.
|
|
4
|
+
|
|
5
|
+
## 1. Decide which connection mode you need
|
|
6
|
+
|
|
7
|
+
Machine Bridge has two transports. Choose one before installing:
|
|
8
|
+
|
|
9
|
+
| Requirement | Use | Cloudflare required | Process that must remain available |
|
|
10
|
+
|---|---|---:|---|
|
|
11
|
+
| ChatGPT web, a hosted agent, or another device must reach this computer | Remote HTTPS/OAuth | Yes | Machine Bridge daemon or installed login service |
|
|
12
|
+
| Claude Desktop, Cursor, Codex, ChatGPT Desktop, or another local host runs on this computer | Local stdio | No | The MCP host launches Machine Bridge as a subprocess |
|
|
13
|
+
| The local coding client already has equivalent file, Git, patch, and terminal tools | Neither | No | None |
|
|
14
|
+
|
|
15
|
+
Remote and stdio modes use the same local runtime and policy model. The remote Worker authenticates and relays calls; it does not read files or execute processes by itself.
|
|
16
|
+
|
|
17
|
+
## 2. Understand the authority you are granting
|
|
18
|
+
|
|
19
|
+
A new workspace uses the `full` profile unless another profile is selected explicitly. `full` exposes the complete tool catalog, shell execution, paths outside the selected workspace, absolute paths, and the complete parent process environment. It is intended for a trusted owner using a trusted MCP host.
|
|
20
|
+
|
|
21
|
+
For a first connection to an unfamiliar host, start with a narrower profile:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
machine-mcp --workspace /path/to/project --profile review
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Profiles:
|
|
28
|
+
|
|
29
|
+
| Profile | Reads | File changes | Direct processes | Shell | Direct filesystem scope |
|
|
30
|
+
|---|---:|---:|---:|---:|---|
|
|
31
|
+
| `full` | Yes | Yes | Yes | Yes | Unrestricted |
|
|
32
|
+
| `agent` | Yes | Yes | Yes | No | Selected workspace |
|
|
33
|
+
| `edit` | Yes | Yes | No | No | Selected workspace |
|
|
34
|
+
| `review` | Yes | No | No | No | Selected workspace |
|
|
35
|
+
|
|
36
|
+
A profile is a Machine Bridge policy ceiling. The MCP host, operating system, endpoint-security software, container, and cloud platform may impose additional restrictions.
|
|
37
|
+
|
|
38
|
+
## 3. Prerequisites
|
|
39
|
+
|
|
40
|
+
Required for every installation:
|
|
41
|
+
|
|
42
|
+
- Node.js 26 or newer;
|
|
43
|
+
- npm 12 or newer;
|
|
44
|
+
- macOS, Linux, or Windows;
|
|
45
|
+
- a local user account permitted to read or modify the selected workspace.
|
|
46
|
+
|
|
47
|
+
Remote mode additionally requires:
|
|
48
|
+
|
|
49
|
+
- a Cloudflare account permitted to deploy Workers and Durable Objects;
|
|
50
|
+
- a browser available for Wrangler sign-in;
|
|
51
|
+
- outbound HTTPS and WebSocket access from the computer running the daemon.
|
|
52
|
+
|
|
53
|
+
Browser automation additionally requires:
|
|
54
|
+
|
|
55
|
+
- a Chromium-based browser that can load an unpacked Manifest V3 extension;
|
|
56
|
+
- the extension loaded into the browser profile whose tabs and login state should be controlled.
|
|
57
|
+
|
|
58
|
+
Check the installed runtimes:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
node --version
|
|
62
|
+
npm --version
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Do not continue with an older runtime. This repository intentionally rejects unsupported Node/npm versions.
|
|
66
|
+
|
|
67
|
+
## 4. Install the released package
|
|
68
|
+
|
|
69
|
+
Use a temporary package-free directory. An older npm front-end can inspect metadata in the current directory before it launches npm 12; the isolated bootstrap prevents an unrelated project configuration from breaking installation.
|
|
70
|
+
|
|
71
|
+
### macOS and Linux
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
install_dir="$(mktemp -d)"
|
|
75
|
+
(
|
|
76
|
+
cd "$install_dir"
|
|
77
|
+
npx --yes npm@12.0.1 install --global npm@12.0.1
|
|
78
|
+
npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
|
|
79
|
+
)
|
|
80
|
+
rm -rf "$install_dir"
|
|
81
|
+
|
|
82
|
+
npm --version
|
|
83
|
+
machine-mcp doctor
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Windows Command Prompt
|
|
87
|
+
|
|
88
|
+
```bat
|
|
89
|
+
set "MBM_INSTALL_DIR=%TEMP%\machine-bridge-mcp-install-%RANDOM%-%RANDOM%"
|
|
90
|
+
mkdir "%MBM_INSTALL_DIR%"
|
|
91
|
+
pushd "%MBM_INSTALL_DIR%"
|
|
92
|
+
npx --yes npm@12.0.1 install --global npm@12.0.1
|
|
93
|
+
npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
|
|
94
|
+
popd
|
|
95
|
+
rmdir /s /q "%MBM_INSTALL_DIR%"
|
|
96
|
+
|
|
97
|
+
npm --version
|
|
98
|
+
machine-mcp doctor
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Reopen the terminal if `npm --version` still resolves to an older global installation.
|
|
102
|
+
|
|
103
|
+
### Run from a source checkout
|
|
104
|
+
|
|
105
|
+
macOS/Linux:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
git clone https://github.com/YuLeiFuYun/machine-bridge-mcp.git
|
|
109
|
+
cd machine-bridge-mcp
|
|
110
|
+
npm install
|
|
111
|
+
./mbm
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Windows Command Prompt:
|
|
115
|
+
|
|
116
|
+
```bat
|
|
117
|
+
git clone https://github.com/YuLeiFuYun/machine-bridge-mcp.git
|
|
118
|
+
cd machine-bridge-mcp
|
|
119
|
+
npm install
|
|
120
|
+
.\mbm.cmd
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Source installs are for development or review. Released installations should normally use the global package command above.
|
|
124
|
+
|
|
125
|
+
## 5. Interpret the initial doctor result
|
|
126
|
+
|
|
127
|
+
Run:
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
machine-mcp doctor
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The command checks the local runtime, package layout, Wrangler availability, Cloudflare login state, and known Worker health. Before the first remote deployment, a missing Cloudflare login or Worker is expected; local runtime failures are not.
|
|
134
|
+
|
|
135
|
+
Common installation failures:
|
|
136
|
+
|
|
137
|
+
| Message or symptom | Meaning | Corrective action |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `Unknown cli config "--allow-scripts"` | npm 11 or older executed the install | Repeat the isolated npm 12 bootstrap |
|
|
140
|
+
| `Invalid property "node"` or `Invalid property "devEngines.node"` | An old npm parser inspected incompatible project metadata | Run the install from a new empty temporary directory |
|
|
141
|
+
| `machine-mcp: command not found` | The global npm binary directory is not on `PATH` | Reopen the terminal, inspect `npm prefix -g`, and add its binary directory to `PATH` |
|
|
142
|
+
| Native package install warning | npm did not approve reviewed native build scripts | Use the documented `--allow-scripts` and `--omit=optional` command exactly |
|
|
143
|
+
|
|
144
|
+
## 6. First remote start
|
|
145
|
+
|
|
146
|
+
Choose the exact workspace to expose. Prefer an explicit path rather than relying on the current directory:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
machine-mcp --workspace /path/to/project
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For a narrower first run:
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
machine-mcp --workspace /path/to/project --profile review
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The first start performs these operations:
|
|
159
|
+
|
|
160
|
+
1. canonicalizes and remembers the workspace;
|
|
161
|
+
2. creates owner-only per-workspace state and credentials;
|
|
162
|
+
3. opens the Wrangler/Cloudflare sign-in flow when required;
|
|
163
|
+
4. deploys a Worker and Durable Object for that workspace;
|
|
164
|
+
5. installs a platform-native login service unless `--no-autostart` is supplied;
|
|
165
|
+
6. starts an outbound authenticated WebSocket from the local daemon to the Worker;
|
|
166
|
+
7. prints the remote `/mcp` URL and a connection password.
|
|
167
|
+
|
|
168
|
+
The foreground command remains attached to the terminal. Keep it running while testing. The remote Worker cannot execute local tools when no authenticated daemon is connected.
|
|
169
|
+
|
|
170
|
+
To run only in the background after setup:
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
machine-mcp service start
|
|
174
|
+
machine-mcp service status
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
To avoid installing autostart during a temporary test:
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
machine-mcp --workspace /path/to/project --no-autostart
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## 7. Connect ChatGPT
|
|
184
|
+
|
|
185
|
+
Machine Bridge prints values in this form:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
MCP Server URL: https://<worker>.<account>.workers.dev/mcp
|
|
189
|
+
MCP connection password: mcp_password_...
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Treat the connection password as a credential. Do not place it in a repository, issue, screenshot, chat message, shell history note, or shared document.
|
|
193
|
+
|
|
194
|
+
Current ChatGPT developer-mode flow, as documented in OpenAI's [Connect from ChatGPT](https://developers.openai.com/apps-sdk/deploy/connect-chatgpt) guide:
|
|
195
|
+
|
|
196
|
+
1. Open ChatGPT settings.
|
|
197
|
+
2. Under **Security and login**, enable **Developer mode** if the account or workspace permits it.
|
|
198
|
+
3. Open **Plugins** and create a developer-mode app.
|
|
199
|
+
4. Enter a descriptive name and the exact printed `/mcp` URL.
|
|
200
|
+
5. Start the connection.
|
|
201
|
+
6. On the Machine Bridge authorization page, verify the displayed client name, redirect URI, and resource.
|
|
202
|
+
7. Enter the printed connection password only after those values are recognized.
|
|
203
|
+
8. Create a new chat and enable the app for that conversation.
|
|
204
|
+
|
|
205
|
+
ChatGPT navigation labels can change. The invariant is that the client must connect to the public `/mcp` endpoint and complete the OAuth authorization page served by the Worker.
|
|
206
|
+
|
|
207
|
+
## 8. Verify the first connection
|
|
208
|
+
|
|
209
|
+
Ask the MCP host to call these tools in order:
|
|
210
|
+
|
|
211
|
+
1. `server_info` — proves the request reached the Worker and reports whether a daemon is authenticated;
|
|
212
|
+
2. `project_overview` — proves the relay reached the selected local runtime;
|
|
213
|
+
3. `diagnose_runtime` — runs fixed policy, filesystem, process, shell, managed-job, and resource probes permitted by the active profile.
|
|
214
|
+
|
|
215
|
+
For an explicit local acceptance test of the canonical `full` profile:
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
machine-mcp full-test --workspace /path/to/project
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
A successful local test does not prove that a hosted MCP client exposes every advertised tool. Hosts can independently hide tools or require approvals.
|
|
222
|
+
|
|
223
|
+
## 9. Configure a local stdio client
|
|
224
|
+
|
|
225
|
+
Generate configurations rather than manually guessing the Node path or package entrypoint:
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
machine-mcp client-config --client all --workspace /path/to/project
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Use a narrower policy when appropriate:
|
|
232
|
+
|
|
233
|
+
```sh
|
|
234
|
+
machine-mcp client-config --client all --workspace /path/to/project --profile agent
|
|
235
|
+
machine-mcp client-config --client all --workspace /path/to/project --profile edit
|
|
236
|
+
machine-mcp client-config --client all --workspace /path/to/project --profile review
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The generated command uses absolute executable paths so GUI applications do not depend on a shell-specific `PATH`.
|
|
240
|
+
|
|
241
|
+
A generic stdio invocation is:
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
machine-mcp stdio --workspace /path/to/project --profile full
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The host owns the model, conversation, tool-selection loop, and approvals. Machine Bridge supplies tools; it is not a model provider.
|
|
248
|
+
|
|
249
|
+
## 10. Enable existing-browser automation
|
|
250
|
+
|
|
251
|
+
Run once after installation:
|
|
252
|
+
|
|
253
|
+
```sh
|
|
254
|
+
machine-mcp browser setup
|
|
255
|
+
machine-mcp browser status
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Then:
|
|
259
|
+
|
|
260
|
+
1. Open the extensions page in the Chromium profile you actually use.
|
|
261
|
+
2. Enable Developer mode.
|
|
262
|
+
3. Load the unpacked extension directory printed by `browser setup`.
|
|
263
|
+
4. Complete the local pairing page.
|
|
264
|
+
5. Confirm that `machine-mcp browser status` reports the expected packaged version, protocol, and an authenticated connection.
|
|
265
|
+
|
|
266
|
+
Machine Bridge does not launch a separate browser profile. The extension controls the profile into which it was loaded, including its open tabs and logged-in sessions. Reload the unpacked extension after every Machine Bridge upgrade.
|
|
267
|
+
|
|
268
|
+
## 11. Routine commands
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
machine-mcp status
|
|
272
|
+
machine-mcp doctor
|
|
273
|
+
machine-mcp workspace show
|
|
274
|
+
machine-mcp service status
|
|
275
|
+
machine-mcp browser status
|
|
276
|
+
machine-mcp --print-mcp-credentials
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Useful operational actions:
|
|
280
|
+
|
|
281
|
+
```sh
|
|
282
|
+
machine-mcp service stop
|
|
283
|
+
machine-mcp service start
|
|
284
|
+
machine-mcp rotate-secrets
|
|
285
|
+
machine-mcp resource list
|
|
286
|
+
machine-mcp job list
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`rotate-secrets` invalidates previously issued OAuth access tokens and requires clients to reconnect. It is a whole-workspace revocation mechanism in the current release.
|
|
290
|
+
|
|
291
|
+
## 12. Work with more than one workspace
|
|
292
|
+
|
|
293
|
+
Each canonical workspace receives independent state, policy, credentials, Worker name, resource registry, job directory, startup lock, and daemon lock. Select workspaces explicitly:
|
|
294
|
+
|
|
295
|
+
```sh
|
|
296
|
+
machine-mcp --workspace /path/to/project-a
|
|
297
|
+
machine-mcp --workspace /path/to/project-b
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Do not point two logical trust domains at one broad parent directory merely to reduce configuration. A workspace is an authorization boundary for confined profiles and an operational identity even when `full` permits paths outside it.
|
|
301
|
+
|
|
302
|
+
## 13. Upgrade
|
|
303
|
+
|
|
304
|
+
Repeat the isolated global installation, then start Machine Bridge in the target workspace:
|
|
305
|
+
|
|
306
|
+
```sh
|
|
307
|
+
install_dir="$(mktemp -d)"
|
|
308
|
+
(
|
|
309
|
+
cd "$install_dir"
|
|
310
|
+
npx --yes npm@12.0.1 install --global npm@12.0.1
|
|
311
|
+
npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
|
|
312
|
+
)
|
|
313
|
+
rm -rf "$install_dir"
|
|
314
|
+
|
|
315
|
+
machine-mcp --workspace /path/to/project --verbose
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
The foreground start performs bounded takeover of an old verified daemon when safe. If it refuses:
|
|
319
|
+
|
|
320
|
+
```sh
|
|
321
|
+
machine-mcp service status
|
|
322
|
+
machine-mcp service stop
|
|
323
|
+
machine-mcp --workspace /path/to/project --verbose
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
After upgrading, reload the unpacked browser extension before relying on browser tools.
|
|
327
|
+
|
|
328
|
+
## 14. Troubleshooting by layer
|
|
329
|
+
|
|
330
|
+
Use the first failing layer rather than changing unrelated settings.
|
|
331
|
+
|
|
332
|
+
### Package or runtime layer
|
|
333
|
+
|
|
334
|
+
```sh
|
|
335
|
+
node --version
|
|
336
|
+
npm --version
|
|
337
|
+
which machine-mcp # macOS/Linux
|
|
338
|
+
where machine-mcp # Windows
|
|
339
|
+
machine-mcp doctor
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Cloudflare/deployment layer
|
|
343
|
+
|
|
344
|
+
```sh
|
|
345
|
+
machine-mcp doctor
|
|
346
|
+
machine-mcp status
|
|
347
|
+
machine-mcp --workspace /path/to/project --force-worker --verbose
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
A forced deployment changes cloud state. Use it only when the normal deployment hash/health reconciliation is insufficient.
|
|
351
|
+
|
|
352
|
+
### Local service/daemon layer
|
|
353
|
+
|
|
354
|
+
```sh
|
|
355
|
+
machine-mcp service status
|
|
356
|
+
machine-mcp service stop
|
|
357
|
+
machine-mcp service start
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
A connected Worker with no authenticated daemon can authorize OAuth successfully but cannot execute local tools.
|
|
361
|
+
|
|
362
|
+
### MCP-host layer
|
|
363
|
+
|
|
364
|
+
If `server_info` or `project_overview` never returns a structured Machine Bridge result, the host may not have delivered the call. Check the host's plugin/app enablement, tool permissions, approval UI, and connection status.
|
|
365
|
+
|
|
366
|
+
### Operating-system layer
|
|
367
|
+
|
|
368
|
+
If `diagnose_runtime` reaches the daemon but a probe fails, inspect filesystem permissions, macOS TCC/SIP, Windows ACLs, endpoint-security policy, shell availability, or container restrictions. Changing Machine Bridge to `full` cannot override the operating system.
|
|
369
|
+
|
|
370
|
+
### Browser layer
|
|
371
|
+
|
|
372
|
+
```sh
|
|
373
|
+
machine-mcp browser status
|
|
374
|
+
machine-mcp browser setup
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Verify that the extension is loaded in the intended profile, its version matches the installed package, and its badge reports authenticated readiness.
|
|
378
|
+
|
|
379
|
+
For detailed recovery procedures, see [OPERATIONS.md](OPERATIONS.md).
|
|
380
|
+
|
|
381
|
+
## 15. Remove Machine Bridge
|
|
382
|
+
|
|
383
|
+
Remove the selected deployment, service, and local state:
|
|
384
|
+
|
|
385
|
+
```sh
|
|
386
|
+
machine-mcp uninstall
|
|
387
|
+
npm uninstall -g machine-bridge-mcp
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Keep the deployed Worker while removing local state and autostart:
|
|
391
|
+
|
|
392
|
+
```sh
|
|
393
|
+
machine-mcp uninstall --keep-worker
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Uninstall is fail-closed. If a verified daemon, active managed job, unreadable lock, or service removal cannot be resolved safely, Machine Bridge retains state for diagnosis instead of deleting only part of the installation.
|
|
397
|
+
|
|
398
|
+
## 16. Before sharing access
|
|
399
|
+
|
|
400
|
+
The current release supports multiple OAuth client registrations but not isolated multi-user tenancy. Different ChatGPT accounts can technically authorize against the same Worker if they know the shared connection password, but they receive the same workspace authority and cannot be managed as independent principals. Read [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md) before sharing a deployment.
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
# Multi-client, multi-account, and tenancy architecture
|
|
2
|
+
|
|
3
|
+
## The actual question
|
|
4
|
+
|
|
5
|
+
The important question is not whether several OAuth clients can obtain tokens. The important question is:
|
|
6
|
+
|
|
7
|
+
> Can independently managed principals share one Machine Bridge deployment without inheriting the same local-machine authority or being able to affect one another?
|
|
8
|
+
|
|
9
|
+
The current release answers **no**. It supports multiple client registrations and multiple access tokens, but it does not model human or service accounts as independent security principals. Anyone who completes authorization with the shared workspace connection password receives the same workspace-level Machine Bridge authority.
|
|
10
|
+
|
|
11
|
+
This distinction must remain explicit because an OAuth `client_id` identifies client software, not the person or service using it.
|
|
12
|
+
|
|
13
|
+
## Terms
|
|
14
|
+
|
|
15
|
+
| Term | Meaning in this document |
|
|
16
|
+
|---|---|
|
|
17
|
+
| OAuth client | A registered MCP host/application with redirect URIs and a `client_id` |
|
|
18
|
+
| Principal/account | A human or service identity that may receive authority |
|
|
19
|
+
| Grant | A named, versioned set of capabilities assigned to a principal for one bridge |
|
|
20
|
+
| Bridge | One canonical workspace, local runtime, Worker, Durable Object, policy ceiling, and credential set |
|
|
21
|
+
| Trust domain | Principals that may safely share the same local OS/runtime authority |
|
|
22
|
+
| Tenant | An independently administered trust domain requiring isolation from other tenants |
|
|
23
|
+
|
|
24
|
+
## Current behavior
|
|
25
|
+
|
|
26
|
+
| Scenario | Current support | Security meaning |
|
|
27
|
+
|---|---:|---|
|
|
28
|
+
| Several MCP applications or installations connect to one Worker | Yes | Each can register a client and receive separate tokens |
|
|
29
|
+
| Several ChatGPT accounts connect using the same connection password | Technically yes | They receive the same workspace authority; no account isolation |
|
|
30
|
+
| Several active OAuth tokens for one client | Yes | Tokens are distinct but share the same authorization semantics |
|
|
31
|
+
| Per-account roles, capability grants, suspension, or revocation | No | Only workspace-wide policy and token-version revocation exist |
|
|
32
|
+
| Revoke one principal while preserving all others | No | The current emergency mechanism rotates the workspace token version and invalidates all access tokens |
|
|
33
|
+
| Several independent workspaces | Yes | Each canonical workspace has independent state, policy, Worker name, secrets, resources, jobs, and locks |
|
|
34
|
+
| Several mutually untrusted users in one local runtime | No | Process and browser authority remain local-user authority |
|
|
35
|
+
| Multi-user tenancy in one Worker deployment | No | The architecture currently has one shared password, one policy, one active daemon, and one default Durable Object route |
|
|
36
|
+
|
|
37
|
+
Relevant current implementation:
|
|
38
|
+
|
|
39
|
+
- `src/worker/oauth-state.ts` stores OAuth clients, authorization codes, and tokens by `client_id` but has no principal or membership record.
|
|
40
|
+
- `src/worker/index.ts` accepts one `MCP_OAUTH_PASSWORD`, validates one `OAUTH_TOKEN_VERSION`, routes the deployment to the Durable Object named `default`, and selects one authenticated daemon.
|
|
41
|
+
- `src/local/state.mjs` creates independent credentials and state per canonical workspace.
|
|
42
|
+
- [ARCHITECTURE.md](ARCHITECTURE.md) explicitly excludes multi-user tenancy in one Worker deployment.
|
|
43
|
+
|
|
44
|
+
## Safe deployment choices today
|
|
45
|
+
|
|
46
|
+
### Same owner and same trust domain
|
|
47
|
+
|
|
48
|
+
Use one bridge and authorize multiple MCP clients. This is appropriate when every client/account is controlled by the same person or team and all may receive the same workspace authority.
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
|
|
52
|
+
- the same owner connects ChatGPT web and a local Codex client;
|
|
53
|
+
- one trusted team uses several MCP host installations with an identical role;
|
|
54
|
+
- a user reconnects after replacing a device.
|
|
55
|
+
|
|
56
|
+
The separate tokens improve protocol hygiene and cancellation correlation, but they are not separate authorization domains.
|
|
57
|
+
|
|
58
|
+
### Different workspaces with the same owner
|
|
59
|
+
|
|
60
|
+
Run one bridge identity per canonical workspace. This preserves independent state, credentials, Workers, policies, resources, and jobs. It is preferable to exposing one broad parent directory solely for convenience.
|
|
61
|
+
|
|
62
|
+
### Different trust domains or mutually untrusted users
|
|
63
|
+
|
|
64
|
+
Do not share one `full` runtime. Use a separate bridge instance per trust domain, preferably with:
|
|
65
|
+
|
|
66
|
+
1. a dedicated low-privilege OS account, container, or VM;
|
|
67
|
+
2. a narrow workspace mounted or accessible only to that identity;
|
|
68
|
+
3. an independent state root and Worker credential set;
|
|
69
|
+
4. a separate Worker/deployment identity where administrative separation matters;
|
|
70
|
+
5. a profile no broader than the required workflow.
|
|
71
|
+
|
|
72
|
+
This external boundary is necessary because direct processes, package scripts, shells, browser sessions, Accessibility actions, credential stores, and network access inherit the local user's authority. A per-account row in Worker storage cannot turn Node.js process execution into an OS sandbox.
|
|
73
|
+
|
|
74
|
+
## Recommended future architecture
|
|
75
|
+
|
|
76
|
+
The least coupled design is **principal-aware authorization inside one bridge, while retaining one bridge per workspace/trust domain**. Do not begin by turning the Worker into a global router for every workspace, machine, and tenant.
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
MCP host / OAuth client
|
|
80
|
+
|
|
|
81
|
+
| authorization code + PKCE
|
|
82
|
+
v
|
|
83
|
+
Principal authentication
|
|
84
|
+
|
|
|
85
|
+
v
|
|
86
|
+
Bridge membership ------> named grant + revision
|
|
87
|
+
|
|
|
88
|
+
v
|
|
89
|
+
OAuth code/token bound to principal + grant + resource
|
|
90
|
+
|
|
|
91
|
+
v
|
|
92
|
+
Worker authorization gate
|
|
93
|
+
|
|
|
94
|
+
| bounded auth context + tool call
|
|
95
|
+
v
|
|
96
|
+
Local daemon authorization gate
|
|
97
|
+
|
|
|
98
|
+
v
|
|
99
|
+
Workspace / process / browser operation
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The design has five distinct concepts:
|
|
103
|
+
|
|
104
|
+
1. **Client registration** identifies the MCP application and its redirect URIs.
|
|
105
|
+
2. **Principal authentication** establishes the human or service identity.
|
|
106
|
+
3. **Membership** says that a principal may access one bridge.
|
|
107
|
+
4. **Grant** defines the exact capabilities and constraints available to that membership.
|
|
108
|
+
5. **OAuth tokens** carry a revocable reference to the principal, membership, grant revision, and protected resource.
|
|
109
|
+
|
|
110
|
+
Keeping these concepts separate prevents `client_id`, email address, connection password, and policy profile from becoming overloaded pseudo-identities.
|
|
111
|
+
|
|
112
|
+
## Data model
|
|
113
|
+
|
|
114
|
+
A minimal Worker-side model can remain inside the existing per-bridge Durable Object:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
principals
|
|
118
|
+
principal_id
|
|
119
|
+
display_label
|
|
120
|
+
status
|
|
121
|
+
authentication_method
|
|
122
|
+
credential_reference_or_subject
|
|
123
|
+
revocation_version
|
|
124
|
+
created_at
|
|
125
|
+
last_authenticated_at
|
|
126
|
+
|
|
127
|
+
memberships
|
|
128
|
+
membership_id
|
|
129
|
+
principal_id
|
|
130
|
+
bridge_id
|
|
131
|
+
grant_id
|
|
132
|
+
grant_revision
|
|
133
|
+
status
|
|
134
|
+
expires_at
|
|
135
|
+
created_at
|
|
136
|
+
|
|
137
|
+
clients
|
|
138
|
+
client_id
|
|
139
|
+
client_name
|
|
140
|
+
redirect_uris
|
|
141
|
+
registration_identity
|
|
142
|
+
created_at
|
|
143
|
+
last_used_at
|
|
144
|
+
|
|
145
|
+
authorization_codes
|
|
146
|
+
code_hash
|
|
147
|
+
client_id
|
|
148
|
+
principal_id
|
|
149
|
+
membership_id
|
|
150
|
+
grant_id
|
|
151
|
+
grant_revision
|
|
152
|
+
redirect_uri
|
|
153
|
+
code_challenge
|
|
154
|
+
scope
|
|
155
|
+
resource
|
|
156
|
+
expires_at
|
|
157
|
+
|
|
158
|
+
access_tokens
|
|
159
|
+
token_hash
|
|
160
|
+
client_id
|
|
161
|
+
principal_id
|
|
162
|
+
membership_id
|
|
163
|
+
grant_id
|
|
164
|
+
grant_revision
|
|
165
|
+
principal_revocation_version
|
|
166
|
+
scope
|
|
167
|
+
resource
|
|
168
|
+
expires_at
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use random opaque identifiers. Do not use an email address, OAuth source address, client name, or browser fingerprint as the primary authorization key.
|
|
172
|
+
|
|
173
|
+
The current `OAuthClient`, `OAuthCode`, and `OAuthToken` records can be migrated by adding optional principal/grant fields and treating legacy records as the single-owner principal until reauthorization. Avoid a flag day that silently changes existing users' authority.
|
|
174
|
+
|
|
175
|
+
## Grant model
|
|
176
|
+
|
|
177
|
+
A grant should reference capabilities rather than duplicate an arbitrary list of tool names. The current shared policy contract already provides the correct vocabulary and compound requirements.
|
|
178
|
+
|
|
179
|
+
Example conceptual grants:
|
|
180
|
+
|
|
181
|
+
| Grant | Capabilities | Typical use |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `reviewer` | read-only, workspace-confined | inspection and review |
|
|
184
|
+
| `editor` | read/write, no process execution | deterministic file changes |
|
|
185
|
+
| `agent` | workspace-confined writes and direct processes, isolated environment | trusted coding automation |
|
|
186
|
+
| `operator` | canonical `full` | single-owner or highly trusted administration |
|
|
187
|
+
|
|
188
|
+
The effective authorization must be the intersection of all applicable ceilings:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
local workspace policy ceiling
|
|
192
|
+
AND principal membership grant
|
|
193
|
+
AND OAuth scopes/resource binding
|
|
194
|
+
AND any MCP-host-side restrictions
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
A grant must never widen the locally selected workspace policy. For example, assigning `operator` cannot make a daemon started under `review` execute a process.
|
|
198
|
+
|
|
199
|
+
Do not create a second independently maintained permission matrix. Grant resolution should consume the same shared capability contract used by local policy, Worker tool advertisement, and runtime execution.
|
|
200
|
+
|
|
201
|
+
## Dual enforcement without two sources of truth
|
|
202
|
+
|
|
203
|
+
Worker-only filtering is insufficient because a protocol bug, stale token, alternate transport, or compromised relay must not bypass local authority. Local-only filtering is also insufficient because unauthorized tools should not be advertised or relayed.
|
|
204
|
+
|
|
205
|
+
Use the following contract:
|
|
206
|
+
|
|
207
|
+
1. The local daemon owns the machine policy ceiling and the canonical grant catalog it is willing to honor.
|
|
208
|
+
2. On authenticated `hello`, the daemon advertises a bounded grant-catalog revision/digest plus the policy ceiling and tool catalog.
|
|
209
|
+
3. The Worker owns principals, memberships, OAuth codes, and access tokens for that bridge.
|
|
210
|
+
4. A membership references only a grant ID/revision present in the daemon-advertised catalog.
|
|
211
|
+
5. The Worker filters `tools/list` and rejects `tools/call` using the membership grant.
|
|
212
|
+
6. Every relayed call includes a bounded authorization context containing opaque principal, membership, grant, and revision identifiers plus scopes.
|
|
213
|
+
7. The local daemon independently resolves the grant and rechecks the operation against the current local ceiling before execution.
|
|
214
|
+
8. A missing grant, stale revision, digest mismatch, suspended membership, or revoked principal fails closed.
|
|
215
|
+
|
|
216
|
+
This is not duplicated policy: the shared contract defines capability semantics; the Worker and daemon enforce the same resolved grant at separate trust boundaries.
|
|
217
|
+
|
|
218
|
+
## Authentication choices
|
|
219
|
+
|
|
220
|
+
Authentication should be an adapter behind the principal model, not embedded in tool authorization.
|
|
221
|
+
|
|
222
|
+
Reasonable progression:
|
|
223
|
+
|
|
224
|
+
1. **Compatibility mode:** retain the existing shared connection password and map it to one owner principal.
|
|
225
|
+
2. **Local managed accounts:** create separate high-entropy one-time invitations or passkey/WebAuthn credentials for principals.
|
|
226
|
+
3. **External identity provider:** accept OIDC identities from a configured issuer and map stable issuer/subject pairs to principals.
|
|
227
|
+
4. **Service principals:** use separately rotated non-human credentials with narrow grants and explicit expiry.
|
|
228
|
+
|
|
229
|
+
Do not issue one static password per person while leaving tokens unbound to a principal. That changes credential distribution but does not create account semantics.
|
|
230
|
+
|
|
231
|
+
For external identity, key principals by the pair `(issuer, subject)`, not by mutable email or display name. Keep identity-provider integration optional so a self-hosted single-owner installation remains simple.
|
|
232
|
+
|
|
233
|
+
## OAuth and MCP authorization rules
|
|
234
|
+
|
|
235
|
+
A principal-aware implementation should preserve the current authorization-code and PKCE flow and add these rules:
|
|
236
|
+
|
|
237
|
+
- the authorization code is bound to the authenticated principal, membership, client, redirect URI, PKCE challenge, scope, and resource;
|
|
238
|
+
- access tokens are audience/resource restricted to the exact bridge `/mcp` endpoint;
|
|
239
|
+
- tokens contain no raw credentials and are stored only as hashes;
|
|
240
|
+
- requested scopes are intersected with the membership grant rather than accepted as authority by themselves;
|
|
241
|
+
- dynamic client registration limits remain independent of principal limits;
|
|
242
|
+
- token and authorization-code quotas are enforced per principal and client as well as globally;
|
|
243
|
+
- cancellation/request correlation includes the token identity but never treats it as the principal database key;
|
|
244
|
+
- token validation checks principal status, membership status/expiry, principal revocation version, grant revision, token expiry, and resource binding.
|
|
245
|
+
|
|
246
|
+
The MCP authorization specification uses OAuth protected-resource metadata and resource indicators; OAuth security best practice requires exact redirect matching, PKCE, and audience-restricted tokens. Preserve those properties when adding identity rather than inventing a parallel session protocol.
|
|
247
|
+
|
|
248
|
+
## Revocation
|
|
249
|
+
|
|
250
|
+
The current `OAUTH_TOKEN_VERSION` is useful as an emergency whole-bridge kill switch but too coarse for ordinary account management.
|
|
251
|
+
|
|
252
|
+
Add four independent revocation levels:
|
|
253
|
+
|
|
254
|
+
1. revoke one access token/session;
|
|
255
|
+
2. revoke all tokens issued to one OAuth client for a principal;
|
|
256
|
+
3. increment one principal's revocation version or suspend the principal;
|
|
257
|
+
4. change or revoke one membership/grant revision.
|
|
258
|
+
|
|
259
|
+
Keep whole-bridge secret rotation for suspected deployment compromise. Ordinary account removal should not disconnect every other principal.
|
|
260
|
+
|
|
261
|
+
Managed jobs require separate treatment: a running job snapshots authority at acceptance and can outlive an MCP token. Revoking an account should block new calls immediately and provide an explicit option to cancel active jobs accepted by that membership. Silent automatic cancellation may be unsafe when cleanup/finally steps are required.
|
|
262
|
+
|
|
263
|
+
## Administration surface
|
|
264
|
+
|
|
265
|
+
Account management is an operator function, not an ordinary MCP tool available to every account. A future CLI could expose:
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
machine-mcp account invite
|
|
269
|
+
machine-mcp account list
|
|
270
|
+
machine-mcp account show ACCOUNT_ID
|
|
271
|
+
machine-mcp account set-grant ACCOUNT_ID GRANT
|
|
272
|
+
machine-mcp account suspend ACCOUNT_ID
|
|
273
|
+
machine-mcp account revoke ACCOUNT_ID
|
|
274
|
+
machine-mcp account sessions ACCOUNT_ID
|
|
275
|
+
machine-mcp account revoke-session SESSION_ID
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
These names are a design proposal, not current commands.
|
|
279
|
+
|
|
280
|
+
Administrative mutations should require local operator access or a separately protected admin plane. Do not let a normal `full` MCP session implicitly create additional principals merely because it can execute shell commands; that would make account administration indistinguishable from local-machine takeover.
|
|
281
|
+
|
|
282
|
+
## Concurrency and quotas
|
|
283
|
+
|
|
284
|
+
The existing global bounds should remain. Add fair per-principal limits for:
|
|
285
|
+
|
|
286
|
+
- concurrent relay calls;
|
|
287
|
+
- OAuth clients and active tokens;
|
|
288
|
+
- authorization failures;
|
|
289
|
+
- process sessions;
|
|
290
|
+
- accepted managed jobs;
|
|
291
|
+
- response bytes and rate windows where needed.
|
|
292
|
+
|
|
293
|
+
A principal limit must not replace a global limit. Otherwise many accounts can exhaust one daemon collectively.
|
|
294
|
+
|
|
295
|
+
A single local daemon still serializes or coordinates operations that mutate shared workspace state. Account support does not make concurrent Git operations, patches, browser actions, or service changes conflict-free. Existing mutation locks and transactional checks remain authoritative.
|
|
296
|
+
|
|
297
|
+
## Audit and privacy
|
|
298
|
+
|
|
299
|
+
Operational logs must continue to omit credentials, tool arguments, file contents, process output, browser content, and personal identifiers.
|
|
300
|
+
|
|
301
|
+
For account diagnosis, log only bounded pseudonymous identifiers such as an HMAC-derived principal handle, grant ID/revision, event class, and outcome. Store a separate owner-only administrative audit record only when there is a concrete operator requirement, with explicit retention and access controls.
|
|
302
|
+
|
|
303
|
+
Do not expose account lists, emails, identity-provider subjects, or session inventories through ordinary `server_info`. A principal may see its own effective grant and token expiry; only the local administrator should see cross-account inventory.
|
|
304
|
+
|
|
305
|
+
## Cloudflare Durable Object topology
|
|
306
|
+
|
|
307
|
+
Retain one Durable Object instance per bridge/workspace. It already provides a serializable state and WebSocket coordination boundary for that bridge.
|
|
308
|
+
|
|
309
|
+
Avoid these two extremes:
|
|
310
|
+
|
|
311
|
+
- **One Durable Object per access token:** this fragments the one-daemon call registry and complicates global limits, cancellation, and grant updates.
|
|
312
|
+
- **One global Durable Object for every workspace and tenant:** this increases blast radius, couples unrelated deployments, creates a central routing/credential registry, and makes local lifecycle operations harder to reason about.
|
|
313
|
+
|
|
314
|
+
If a future shared control plane is required, keep it limited to account discovery/invitations and route each request to the bridge-specific Durable Object. Local tool calls should still terminate at the bridge boundary.
|
|
315
|
+
|
|
316
|
+
## Rejected designs
|
|
317
|
+
|
|
318
|
+
### Treat `client_id` as the account
|
|
319
|
+
|
|
320
|
+
Rejected because one application may serve many users and one user may use many applications. Client rotation or reinstall would also appear to create a new person.
|
|
321
|
+
|
|
322
|
+
### Give each user a different connection password only
|
|
323
|
+
|
|
324
|
+
Rejected because credentials, tokens, grants, revocation, quotas, and audit events still lack a stable principal binding.
|
|
325
|
+
|
|
326
|
+
### Enforce grants only in the Worker
|
|
327
|
+
|
|
328
|
+
Rejected because the local runtime is the final filesystem/process boundary and must fail closed independently.
|
|
329
|
+
|
|
330
|
+
### Enforce grants only in the daemon
|
|
331
|
+
|
|
332
|
+
Rejected because the Worker would advertise and relay tools to unauthorized callers, increasing leakage and attack surface.
|
|
333
|
+
|
|
334
|
+
### Put all machines and workspaces behind one tenant router immediately
|
|
335
|
+
|
|
336
|
+
Rejected as unnecessary coupling. The existing per-workspace lifecycle is a useful security and operational boundary.
|
|
337
|
+
|
|
338
|
+
### Share `full` among mutually untrusted accounts
|
|
339
|
+
|
|
340
|
+
Rejected because `full` exposes local-user process, shell, browser, and filesystem authority. Application-level role checks cannot provide OS isolation.
|
|
341
|
+
|
|
342
|
+
## Incremental delivery plan
|
|
343
|
+
|
|
344
|
+
### Phase 0: make the boundary explicit
|
|
345
|
+
|
|
346
|
+
- document current multi-client behavior and lack of account isolation;
|
|
347
|
+
- recommend separate bridge/OS boundaries for different trust domains;
|
|
348
|
+
- keep current behavior unchanged.
|
|
349
|
+
|
|
350
|
+
### Phase 1: principal records and targeted revocation
|
|
351
|
+
|
|
352
|
+
- add owner principal migration;
|
|
353
|
+
- bind codes/tokens to principal and membership IDs;
|
|
354
|
+
- add per-principal suspension and revocation version;
|
|
355
|
+
- preserve one common grant initially;
|
|
356
|
+
- add migration, expiry, quota, and revocation tests.
|
|
357
|
+
|
|
358
|
+
### Phase 2: named grants and dual enforcement
|
|
359
|
+
|
|
360
|
+
- derive grant capabilities from the shared policy contract;
|
|
361
|
+
- advertise grant catalog revision/digest in the daemon handshake;
|
|
362
|
+
- filter tool lists/calls in the Worker and recheck locally;
|
|
363
|
+
- add stale-revision, reconnect, policy-narrowing, cancellation, and managed-job tests.
|
|
364
|
+
|
|
365
|
+
### Phase 3: operator account lifecycle
|
|
366
|
+
|
|
367
|
+
- add invitation, list, suspension, grant, and session-revocation CLI commands;
|
|
368
|
+
- add owner-only persistence and administrative audit retention;
|
|
369
|
+
- add concurrent and fault-injection tests for account mutations.
|
|
370
|
+
|
|
371
|
+
### Phase 4: optional external identity
|
|
372
|
+
|
|
373
|
+
- add passkey/WebAuthn or configured OIDC adapters only after the principal/grant model is stable;
|
|
374
|
+
- keep single-owner password compatibility simple;
|
|
375
|
+
- do not require a central SaaS account service.
|
|
376
|
+
|
|
377
|
+
## Acceptance criteria for claiming multi-account support
|
|
378
|
+
|
|
379
|
+
The project should not advertise isolated multi-account support until tests prove all of the following:
|
|
380
|
+
|
|
381
|
+
- two principals can authorize through the same client and receive different grants;
|
|
382
|
+
- one principal can use two clients without becoming two accounts;
|
|
383
|
+
- revoking one principal leaves another principal's sessions valid;
|
|
384
|
+
- narrowing the local policy immediately prevents a broader account grant from executing;
|
|
385
|
+
- a stale grant revision fails in both Worker and daemon paths;
|
|
386
|
+
- `tools/list` and `tools/call` enforce the same effective grant;
|
|
387
|
+
- disconnect, daemon replacement, token expiry, and secret rotation converge safely;
|
|
388
|
+
- account quotas cannot bypass global bounds;
|
|
389
|
+
- managed jobs are attributed to a membership and have an explicit revocation/cancellation policy;
|
|
390
|
+
- logs and public status do not expose personal identifiers or credentials;
|
|
391
|
+
- different trust domains remain externally isolated when process/browser authority is available.
|
|
392
|
+
|
|
393
|
+
Until those conditions are implemented, the accurate product statement is:
|
|
394
|
+
|
|
395
|
+
> Machine Bridge supports multiple OAuth clients and tokens for one trusted workspace owner or trust domain. It does not yet provide isolated multi-user tenancy in one deployment.
|
|
396
|
+
|
|
397
|
+
## Standards and implementation references
|
|
398
|
+
|
|
399
|
+
- [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
|
|
400
|
+
- [OAuth 2.0 Security Best Current Practice (RFC 9700)](https://www.rfc-editor.org/rfc/rfc9700)
|
|
401
|
+
- [OAuth 2.0 Authorization Server Issuer Identification (RFC 9207)](https://www.rfc-editor.org/rfc/rfc9207)
|
|
402
|
+
- [OAuth 2.0 Resource Indicators (RFC 8707)](https://www.rfc-editor.org/rfc/rfc8707)
|
|
403
|
+
- [OAuth 2.0 Protected Resource Metadata (RFC 9728)](https://www.rfc-editor.org/rfc/rfc9728)
|
|
404
|
+
- [Cloudflare Durable Objects documentation](https://developers.cloudflare.com/durable-objects/)
|
|
405
|
+
- [Security policy](../SECURITY.md)
|
|
406
|
+
- [Architecture](ARCHITECTURE.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "machine-bridge-mcp",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "Cross-client MCP bridge for local agent context, structured browser and application automation, files, Git, processes, resources, and durable jobs over stdio or OAuth relay.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/worker/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "./http";
|
|
18
18
|
|
|
19
19
|
const SERVER_NAME = String(serverMetadata.name);
|
|
20
|
-
const SERVER_VERSION = "0.17.
|
|
20
|
+
const SERVER_VERSION = "0.17.1";
|
|
21
21
|
const MCP_PROTOCOL_VERSION = String(serverMetadata.protocolVersion);
|
|
22
22
|
const MCP_SUPPORTED_PROTOCOL_VERSIONS = serverMetadata.supportedProtocolVersions.map((value) => String(value));
|
|
23
23
|
const JSONRPC_VERSION = "2.0";
|