machine-bridge-mcp 1.0.8 → 1.1.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.1 - 2026-07-15
4
+
5
+ ### Windows Worker deployment convergence
6
+
7
+ - Make Worker health probes use the same standard `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` routing model as the relay. The previous direct `fetch` probe could time out on Windows or managed networks after Wrangler had already completed a valid deployment. Restrict probes to the recorded HTTPS `workers.dev` origin and reject redirects so persisted state cannot redirect the verifier to an arbitrary network target.
8
+ - Persist the successful Wrangler deployment URL, content/secret fingerprint, and deployed package version before the secondary health probe. A timeout, proxy failure, TLS failure, network failure, or temporary HTTP 5xx response now stops with corrective guidance and a later start verifies the same Worker instead of uploading it again; only bounded, definitive stale-version/identity evidence triggers automatic same-name redeployment.
9
+ - Prevent accidental Worker proliferation by rejecting a changed `--worker-name` for an initialized workspace unless `--force-worker` explicitly authorizes the replacement. Retain prior names in local inventory so full uninstall can delete intentionally replaced Workers. Add a real local CONNECT-proxy regression, deployment ambiguity/idempotency tests, worker-name replacement tests, expanded CLI/state coverage, and higher critical-module coverage floors.
10
+
11
+ ## 1.1.0 - 2026-07-15
12
+
13
+ ### Claude and Microsoft Copilot Studio remote MCP
14
+
15
+ - Add hosted-client OAuth interoperability for Claude custom connectors and Microsoft Copilot Studio while preserving the existing Streamable HTTP transport and current MCP protocol contract. Authorization-server and protected-resource discovery now advertise `offline_access` and the refresh-token grant; dynamic client registration declares both authorization-code and refresh-token grants.
16
+ - Issue hashed access and refresh-token records from authorization-code exchange. Public-client refresh requests are form-encoded, bound to the original client/account/version/role/scope/resource, and rotate the refresh token atomically. Reuse, expiry, account changes, or deployment token-version changes return `invalid_grant`. Refresh state uses a separate versioned Durable Object key, so existing primary OAuth state does not require migration. The per-source DCR throttle now counts only registrations that have not completed authorization, preventing legitimate Claude or Copilot reconnections from consuming the abuse quota while retaining the deployment-wide client cap.
17
+ - Exercise the exact hosted Claude callback, the unauthenticated `resource_metadata` challenge, discovery/DCR metadata, refreshed MCP access, refresh replay rejection, and account-targeted refresh revocation in the real Worker integration. Document Claude and Copilot Studio setup, and retain the narrow ChatGPT/Grok browser CORS set because Claude and Copilot Studio connect through cloud-side infrastructure rather than browser-origin response sharing.
18
+
3
19
  ## 1.0.8 - 2026-07-14
4
20
 
5
21
  ### Effective account authority reporting
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  It supports two transports:
6
6
 
7
7
  ```text
8
- Remote clients such as ChatGPT
8
+ Remote clients such as ChatGPT, Claude, and Copilot Studio
9
9
  HTTPS + OAuth 2.1 / PKCE
10
10
  |
11
11
  Cloudflare Worker + Durable Object
@@ -47,7 +47,7 @@ This default prioritizes usability, not least privilege. `run_process` and proce
47
47
 
48
48
  ## Install
49
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.
50
+ For prerequisites, first remote deployment, hosted-client 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
51
 
52
52
  Version 1 supports only MCP protocol `2025-11-25`. Existing current-schema local state upgrades in place, but clients must reconnect and the unpacked browser extension must be reloaded after package upgrade.
53
53
 
@@ -95,7 +95,7 @@ npm install
95
95
  .\mbm.cmd # Windows cmd
96
96
  ```
97
97
 
98
- ## Remote MCP for ChatGPT
98
+ ## Remote MCP for hosted clients
99
99
 
100
100
  After a global installation, `machine-mcp` can be launched from any Command Prompt or terminal; the package installation directory is not the workspace. On the first interactive Windows start, the CLI asks:
101
101
 
@@ -120,6 +120,10 @@ On first remote start, the CLI:
120
120
  7. creates the initial `owner` account when needed and prints its password once;
121
121
  8. prints the Remote MCP URL.
122
122
 
123
+ Worker deployment is convergent rather than append-only. Each workspace retains one stable Worker name. After Wrangler reports a successful upload, Machine Bridge records the Worker URL, package/secret fingerprint, and deployed version before performing the independent `/healthz` verification. A proxy, TLS, network, timeout, or temporary service failure therefore does not erase the successful deployment evidence or cause the next start to upload again. Only a bounded health result proving that the recorded Worker is stale causes an automatic redeploy under the same name.
124
+
125
+ Do not append suffixes such as `-r2` to work around a health timeout: a different `--worker-name` is a different Cloudflare Worker. Changing an initialized workspace's Worker name is rejected unless the same command also supplies `--force-worker`, which makes the replacement intentional and keeps the prior name in uninstall inventory.
126
+
123
127
  A normal `machine-mcp` invocation is a foreground start: it remains attached to the terminal and prints `info` logs. Startup and other state-changing operations use an owner-token/process-identity lock and wait up to 30 seconds for an ordinary concurrent startup to finish instead of failing on a brief launchd/systemd race. After a global package upgrade, the CLI unloads the platform service and also checks the workspace lock for a detached/orphan `--daemon-only` process that the service manager no longer tracks. It terminates only a process whose PID, process start time, entrypoint, canonical workspace, canonical state root, and daemon-only arguments all match, waits up to 15 seconds for the PID and lock to disappear, and then starts the installed version in the foreground. A genuine foreground or unverifiable conflict is left untouched and reported with actionable guidance. To run only in the background, use `machine-mcp service start`; inspect its owner-only logs under `~/.local/state/machine-bridge-mcp/logs/`.
124
128
 
125
129
  Recommended upgrade sequence: repeat the package-free temporary-directory installation above, then run:
@@ -136,13 +140,21 @@ Use the printed URL in the MCP client:
136
140
  MCP Server URL: https://<worker>.<account>.workers.dev/mcp
137
141
  ```
138
142
 
139
- During OAuth authorization, enter the name and password of a Machine Bridge account. The first start creates `owner` and prints its generated password once. Additional accounts are managed with `machine-mcp account`. The authorization flow uses PKCE S256, exact redirect/resource binding, expiring hashed access tokens, per-account version checks, and a deployment-wide token version for emergency bulk revocation.
143
+ During OAuth authorization, enter the name and password of a Machine Bridge account. The first start creates `owner` and prints its generated password once. Additional accounts are managed with `machine-mcp account`. The authorization flow exposes protected-resource and authorization-server discovery, dynamic client registration, PKCE S256, exact redirect/resource binding, `offline_access`, expiring hashed access and refresh-token records, public-client refresh-token rotation, per-account version checks, and a deployment-wide token version for emergency bulk revocation.
144
+
145
+ The same `/mcp` endpoint supports the current hosted-client paths documented by their vendors:
146
+
147
+ - ChatGPT and Grok can use the public Streamable HTTP endpoint and browser OAuth flow.
148
+ - Claude custom connectors can add the exact `/mcp` URL and use dynamic client registration. The hosted Claude callback `https://claude.ai/api/mcp/auth_callback`, `offline_access`, form-encoded token exchange, proactive/reactive refresh, and one-time refresh-token rotation are covered by the Worker integration contract.
149
+ - Microsoft Copilot Studio can use **Tools > Add a tool > New tool > Model Context Protocol**, select **OAuth 2.0 > Dynamic discovery**, and point **Server URL** at the exact `/mcp` endpoint. Machine Bridge advertises the Streamable transport, discovery metadata, DCR, access tokens, and rotating refresh tokens expected by that path.
150
+
151
+ These are protocol and integration-contract claims, not evidence that every external tenant, plan, administrator policy, or host-side tool filter has been tested. Claude remote connectors originate from Anthropic's cloud, and Copilot Studio reaches MCP servers through Power Platform connectivity; neither requires adding a browser origin to Machine Bridge's CORS list.
140
152
 
141
- The Worker grants browser CORS response access to the exact first-party origins used by ChatGPT (`https://chatgpt.com` and the legacy `https://chat.openai.com`) and Grok (`https://grok.com` and its X-hosted surface at `https://x.com`). Users do not need to run Wrangler or edit Cloudflare variables. `MBM_ALLOWED_ORIGINS` remains an operator-only, comma-separated extension point for other exact origins; it is additive and does not replace the built-in set. Wildcards and `null` do not receive CORS permission. Actual OAuth navigations and form submissions are not rejected solely because a browser container supplies an opaque or client-specific `Origin`; authorization still depends on exact redirect/resource binding, PKCE, account authentication, and bearer-token validation. The consent page CSP permits form navigation only to the Worker itself and the exact validated redirect origin for that request, so Chromium can follow the successful `303` callback without granting arbitrary form destinations.
153
+ The Worker grants browser CORS response access to the exact first-party origins used by ChatGPT (`https://chatgpt.com` and the legacy `https://chat.openai.com`) and Grok (`https://grok.com` and its X-hosted surface at `https://x.com`). Users do not need to run Wrangler or edit Cloudflare variables. `MBM_ALLOWED_ORIGINS` remains an operator-only, comma-separated extension point for other exact browser origins; it is additive and does not replace the built-in set. Wildcards and `null` do not receive CORS permission. Actual OAuth navigations and form submissions are not rejected solely because a browser container supplies an opaque or client-specific `Origin`; authorization still depends on exact redirect/resource binding, PKCE, account authentication, and bearer-token validation. The consent page CSP permits form navigation only to the Worker itself and the exact validated redirect origin for that request, so Chromium can follow the successful `303` callback without granting arbitrary form destinations.
142
154
 
143
155
  ### Multiple clients and accounts
144
156
 
145
- One Worker supports several named accounts and OAuth clients. Accounts have independent passwords, roles, active state, versions, codes, and tokens. The roles are `reviewer`, `editor`, `operator`, and `owner`; their effective authority is intersected with the connected daemon policy and enforced in both the Worker and local runtime. Role changes, suspension, password rotation, and removal revoke only the affected account.
157
+ One Worker supports several named accounts and OAuth clients. Accounts have independent passwords, roles, active state, versions, authorization codes, access tokens, and refresh tokens. The roles are `reviewer`, `editor`, `operator`, and `owner`; their effective authority is intersected with the connected daemon policy and enforced in both the Worker and local runtime. Role changes, suspension, password rotation, and removal revoke only the affected account.
146
158
 
147
159
  For remote connections, `server_info.authorization.effective_policy` and `server_info.authorization.effective_tools` are the authoritative account-permission fields. `server_info.daemon.policy` and `server_info.daemon.tools` describe only the local daemon capability ceiling before account-role filtering. Remote `project_overview` likewise places the effective account policy/tools in `policy` and `tools`, while preserving the daemon ceiling separately as `daemonPolicy` and `daemonTools`.
148
160
 
@@ -219,7 +231,7 @@ machine-mcp browser status
219
231
 
220
232
  Load the printed unpacked-extension directory in the Chromium profile you actually use; Machine Bridge does not install it into Playwright or a separate automation profile. After every Machine Bridge upgrade, reload the unpacked extension and accept any new browser permission. `machine-mcp browser status` reports both the expected packaged extension build and the authenticated connected build/protocol, and states that Machine Bridge did not launch the browser. It cannot infer whether the extension was loaded into a daily or isolated profile; that is determined by where the user installed it. Trusted input uses the Chromium `debugger` permission only for fixed, short-lived DevTools Input commands. The badge shows `ON` only after the broker acknowledges the version/capability handshake. The local pairing token remains in owner-only state and non-cacheable loopback HTML; it is not returned through MCP. For a mass-market release, distribute the same extension as a signed browser-store build rather than asking end users to enable Developer mode. See [Local application and browser automation](docs/LOCAL_AUTOMATION.md).
221
233
 
222
- Machine Bridge can discover, refresh, rank, and load capabilities automatically. The ChatGPT/MCP host still owns tool selection and approval, so the server cannot force a host to expose or invoke a recommended skill, command, app, or browser operation. Check `server_info.observability.capability_routing` or `project_overview.capabilityRouting` to distinguish “the host never called the resolver” from “the resolver ran but found no relevant capability.”
234
+ Machine Bridge can discover, refresh, rank, and load capabilities automatically. The MCP host still owns tool selection and approval, so the server cannot force a host to expose or invoke a recommended skill, command, app, or browser operation. Check `server_info.observability.capability_routing` or `project_overview.capabilityRouting` to distinguish “the host never called the resolver” from “the resolver ran but found no relevant capability.”
223
235
 
224
236
 
225
237
  ## Runtime lifecycle and observability
@@ -472,7 +484,7 @@ The service definition contains neither credentials nor a duplicate policy. It l
472
484
  machine-mcp rotate-secrets
473
485
  ```
474
486
 
475
- Rotation stops the installed service, refuses to proceed while another foreground daemon owns the workspace lock, and rotates the account-administration secret, daemon secret, and deployment-wide OAuth token version. It invalidates every account access token and requires all clients to authorize again. Use `machine-mcp account rotate-password NAME` for targeted password rotation without affecting other accounts.
487
+ Rotation stops the installed service, refuses to proceed while another foreground daemon owns the workspace lock, and rotates the account-administration secret, daemon secret, and deployment-wide OAuth token version. It invalidates every account access and refresh token and requires all clients to authorize again. Use `machine-mcp account rotate-password NAME` for targeted password rotation without affecting other accounts.
476
488
 
477
489
  ## State and observability
478
490
 
@@ -484,7 +496,7 @@ Default state roots:
484
496
 
485
497
  State/config writes use owner-only temporary files, `fsync`, and atomic replacement. Exclusive locks are fully written before a same-directory hard-link claim becomes visible; lock ownership includes a token and process start time so PID reuse and lock replacement cannot silently transfer ownership. Only successfully read but invalid JSON is retained as a bounded corrupt backup; permission, symbolic-link, size, encoding, and I/O failures remain explicit. A custom state root must not equal, contain, or be contained by the selected workspace. Resource source paths are redacted from `status` output. Active managed-job plans are owner-only and are deleted after a terminal result; bounded redacted results are retained temporarily. Uninstall first acquires a state-root maintenance lock that blocks new profile/state operations and state-backed operations from already constructed managed-job/browser managers, stops the platform service and all verified workspace daemons before removing definitions, rechecks jobs and locks, then validates markers, canonical paths, workspace/source exclusions, and known contents before recursive deletion.
486
498
 
487
- The relay honors standard `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment routing. Invalid or unsupported proxy configuration fails fast; runtime status reports only `direct`, `proxy`, or `invalid-proxy-configuration`, never proxy URLs or credentials.
499
+ Worker health probes and the relay honor standard `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment routing. Invalid or unsupported proxy configuration fails fast. Health and relay diagnostics report only coarse `direct`, `proxy`, or invalid-route state and never proxy URLs or credentials.
488
500
 
489
501
  Default foreground logs report authenticated relay readiness, readable persistent-degradation summaries, and recovery rather than raw WebSocket callbacks or JSON field dumps. Brief self-healing disconnects and close codes/reasons are debug-only. Stalled connection attempts have a deadline, and sustained-outage reminders use autonomous exponential backoff. Every per-tool event—including success, failure, cancellation, and slow-call timing—also appears only at `--log-level debug` or `--verbose`. Background services use `warn`, so ordinary tool outcomes and brief network changes do not fill daemon logs. Log messages and structured fields are bounded, secret-like keys and known token formats are redacted, and tool arguments/results are not written. See [docs/LOGGING.md](docs/LOGGING.md) and [docs/OPERATIONS.md](docs/OPERATIONS.md).
490
502
 
@@ -499,7 +511,7 @@ npm audit --omit=dev --audit-level=high
499
511
  npm pack --dry-run
500
512
  ```
501
513
 
502
- `npm run check` covers privacy and release-impact gates, architecture/link invariants, generated Worker types, TypeScript, recursively discovered JavaScript syntax, semantic undefined-identifier checks, 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, an isolated installation of the actual packed tarball whose zero-argument CLI startup must reach a controlled Worker-deployment boundary, managed-job integrity/redaction/finally/cancellation/recovery, a live stdio MCP flow, a live local OAuth/Worker/WebSocket/MCP flow, and a real Chromium negative/positive test for CSP-governed OAuth callback navigation. GitHub Actions runs the complete suite on Linux, macOS, and Windows with the pinned Node 26/npm 12 baseline.
514
+ `npm run check` covers privacy and release-impact gates, architecture/link invariants, generated Worker types, TypeScript, recursively discovered JavaScript syntax, semantic undefined-identifier checks, 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, an isolated installation of the actual packed tarball whose zero-argument CLI startup must reach a controlled Worker-deployment boundary, managed-job integrity/redaction/finally/cancellation/recovery, a live stdio MCP flow, a live local OAuth/Worker/WebSocket/MCP flow covering discovery, Claude callback routing, rotating refresh tokens, replay rejection, and account-targeted refresh revocation, plus a real Chromium negative/positive test for CSP-governed OAuth callback navigation. GitHub Actions runs the complete suite on Linux, macOS, and Windows with the pinned Node 26/npm 12 baseline.
503
515
 
504
516
  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).
505
517
 
package/SECURITY.md CHANGED
@@ -40,7 +40,7 @@ The Worker is a remote authentication and relay boundary. The local runtime is t
40
40
 
41
41
  ### Accounts are authorization boundaries, not OS sandboxes
42
42
 
43
- Remote mode supports named accounts with independent passwords, roles, active state, versions, OAuth codes, and access tokens. The roles `reviewer`, `editor`, `operator`, and `owner` map to the local review, edit, agent, and full policy profiles. The Worker intersects the account role with the connected daemon policy, and the local runtime validates the account role again before dispatch. Account suspension, role changes, password rotation, and removal revoke only that account.
43
+ Remote mode supports named accounts with independent passwords, roles, active state, versions, OAuth authorization codes, access tokens, and rotating refresh tokens. The roles `reviewer`, `editor`, `operator`, and `owner` map to the local review, edit, agent, and full policy profiles. The Worker intersects the account role with the connected daemon policy, and the local runtime validates the account role again before dispatch. Account suspension, role changes, password rotation, and removal revoke only that account.
44
44
 
45
45
  An OAuth `client_id` still identifies client software and redirect URIs; it is not an account. One account can authorize several clients, and one client can be authorized by several accounts.
46
46
 
@@ -162,11 +162,11 @@ Runner diagnostic logs are owner-only and do not receive child stdout/stderr. Ex
162
162
 
163
163
  ## OAuth and public endpoints
164
164
 
165
- Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, per-account version checks, deployment-wide token-version revocation, and bounded dynamic client registration. Authorization codes and tokens are bound to one account ID, account version, and role. 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.
165
+ Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, protected-resource and authorization-server discovery, bounded dynamic client registration, `offline_access`, expiring authorization codes, access tokens, and refresh tokens, hashed bearer-token storage, per-account version checks, and deployment-wide token-version revocation. Authorization codes and both token classes are bound to one client ID, account ID, account version, role, scope, and resource. Public-client refresh tokens are single-use: a successful refresh atomically stores a new access/refresh pair and removes the presented refresh token; replay and account/version invalidation return RFC 6749 `invalid_grant`. 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.
166
166
 
167
167
  The authorization page displays the validated client name and redirect URI. Enter an account name and password only after initiating the connection and recognizing both values.
168
168
 
169
- Password failures and registrations are limited by deployment-keyed HMAC source identity. Browser requests are same-origin unless an exact origin is listed in `MBM_ALLOWED_ORIGINS`; loopback OAuth redirect permission does not grant browser-origin access.
169
+ Password failures and pending, not-yet-authorized client registrations are limited by deployment-keyed HMAC source identity; successfully authorized DCR clients no longer consume that pending-registration quota, while the global client cap remains authoritative. Browser requests are same-origin unless an exact origin is listed in `MBM_ALLOWED_ORIGINS`; loopback OAuth redirect permission does not grant browser-origin access.
170
170
 
171
171
  Public health and metadata do not expose live workspace or daemon status. The daemon handshake omits workspace path/name/hash and process ID.
172
172
 
@@ -174,7 +174,7 @@ Public health and metadata do not expose live workspace or daemon status. The da
174
174
 
175
175
  Only one authenticated daemon is active. Candidates have a handshake deadline and cannot displace the current daemon before success. Pending calls are socket-bound, client-request-bound, concurrency-limited, size-limited, and timed out. Duplicate in-flight request IDs are rejected only within the same authenticated MCP session. Initialization returns a stateless HMAC-bound `MCP-Session-Id`; the signature binds the session nonce to the OAuth token identity, so one account can use multiple concurrent chat windows without sharing a cancellation or request-id namespace. Independent sessionless POST requests are not indexed by token and request id. A session id is an integrity/correlation token, not a second authorization credential.
176
176
 
177
- Request bodies, WebSocket messages, tool outputs, traversals, sessions, stdin writes, OAuth records, clients, codes, tokens, and failure identities are bounded. Disconnect/replacement terminates active child process trees.
177
+ Request bodies, WebSocket messages, tool outputs, traversals, sessions, stdin writes, OAuth records, clients, codes, access tokens, refresh tokens, and failure identities are bounded. Disconnect/replacement terminates active child process trees.
178
178
 
179
179
  These controls reduce accidental exhaustion and simple abuse. They do not replace Cloudflare account MFA, WAF/rate limits, usage alerts, or cost controls for an internet-facing Worker.
180
180
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Machine Bridge Browser",
4
- "version": "1.0.8",
4
+ "version": "1.1.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": "1.0.8"
33
+ "version_name": "1.1.1"
34
34
  }
@@ -103,14 +103,14 @@ The stdio server implements newline-delimited JSON-RPC over stdin/stdout. It neg
103
103
 
104
104
  All requests for a deployed Worker route to one named Durable Object. It owns:
105
105
 
106
- - OAuth clients, authorization codes, hashed access-token records, and throttling metadata;
106
+ - OAuth clients, authorization codes, hashed access-token records, an independently versioned hashed refresh-token store, and throttling metadata;
107
107
  - one active authenticated daemon WebSocket plus bounded candidate sockets;
108
108
  - policy/tool metadata attached to the active socket;
109
109
  - a bounded in-memory map of pending daemon calls.
110
110
 
111
111
  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.
112
112
 
113
- The OAuth store separates client registrations from named accounts. A `client_id` identifies an MCP application and redirect URIs; account records identify the authorized human or service identity. Codes and tokens bind client ID, account ID, account version, role, scope, resource, and expiration. The Worker intersects the role with the active daemon policy before advertising or relaying tools, and the local runtime validates the relayed role again. One bridge-specific Durable Object and one local runtime remain the normal topology for a workspace/trust domain; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
113
+ The primary OAuth store separates client registrations and named accounts from authorization codes and access-token records. A separate Durable Object key owns refresh-token records so refresh support can be added without rewriting the live primary-store schema. A `client_id` identifies an MCP application and redirect URIs; account records identify the authorized human or service identity. Codes, access tokens, and refresh tokens bind client ID, account ID, account version, role, scope, resource, token version, and expiration. Only hashes of bearer tokens are persisted, and every public-client refresh invalidates the presented refresh token while returning a replacement. The Worker intersects the role with the active daemon policy before advertising or relaying tools, and the local runtime validates the relayed role again. One bridge-specific Durable Object and one local runtime remain the normal topology for a workspace/trust domain; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
114
114
 
115
115
  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.
116
116
 
@@ -141,16 +141,16 @@ flowchart LR
141
141
  CLI --> S[Autostart provider]
142
142
  ```
143
143
 
144
- Remote OAuth binds each code and token to a named Machine Bridge account, account version, and role, so accounts are independent application-level authorization principals. The Worker intersects that role with the connected daemon policy, and the local runtime enforces the relayed authorization again. This distinction is not operating-system tenancy: every account still reaches one daemon, workspace, browser profile, and OS user authority. Local stdio access relies on the local process and configuration boundary. 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.
144
+ Remote OAuth binds each code, access token, and refresh token to a named Machine Bridge account, account version, and role, so accounts are independent application-level authorization principals. The Worker intersects that role with the connected daemon policy, and the local runtime enforces the relayed authorization again. This distinction is not operating-system tenancy: every account still reaches one daemon, workspace, browser profile, and OS user authority. Local stdio access relies on the local process and configuration boundary. 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.
145
145
 
146
146
  ## Remote request lifecycle
147
147
 
148
148
  1. The MCP client discovers protected-resource and authorization-server metadata.
149
- 2. It dynamically registers bounded redirect metadata.
149
+ 2. It dynamically registers bounded redirect metadata. Per-source throttling counts only registrations that have not yet completed authorization, while a separate global cap bounds all retained clients.
150
150
  3. The Worker validates authorization parameters before displaying a password form.
151
151
  4. The user verifies client name and redirect URI and enters a Machine Bridge account name and password.
152
- 5. The Worker creates a five-minute code bound to client, redirect, resource, scope, and PKCE challenge.
153
- 6. A valid verifier exchanges the one-time code for an expiring bearer token; only its hash is stored.
152
+ 5. The Worker creates a five-minute code bound to client, redirect, resource, normalized scope, and PKCE challenge.
153
+ 6. A valid verifier exchanges the one-time code for an expiring access token and refresh token; only their hashes are stored. A refresh request is bound to the original public client, account, scope, resource, and deployment token version, and atomically replaces the refresh token so replay returns `invalid_grant`.
154
154
  7. The MCP client initializes against the sole current protocol version; an obsolete client must upgrade rather than enter a legacy execution path. The Worker returns a stateless HMAC-bound `MCP-Session-Id`, and later request/cancellation correlation is scoped by OAuth token, MCP session, JSON-RPC id type, and id value. Two clients may therefore reuse the same JSON-RPC id concurrently without collision. Sessionless POSTs remain independent and are not inserted into a token-global cancellation index. When the daemon advertises `session_bootstrap`, the Worker requests bounded local instructions and appends them to the initialization result; failure degrades to static instructions.
155
155
  8. `tools/list` is derived from the active daemon handshake; without a daemon, only `server_info` is advertised.
156
156
  9. `tools/call` receives a random relay call ID and is bound to the current socket and authenticated client request key.
@@ -211,9 +211,17 @@ Child processes run in a separate process group where supported. Timeout, cancel
211
211
 
212
212
  Managed jobs use the same argv/environment primitives but a different lifecycle. Each job is capped at 16 main and 16 finally steps, 50 retained jobs, 64 registered resources, 8 MiB of referenced resource bytes, 512 KiB of temporary-file content, and bounded per-step output. They are non-interactive. Resource paths/stdin/environment are injected only inside the runner. Exact resource output redaction is defense in depth; discard capture is the strong option when a command may echo credentials.
213
213
 
214
+ ## Worker deployment convergence
215
+
216
+ Worker deployment is an explicit two-evidence state machine owned by `worker-deployment.mjs`. Wrangler upload is the authoritative remote write. Public `/healthz` is a subsequent read used to verify identity and package version; it is not a transaction commit signal for the upload. After a successful Wrangler result, local state atomically records the detected `workers.dev` URL, MCP URL, content/secret fingerprint, deployed package version, and timestamp before health verification begins. If verification is ambiguous, the next start compares the same fingerprint and performs a read-only verification rather than repeating the remote write.
217
+
218
+ `worker-health.mjs` owns bounded health I/O: exact HTTPS `workers.dev` origin and Worker-name validation, environment-proxy selection, request timeout, redirect rejection, response-size limit, JSON/identity/version validation, and coarse error classification. `network-proxy.mjs` is shared by HTTP health probes and WebSocket relay construction so both paths honor `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` without exposing proxy details. Definitive stale evidence is retried for propagation and then permits a same-name redeploy; timeout, TLS, network, proxy, and temporary server failure remain ambiguous and fail without upload.
219
+
220
+ Worker-name mutation is a separate identity transition. Existing state rejects a different name unless the caller also supplies the explicit force option. An authorized transition clears the current URL/fingerprint and appends the prior validated name to bounded uninstall inventory. This prevents a health-retry workaround from silently becoming a new remote resource while preserving cleanup of intentional replacements.
221
+
214
222
  ## Daemon reconnect and replacement
215
223
 
216
- The local `RelayConnection` treats proxy selection, transport construction, transport open, authenticated readiness, and outage recovery as separate states. `network-proxy.mjs` maps WebSocket targets to standard HTTP(S) environment-proxy resolution, honors `NO_PROXY`, rejects non-HTTP(S) proxy schemes, and creates the proxy agent without exposing its URL or credentials. Invalid proxy configuration is a fatal configuration error rather than a retryable outage.
224
+ The local `RelayConnection` treats proxy selection, transport construction, transport open, authenticated readiness, and outage recovery as separate states. The shared proxy module maps WebSocket targets to standard HTTP(S) environment-proxy resolution, honors `NO_PROXY`, rejects non-HTTP(S) proxy schemes, and creates the proxy agent without exposing its URL or credentials. Invalid proxy configuration is a fatal configuration error rather than a retryable outage.
217
225
 
218
226
  The local `RelayConnection` otherwise treats transport construction, transport open, authenticated readiness, and outage recovery as separate states. A connection-attempt deadline terminates sockets stuck in `CONNECTING`; after WebSocket open it sends `hello` and reports readiness only after `hello_ack`. A missing acknowledgement reaches a distinct handshake deadline and the candidate is terminated. Once ready, application heartbeats require inbound activity; a silent half-open socket is terminated and reconnected. Outage reminders run on their own exponential-backoff timer rather than depending on another transport callback.
219
227
 
@@ -233,9 +241,9 @@ Active managed jobs persist an owner-only plan, status, runner process identity,
233
241
 
234
242
  Removal first acquires a state-root maintenance lock that blocks new profile/state claims and state-backed operations from already constructed managed-job/browser managers, then stops the platform service and all known verified workspace daemons. It then validates the state marker, canonical target, known contents, active or unreadable locks, filesystem root/home/current/package/workspace/source exclusions, managed jobs, and Worker deletion outcome before recursive deletion. Any unresolved phase retains definitions and state.
235
243
 
236
- OAuth metadata is pruned on access. Expired codes/tokens, old throttling records, and inactive clients without active credentials are removed. Source identities are deployment-keyed HMAC values, not stored source addresses or reversible unsalted hashes.
244
+ OAuth metadata is pruned on access. Expired authorization codes, access tokens, refresh tokens, old throttling records, and inactive clients without active credentials are removed. Account disablement, role or password changes, removal, and deployment-wide token-version rotation make both token classes unusable; stale refresh records are deleted when the refresh store is next read. Source identities are deployment-keyed HMAC values, not stored source addresses or reversible unsalted hashes.
237
245
 
238
- Browser-origin handling separates CORS response sharing from protocol authentication. Preflight succeeds only for the Worker's own origin, a fixed first-party set for ChatGPT (`https://chatgpt.com`, `https://chat.openai.com`) and Grok (`https://grok.com`, `https://x.com`), or optional exact comma-separated additions from `MBM_ALLOWED_ORIGINS`; wildcard and `null` origins are not granted CORS access. Actual requests are routed without using `Origin` as an authentication boundary, and `Access-Control-Allow-Origin` is added only when that exact predicate passes. This permits OAuth top-level navigation and form submission from opaque or client-specific containers while exact redirect/resource binding, PKCE, account credentials, bearer tokens, and private admin/daemon secrets enforce authority. The authorization document's `form-action` policy is generated only after request validation and contains `'self'` plus the exact origin of that validated redirect URI. This allows Chromium to follow the successful form submission's `303` callback without opening form navigation to unrelated origins.
246
+ Browser-origin handling separates CORS response sharing from protocol authentication. Preflight succeeds only for the Worker's own origin, a fixed first-party set for ChatGPT (`https://chatgpt.com`, `https://chat.openai.com`) and Grok (`https://grok.com`, `https://x.com`), or optional exact comma-separated additions from `MBM_ALLOWED_ORIGINS`; wildcard and `null` origins are not granted CORS access. Actual requests are routed without using `Origin` as an authentication boundary, and `Access-Control-Allow-Origin` is added only when that exact predicate passes. This permits OAuth top-level navigation and form submission from opaque or client-specific containers while exact redirect/resource binding, PKCE, account credentials, bearer tokens, and private admin/daemon secrets enforce authority. The authorization document's `form-action` policy is generated only after request validation and contains `'self'` plus the exact origin of that validated redirect URI. This allows Chromium to follow the successful form submission's `303` callback without opening form navigation to unrelated origins. Hosted Claude remote connectors originate from Anthropic infrastructure and Copilot Studio uses Power Platform connectivity, so their server-to-server requests do not require browser CORS entries; adding those brands to the browser allowlist would expand response sharing without enabling either integration.
239
247
 
240
248
  ## Observability
241
249
 
package/docs/AUDIT.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Engineering and security audit
2
2
 
3
+ ## 2026-07-15 Windows Worker deployment convergence incident
4
+
5
+ A Windows report showed Wrangler creating a Worker successfully, followed by `Worker deployment did not become healthy at the expected version: timeout`. Re-running startup, and then changing the name to a suffixed `-r2` variant, left multiple Workers in the Cloudflare account. The causal defect was not Worker creation itself: Wrangler and the long-lived relay already followed standard proxy routing, while the independent health verifier used a direct global `fetch`. On networks where outbound HTTPS required an environment proxy, deployment could succeed and health verification could time out.
6
+
7
+ The state transition amplified that routing split. The previous path removed or withheld the deployment fingerprint until `/healthz` succeeded. A successful remote write followed by an ambiguous read therefore looked identical to “not deployed” on the next start and caused another Wrangler upload. Supplying another `--worker-name` made the effect more visible by asking Cloudflare for a distinct resource; Machine Bridge did not generate the suffix automatically, but it also did not guard that identity change.
8
+
9
+ The correction separates upload evidence from verification evidence. `worker-deployment.mjs` persists the detected URL, exact content/secret fingerprint, deployed package version, and timestamp immediately after Wrangler success. `worker-health.mjs` accepts only the recorded HTTPS `workers.dev` origin matching the Worker name, rejects redirects, and performs the bounded request through the same reviewed proxy resolver used by the WebSocket path, with response-size and deadline limits. A subsequent timeout, proxy/TLS/network fault, or temporary 5xx now returns an actionable verification error while preserving the fingerprint, so the next start performs a read-only verification. Persistent version/identity mismatch or `404`/`410` is retried for propagation and is the only automatic same-name redeployment trigger.
10
+
11
+ Existing Worker names are stable. A different `--worker-name` is rejected unless `--force-worker` explicitly authorizes an identity replacement; the prior validated name is retained in bounded uninstall inventory. The test reproduces the original network topology with a real local CONNECT proxy and an otherwise unresolvable hostname, proves one deployment across repeated health timeouts, proves stale-version convergence, and covers accidental/intentional name transitions. Coverage gates now include the extracted proxy, health, and deployment modules and raise the remaining CLI floors. Live duplicate deletion remains an operator decision because repository code cannot safely infer which pre-fix Worker is externally referenced.
12
+
13
+ ## 2026-07-15 hosted-client OAuth interoperability audit
14
+
15
+ The Claude and Microsoft Copilot Studio request was first tested against the existing protocol boundary rather than implemented as a brand allowlist. Both clients already use the deployed Streamable HTTP endpoint; the material gap was token continuity. Anthropic's connector contract advertises `offline_access`, performs form-encoded refreshes, and requires public-client refresh-token rotation. Copilot Studio's recommended Dynamic discovery path likewise discovers DCR/OAuth endpoints and exchanges refresh tokens. Adding browser origins alone would not implement either contract: Claude remote connectors originate from Anthropic cloud infrastructure, while Copilot Studio uses Power Platform connectivity. The fixed ChatGPT/Grok browser CORS set therefore remains unchanged.
16
+
17
+ The Worker now advertises the refresh grant and `offline_access`, stores only hashes of access and refresh tokens, and rotates every refresh token in one Durable Object write with the associated access-token update. Refresh records live under a separate versioned storage key, preserving the current primary OAuth-store schema and avoiding a destructive live migration. Refreshes remain bound to client, account, account version, role, normalized scope, resource, and deployment token version. Old-token replay and account-version changes return `invalid_grant`; account suspension, role/password changes, removal, and global token-version rotation cannot extend authority through refresh. Because both hosted clients may create a new DCR client for a fresh connection, the source throttle now counts only registrations that have not completed authorization; the global retained-client bound is unchanged.
18
+
19
+ The workerd integration proves the hosted Claude callback, the unauthenticated `resource_metadata` challenge, discovery metadata, DCR grant advertisement, `offline_access`, form-encoded refresh, access/refresh replacement, replay rejection, continued MCP use with the refreshed access token, and targeted account refresh revocation. Copilot Studio compatibility is established at the documented protocol boundary rather than claimed as a live tenant test; Power Platform data policies, plan availability, tenant administration, and host-side tool filtering remain external controls.
20
+
3
21
  This document records the cross-cutting audit initiated for version 0.12.0, its later architecture, browser, lifecycle, authorization, release-gate, and version 1 follow-ups, including the version 1.0.2 elapsed-time review and the version 1.0.3 code-scanning inventory correction. It complements, but does not replace, the continuously enforced contracts in `SECURITY.md`, `docs/ENGINEERING.md`, and the test suite.
4
22
 
5
23
 
package/docs/CLIENTS.md CHANGED
@@ -7,7 +7,7 @@ An MCP server is not a model provider. It exposes tools, resources, or prompts.
7
7
  For this project:
8
8
 
9
9
  ```text
10
- Claude Desktop / Cursor / Codex / ChatGPT Desktop / another MCP host
10
+ Claude Desktop / Cursor / Codex / ChatGPT Desktop / another local MCP host
11
11
  owns the model, account, conversation, approvals, and tool-selection loop
12
12
  |
13
13
  | stdio or Streamable HTTP
@@ -32,7 +32,7 @@ For many users, the client's native filesystem, patch, terminal, and approval sy
32
32
  The stdio mode exists for narrower cases:
33
33
 
34
34
  1. **One tool contract across several clients.** The same tool names, schemas, patch behavior, Git handling, process sessions, result formats, and logging work in Claude Desktop, Cursor, Codex, ChatGPT Desktop, and other MCP hosts.
35
- 2. **Transport parity.** The local client can use essentially the same Machine Bridge runtime that remote ChatGPT access uses, without maintaining a second implementation.
35
+ 2. **Transport parity.** The local client can use essentially the same Machine Bridge runtime that hosted ChatGPT, Claude, Grok, and Copilot Studio connections use, without maintaining a second implementation.
36
36
  3. **No remote relay for same-machine use.** The host launches the server directly, so no Cloudflare Worker, public URL, OAuth flow, or network round trip is needed.
37
37
  4. **Capabilities missing from a particular host.** Exact edit semantics, transactional multi-file patches, retained process sessions, image results, or the project's policy profiles may be useful even when the host has basic shell tools.
38
38
  5. **Durable managed jobs.** The same detached job/resource/finally semantics are available to local and remote hosts, while local CLI remains an operator fallback.
@@ -44,7 +44,7 @@ It is therefore an optional compatibility and reuse surface, not a replacement f
44
44
 
45
45
  | Situation | Recommended path |
46
46
  |---|---|
47
- | ChatGPT web or another service must reach this computer remotely | Remote Worker with HTTPS/OAuth |
47
+ | ChatGPT, Claude, Grok, Copilot Studio, or another hosted service must reach this computer remotely | Remote Worker with Streamable HTTPS/OAuth |
48
48
  | A local MCP host needs Machine Bridge-specific tools | stdio |
49
49
  | The local coding client already provides everything needed | Use the client's native tools; do not configure Machine Bridge stdio |
50
50
  | Several local clients should share identical tool semantics | stdio |
@@ -56,7 +56,7 @@ The MCP specification defines stdio and Streamable HTTP as standard transports.
56
56
 
57
57
  MCP initialization and `resolve_task_capabilities` give the host a current view of conservative built-in working agreements, bounded project facts, explicit global/project instructions, skills, explicit/automatic package 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. No instruction file is required for the default layers, and no repository file is written automatically.
58
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.
59
+ The host still owns the agent loop. A hosted client 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
60
 
61
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
62
 
@@ -114,7 +114,7 @@ args = ["/absolute/path/to/machine-mcp.mjs", "stdio", "--workspace", "/path/to/p
114
114
 
115
115
  Codex CLI, the Codex IDE extension, and supported ChatGPT Desktop Codex hosts can share MCP configuration on the same Codex host. ChatGPT web does not read local Codex configuration files; web use requires a hosted plugin/connector or a reachable remote MCP endpoint.
116
116
 
117
- ## Remote ChatGPT and Grok connection
117
+ ## Remote hosted-client connection
118
118
 
119
119
  Run:
120
120
 
@@ -122,9 +122,17 @@ Run:
122
122
  machine-mcp --workspace /path/to/project
123
123
  ```
124
124
 
125
- Enter the printed `/mcp` URL in the remote MCP connector. During OAuth authorization, verify the displayed client name and redirect URI before entering a Machine Bridge account name and password. The Worker has exact built-in CORS response support for `https://chatgpt.com`, legacy `https://chat.openai.com`, `https://grok.com`, and the X-hosted Grok surface at `https://x.com`; no direct Wrangler command or Cloudflare variable edit is required. OAuth navigations and form submissions from opaque or client-specific browser containers are routed normally but receive no cross-origin response-sharing permission unless their exact origin is allowed. The authorization page also permits form navigation only to itself and the exact validated redirect origin, which is required for Chromium to follow the successful `303` back to the client.
125
+ Enter the printed `/mcp` URL in the remote MCP connector. During OAuth authorization, verify the displayed client name and redirect URI before entering a Machine Bridge account name and password. The Worker exposes Streamable HTTP plus protected-resource discovery, authorization-server discovery, dynamic client registration, PKCE S256, `offline_access`, and rotating refresh tokens.
126
126
 
127
- Several OAuth clients and named accounts can coexist. Accounts have independent passwords, roles, active state, versions, and targeted revocation. Their effective tool sets are intersected with the connected daemon policy. All accounts still share one daemon and OS user, so hard tenant isolation requires separate deployments; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
127
+ Supported configuration paths are:
128
+
129
+ - **ChatGPT and Grok:** enter the `/mcp` URL in the corresponding remote connector. The Worker has exact built-in CORS response support for `https://chatgpt.com`, legacy `https://chat.openai.com`, `https://grok.com`, and the X-hosted Grok surface at `https://x.com`.
130
+ - **Claude custom connectors:** add the exact `/mcp` URL under Claude's connector settings and leave static OAuth client credentials empty so Claude can use DCR. Hosted Claude surfaces use `https://claude.ai/api/mcp/auth_callback`; the Worker accepts that exact registered redirect and rotates refresh tokens for the public client. See Anthropic's [remote custom connector guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) and [authentication contract](https://claude.com/docs/connectors/building/authentication).
131
+ - **Microsoft Copilot Studio:** from the agent's **Tools** page, select **Add a tool > New tool > Model Context Protocol**, enter the `/mcp` URL, then select **OAuth 2.0 > Dynamic discovery**. Create the connection and add it to the agent. See Microsoft's [existing MCP server guide](https://learn.microsoft.com/en-us/microsoft-copilot-studio/mcp-add-existing-server-to-agent).
132
+
133
+ OAuth navigations and form submissions from opaque or client-specific browser containers are routed normally but receive no cross-origin response-sharing permission unless their exact origin is allowed. The authorization page permits form navigation only to itself and the exact validated redirect origin. Claude remote connectors originate from Anthropic infrastructure, while Copilot Studio connectivity runs through Power Platform connectors, so neither is a reason to broaden the browser CORS allowlist.
134
+
135
+ Several OAuth clients and named accounts can coexist. Accounts have independent passwords, roles, active state, versions, authorization codes, access tokens, refresh tokens, and targeted revocation. Their effective tool sets are intersected with the connected daemon policy. All accounts still share one daemon and OS user, so hard tenant isolation requires separate deployments; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
128
136
 
129
137
  ## Profile guidance
130
138
 
@@ -18,6 +18,7 @@ This document records project-wide decisions that must survive individual fixes,
18
18
  12. **Read failure is not empty state.** Permission, type, symbolic-link, size, encoding, and I/O errors must propagate. Corrupt backup/reconstruction applies only after a successful read proves that JSON content is invalid.
19
19
  13. **The public protocol contract is current-only.** Shared metadata advertises only the current MCP protocol version. Compatibility code for obsolete protocol dates, lock formats, or state schemas is not retained in the final runtime; upgrade safety comes from explicit version negotiation, fail-closed state validation, and bounded operator convergence.
20
20
  14. **Security analysis is a failing gate.** CodeQL or Scorecard execution alone is not success. Generated SARIF must contain no unaccepted result, and missing rule metadata fails closed rather than being interpreted as non-security. An intentional or externally constrained finding requires an exact rule/path record with a substantive rationale and an expiry date.
21
+ 15. **Ambiguous health is not permission to repeat a remote write.** A successful Wrangler deployment is recorded before secondary health verification. Timeout, proxy, TLS, network, and temporary service failures preserve the deployment fingerprint and fail for diagnosis; only bounded evidence of a stale identity/version permits automatic same-name redeployment. Changing the Worker name is an explicit remote-resource transition, not a retry strategy.
21
22
 
22
23
  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.
23
24
 
@@ -109,7 +110,7 @@ A higher branch count is acceptable only when the function is an explicit state
109
110
 
110
111
  - Preserve a stable coarse error class for automation and a concise human message for operators.
111
112
  - Do not retry an operation merely because it failed; retry only when the error is positively classified as transient and the operation is idempotent or server state is checked after ambiguity.
112
- - When a remote write may have succeeded before the response was lost, query authoritative state before repeating it.
113
+ - When a remote write may have succeeded before the response was lost, query authoritative state before repeating it. When the write itself returned success but a later verification read failed, persist the successful write evidence before returning the verification error.
113
114
  - Duration-based deadlines must use a monotonic clock. Wall time remains appropriate for persisted timestamps, credential expiry, retention, and operator-visible dates, but a system clock correction must not extend or prematurely terminate an in-process wait.
114
115
  - Timeouts must terminate the relevant process tree. Forced escalation must remain referenced until resistant descendants are handled; do not clear it merely because the direct child exited.
115
116
  - Half-open connections need liveness detection, not only periodic writes.
@@ -1,6 +1,6 @@
1
1
  # Installation and first-use guide
2
2
 
3
- This guide covers a clean installation, the first remote ChatGPT connection, local stdio clients, browser setup, routine operation, upgrades, troubleshooting, and removal.
3
+ This guide covers a clean installation, first remote connections from ChatGPT, Claude, Grok, and Microsoft Copilot Studio, local stdio clients, browser setup, routine operation, upgrades, troubleshooting, and removal.
4
4
 
5
5
  ## 1. Decide which connection mode you need
6
6
 
@@ -8,7 +8,7 @@ Machine Bridge has two transports. Choose one before installing:
8
8
 
9
9
  | Requirement | Use | Cloudflare required | Process that must remain available |
10
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 |
11
+ | ChatGPT, Claude, Grok, Copilot Studio, a hosted agent, or another device must reach this computer | Remote Streamable HTTPS/OAuth | Yes | Machine Bridge daemon or installed login service |
12
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
13
  | The local coding client already has equivalent file, Git, patch, and terminal tools | Neither | No | None |
14
14
 
@@ -140,6 +140,7 @@ Common installation failures:
140
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
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
142
  | Native package install warning | npm did not approve reviewed native build scripts | Use the documented `--allow-scripts` and `--omit=optional` command exactly |
143
+ | Worker deployment completed, then health verification timed out | The independent `/healthz` route is blocked, mis-proxied, or temporarily unavailable; the upload may already be valid | Keep the recorded Worker name, run `machine-mcp status` and `machine-mcp doctor` from the same proxy environment, then retry normally. Do not invent a suffixed `--worker-name`; the saved fingerprint prevents a duplicate upload. |
143
144
 
144
145
  ## 6. First remote start
145
146
 
@@ -168,6 +169,8 @@ The first start performs these operations:
168
169
 
169
170
  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.
170
171
 
172
+ The Worker name is a persistent workspace identity, not a retry counter. A successful Wrangler upload is recorded before health verification, so a later timeout does not require a new name and does not make the next start upload again. Supplying a different `--worker-name` for an initialized workspace requires `--force-worker` because it intentionally creates/replaces a separate Cloudflare Worker identity.
173
+
171
174
  To run only in the background after setup:
172
175
 
173
176
  ```sh
@@ -181,7 +184,7 @@ To avoid installing autostart during a temporary test:
181
184
  machine-mcp --workspace /path/to/project --no-autostart
182
185
  ```
183
186
 
184
- ## 7. Connect ChatGPT
187
+ ## 7. Connect a hosted MCP client
185
188
 
186
189
  Machine Bridge prints the remote endpoint and, only when creating the first account, a one-time owner password:
187
190
 
@@ -204,7 +207,35 @@ Current ChatGPT developer-mode flow, as documented in OpenAI's [Connect from Cha
204
207
  7. Enter the Machine Bridge account name and password only after those values are recognized.
205
208
  8. Create a new chat and enable the app for that conversation.
206
209
 
207
- 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. The same public `/mcp` URL can be entered in Grok where remote MCP configuration is available. Machine Bridge grants CORS response access to the exact `https://grok.com` and `https://x.com` browser origins as well as ChatGPT's current and legacy origins without requiring a Wrangler command. OAuth navigation and form POST requests are not failed solely because an embedded authorization container reports an opaque or different origin. After validation, the consent page CSP includes only the exact registered callback origin so the browser can complete the `303` return to ChatGPT or Grok.
210
+ 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. The same public `/mcp` URL can be entered in Grok where remote MCP configuration is available. Machine Bridge grants CORS response access to the exact `https://grok.com` and `https://x.com` browser origins as well as ChatGPT's current and legacy origins without requiring a Wrangler command. OAuth navigation and form POST requests are not failed solely because an embedded authorization container reports an opaque or different origin. After validation, the consent page CSP includes only the exact registered callback origin so the browser can complete the `303` return.
211
+
212
+ ### Claude custom connector
213
+
214
+ For an individual Pro or Max account, Anthropic currently documents this flow:
215
+
216
+ 1. Open **Customize > Connectors**.
217
+ 2. Select **+ > Add custom connector**.
218
+ 3. Enter the exact printed `/mcp` URL.
219
+ 4. Leave optional static OAuth client credentials empty; Machine Bridge supports dynamic client registration.
220
+ 5. Add the connector, select **Connect**, and complete the Machine Bridge authorization page.
221
+ 6. Enable the connector for a conversation from the chat's connector menu.
222
+
223
+ For Team and Enterprise organizations, an Owner first adds **Custom > Web** under **Organization settings > Connectors**, after which members connect individually. Hosted Claude surfaces register `https://claude.ai/api/mcp/auth_callback`, request `offline_access` when advertised, and refresh tokens before or after access-token expiry. Machine Bridge accepts that callback and rotates each public-client refresh token exactly once. See Anthropic's [custom connector guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) and [connector authentication requirements](https://claude.com/docs/connectors/building/authentication).
224
+
225
+ ### Microsoft Copilot Studio
226
+
227
+ Microsoft currently recommends the MCP onboarding wizard:
228
+
229
+ 1. Open the agent's **Tools** page.
230
+ 2. Select **Add a tool > New tool > Model Context Protocol**.
231
+ 3. Enter a server name, description, and the exact printed `/mcp` URL.
232
+ 4. Select **OAuth 2.0**, then **Dynamic discovery**.
233
+ 5. Select **Create**, then **Next**.
234
+ 6. Create or select a connection and choose **Add to agent**.
235
+
236
+ Copilot Studio supports Streamable transport rather than the obsolete SSE transport. Its dynamic-discovery path uses the Worker's protected-resource metadata, authorization-server metadata, DCR endpoint, authorization-code exchange, and refresh-token exchange. See Microsoft's [existing MCP server guide](https://learn.microsoft.com/en-us/microsoft-copilot-studio/mcp-add-existing-server-to-agent). Power Platform data policies and tenant administration can still block a connection independently of Machine Bridge.
237
+
238
+ Claude remote connectors originate from Anthropic's cloud, and Copilot Studio reaches the endpoint through Power Platform connectors. Those server-to-server paths do not require adding Claude or Microsoft browser domains to `MBM_ALLOWED_ORIGINS`; widening CORS would not solve tenant, firewall, or Power Platform policy failures.
208
239
 
209
240
  ## 8. Verify the first connection
210
241
 
@@ -368,7 +399,9 @@ A connected Worker with no authenticated daemon can authorize OAuth successfully
368
399
 
369
400
  ### MCP-host layer
370
401
 
371
- 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.
402
+ 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, connection status, tenant policy, and whether its configured server URL exactly ends in `/mcp`.
403
+
404
+ An OAuth refresh response of `invalid_grant` is intentional after refresh-token rotation, account suspension, role change, password rotation, removal, or deployment-wide token-version rotation. Remove and reconnect the hosted connector rather than retrying the stale refresh token.
372
405
 
373
406
  ### Operating-system layer
374
407
 
package/docs/LOGGING.md CHANGED
@@ -33,6 +33,12 @@ Foreground mode defaults to `info` and human-readable text. Platform autostart s
33
33
  | `info` | Deployment/startup transitions, authenticated relay readiness, and recovery after a visible outage |
34
34
  | `debug` | Transport open/close codes and reasons, brief self-healing interruptions, retry timing, per-tool outcomes, and shortened correlation IDs |
35
35
 
36
+ ## Worker deployment and health event policy
37
+
38
+ Wrangler upload and `/healthz` verification are logged as separate state transitions. A successful upload followed by ambiguous health failure must say that the deployment fingerprint was retained and that retry will verify rather than redeploy. Existing-state timeout, proxy, TLS, network, and temporary server failures must say that no deployment was attempted. Only a persistent stale identity/version result may emit the warning that the same Worker is being redeployed.
39
+
40
+ Health routing records only `direct` or `proxy` at debug level. Proxy URLs, credentials, request headers, Worker secrets, and raw response bodies are never fields. Repeated per-attempt health failures remain debug-only; the terminal startup error contains one user-readable classification and corrective commands.
41
+
36
42
  ## Relay connection event policy
37
43
 
38
44
  A TCP/WebSocket `open` event is only transport availability. The daemon is reported as connected only after the Worker returns `hello_ack` for the authenticated `hello` message.
@@ -79,7 +85,7 @@ The implementation omits:
79
85
  - stdin, stdout, and stderr;
80
86
  - file, patch, image, and temporary-file content;
81
87
  - OAuth request bodies;
82
- - account passwords, account-administration secrets, daemon secrets, authorization codes, and access tokens;
88
+ - account passwords, account-administration secrets, daemon secrets, authorization codes, access tokens, and refresh tokens;
83
89
  - registered resource values and source paths;
84
90
  - browser pairing tokens, page URLs/source, DOM metadata, form values, uploaded file bytes, and screenshots;
85
91
  - application names, Accessibility trees, selectors, and entered values;
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What isolation means
4
4
 
5
- Machine Bridge remote mode supports several named accounts on one workspace Worker. Each account has an independent password, role, active state, version, OAuth authorization codes, and access tokens. Account changes revoke only that account's outstanding credentials.
5
+ Machine Bridge remote mode supports several named accounts on one workspace Worker. Each account has an independent password, role, active state, version, OAuth authorization codes, access tokens, and refresh tokens. Account changes revoke only that account's outstanding credentials.
6
6
 
7
7
  This is application-level authorization, not operating-system isolation. All accounts ultimately reach one local daemon running as one OS user. Roles limit which Machine Bridge tools can be listed and invoked; they do not create separate filesystems, browser profiles, process namespaces, keychains, network identities, or kernel security boundaries.
8
8
 
@@ -10,6 +10,24 @@ machine-mcp service status
10
10
 
11
11
  `status` prints redacted profile state and verifies the deployed Worker version. Resource source paths remain redacted. `doctor` checks Node.js, the package-installed Wrangler binary, Cloudflare login, Worker health, and the same fixed local filesystem/process/shell/job-storage/resource probes exposed by `diagnose_runtime`. Public `/healthz` output contains only server identity and version; daemon details require an authenticated `server_info` call.
12
12
 
13
+ ### Worker deployment and health convergence
14
+
15
+ Wrangler upload and public health verification are two separate observations. Once Wrangler reports a successful deployment and supplies the `workers.dev` URL, Machine Bridge immediately records that URL together with the exact deployment fingerprint and package version. It then verifies `/healthz` through the standard `HTTPS_PROXY`/`HTTP_PROXY` and `NO_PROXY` environment route. If that secondary probe times out or encounters a proxy, TLS, network, or temporary HTTP 5xx failure, startup stops with an actionable error, but the successful deployment evidence remains. The next ordinary start verifies the same Worker and does not repeat the upload.
16
+
17
+ Automatic redeployment is limited to bounded health evidence that the recorded endpoint is genuinely stale: a persistent package-version mismatch, an unexpected Machine Bridge identity, or a persistent `404`/`410`. Unreachability is not proof of absence. `--force-worker` remains the explicit override when an operator deliberately wants an upload despite matching state.
18
+
19
+ Each workspace has one stable Worker name. A command that supplies a different `--worker-name` after initialization is rejected unless `--force-worker` is present. A name such as `mbm-example-r2` is a separate Cloudflare Worker, not a revision of `mbm-example`. If an older release already created duplicates, first run `machine-mcp status` and verify the active URL/name, then remove only the confirmed unused Workers in the Cloudflare dashboard or with Wrangler. Source upgrades do not delete ambiguous live deployments automatically.
20
+
21
+ For a health timeout on Windows or a managed network:
22
+
23
+ ```powershell
24
+ machine-mcp status
25
+ machine-mcp doctor
26
+ machine-mcp --verbose
27
+ ```
28
+
29
+ Run these commands from the same environment used for startup so `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` are identical. Debug logs expose only the selected route and classified error; they never print a proxy endpoint or credentials.
30
+
13
31
  ### Blocking-layer decision table
14
32
 
15
33
  | Result | Interpretation |
@@ -198,6 +216,12 @@ Uninstall refuses to remove local state while any managed job remains active. Ac
198
216
 
199
217
  See [MANAGED_JOBS.md](MANAGED_JOBS.md).
200
218
 
219
+ ## Hosted OAuth reconnection
220
+
221
+ Hosted clients may refresh access without asking the user to authorize again. Machine Bridge returns a replacement refresh token on every successful public-client refresh and invalidates the presented token. `invalid_grant` therefore means the token was already rotated, expired, or revoked by an account change or deployment-wide token-version rotation. Remove and reconnect the hosted connector; repeatedly retrying the stale token cannot recover it.
222
+
223
+ Claude and Copilot Studio call the public Worker from their cloud connectivity layers. Do not add broad Anthropic or Microsoft domains to `MBM_ALLOWED_ORIGINS` as a connectivity workaround; that variable controls browser response sharing, not server-to-server reachability, tenant policy, WAF rules, or Power Platform data policy.
224
+
201
225
  ## Reconnect and replacement
202
226
 
203
227
  The daemon sends heartbeats and reconnects with bounded exponential backoff and jitter. A new socket remains a candidate until it authenticates and sends a valid `hello`; only then does it replace the previous daemon.