machine-bridge-mcp 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/browser-extension/manifest.json +2 -2
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/AUDIT.md +1 -1
- package/docs/CLIENTS.md +1 -1
- package/docs/GETTING_STARTED.md +1 -1
- package/docs/TESTING.md +2 -2
- package/package.json +1 -1
- package/src/worker/http.ts +19 -8
- package/src/worker/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.2 - 2026-07-15
|
|
4
|
+
|
|
5
|
+
### Copilot Studio regional OAuth callback
|
|
6
|
+
|
|
7
|
+
- Allow a validated Microsoft `consent.azure-apim.net` OAuth callback to continue through Power Platform's HTTPS regional consent subdomains. Chromium applies the authorization page's `form-action` policy across redirects; the previous exact-global-origin policy let the Worker issue a valid authorization code but blocked Microsoft's `global` to `asia-001` handoff before token exchange, leaving Copilot Studio disconnected.
|
|
8
|
+
- Keep the exception narrow: every authorization page still allows only `'self'` and its exact validated redirect origin, and only callbacks already validated on the Microsoft consent domain receive `https://*.consent.azure-apim.net`. Other OAuth clients, CORS rules, redirect binding, PKCE, account authentication, and token validation are unchanged.
|
|
9
|
+
- Add Worker integration coverage for the Copilot Studio CSP and a real headless-Chrome three-stage regression proving that self-only policy blocks the first callback, exact-first-hop policy blocks the regional redirect, and the completed policy preserves `code` and `state` through the two-hop chain.
|
|
10
|
+
|
|
3
11
|
## 1.1.1 - 2026-07-15
|
|
4
12
|
|
|
5
13
|
### Windows Worker deployment convergence
|
package/README.md
CHANGED
|
@@ -150,7 +150,7 @@ The same `/mcp` endpoint supports the current hosted-client paths documented by
|
|
|
150
150
|
|
|
151
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.
|
|
152
152
|
|
|
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
|
|
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 validated redirect origin for that request. When the validated callback belongs to Microsoft `consent.azure-apim.net`, it additionally permits HTTPS subdomains of that same Microsoft consent domain so Power Platform can hand the global callback to its regional endpoint without granting arbitrary form destinations.
|
|
154
154
|
|
|
155
155
|
### Multiple clients and accounts
|
|
156
156
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Machine Bridge Browser",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.2",
|
|
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.1.
|
|
33
|
+
"version_name": "1.1.2"
|
|
34
34
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -243,7 +243,7 @@ Removal first acquires a state-root maintenance lock that blocks new profile/sta
|
|
|
243
243
|
|
|
244
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.
|
|
245
245
|
|
|
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.
|
|
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. If that validated origin is Microsoft `consent.azure-apim.net`, the policy also admits `https://*.consent.azure-apim.net`; Power Platform first receives the authorization code at its global consent endpoint and then redirects to a regional endpoint, and Chromium applies `form-action` across that redirect chain. No other callback receives a wildcard source. This allows successful callback navigation without opening form submission 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.
|
|
247
247
|
|
|
248
248
|
## Observability
|
|
249
249
|
|
package/docs/AUDIT.md
CHANGED
|
@@ -16,7 +16,7 @@ The Claude and Microsoft Copilot Studio request was first tested against the exi
|
|
|
16
16
|
|
|
17
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
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
|
|
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. A live Copilot Studio connection exposed an additional browser boundary: Power Platform redirects the validated global `consent.azure-apim.net` callback to a regional subdomain, while Chromium enforces the originating authorization page's `form-action` policy across that redirect chain. The Worker therefore adds the Microsoft consent-subdomain source only when the already validated redirect URI is itself on that HTTPS domain; all other clients retain the exact-origin policy. Power Platform data policies, plan availability, tenant administration, and host-side tool filtering remain external controls.
|
|
20
20
|
|
|
21
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.
|
|
22
22
|
|
package/docs/CLIENTS.md
CHANGED
|
@@ -130,7 +130,7 @@ Supported configuration paths are:
|
|
|
130
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
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
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
|
|
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 validated redirect origin. For a validated Microsoft `consent.azure-apim.net` callback, the form policy also permits HTTPS subdomains of that same Microsoft consent domain because Power Platform redirects its global callback to a regional endpoint. 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
134
|
|
|
135
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).
|
|
136
136
|
|
package/docs/GETTING_STARTED.md
CHANGED
|
@@ -233,7 +233,7 @@ Microsoft currently recommends the MCP onboarding wizard:
|
|
|
233
233
|
5. Select **Create**, then **Next**.
|
|
234
234
|
6. Create or select a connection and choose **Add to agent**.
|
|
235
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.
|
|
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. The authorization page also permits Power Platform's global `consent.azure-apim.net` callback to hand the browser to its regional consent endpoint. 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
237
|
|
|
238
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.
|
|
239
239
|
|
package/docs/TESTING.md
CHANGED
|
@@ -55,9 +55,9 @@ The suite includes:
|
|
|
55
55
|
- deterministic property tests over hostile browser-protocol byte strings, canonical/custom policy combinations, argv bounds/NULs, and a real direct process proving shell metacharacters remain literal argv;
|
|
56
56
|
- CLI parsing, policy profiles, and client configuration boundaries;
|
|
57
57
|
- live stdio MCP initialization with session instructions, capability resolution, discovery, calls, rich content, sessions, cancellation, managed-job acceptance, and a detached job/finally phase that survives stdio shutdown;
|
|
58
|
-
- live local Worker OAuth registration, the unauthenticated `resource_metadata` challenge, protected-resource and authorization-server discovery, Streamable transport metadata, consent, URL-constructed `303` callbacks including the ChatGPT and hosted Claude redirect URIs with encoded state, PKCE, `offline_access`, form-encoded authorization-code and refresh-token exchanges, access/refresh rotation, stale refresh replay rejection, account-version refresh revocation, authorization-code replay rejection, pending-registration throttling that excludes already authorized DCR clients, exact built-in ChatGPT/Grok browser origins, additive custom origins, unrelated-origin preflight rejection, no CORS response sharing for unrelated or opaque origins, opaque-origin authorization-form routing, exact per-request redirect-origin CSP, accessible credential-error rendering, protocol negotiation, HMAC-bound MCP session issuance, two-session same-id concurrency, sessionless same-id independence, session-scoped cancellation isolation, same-session duplicate rejection, daemon-backed session bootstrap, dynamic tool advertisement, rich content, daemon replacement, cancellation, malformed daemon JSON/non-object rejection, duplicate hello rejection, and unknown-message closure. The metadata/refresh contract is the path used by Claude DCR and Copilot Studio Dynamic discovery. The same integration runs an `editor` account against a canonical `full` daemon and proves that `server_info` and remote `project_overview` report effective `edit` authority while retaining the full daemon ceiling only in explicitly scoped fields.
|
|
58
|
+
- live local Worker OAuth registration, the unauthenticated `resource_metadata` challenge, protected-resource and authorization-server discovery, Streamable transport metadata, consent, URL-constructed `303` callbacks including the ChatGPT and hosted Claude redirect URIs with encoded state, PKCE, `offline_access`, form-encoded authorization-code and refresh-token exchanges, access/refresh rotation, stale refresh replay rejection, account-version refresh revocation, authorization-code replay rejection, pending-registration throttling that excludes already authorized DCR clients, exact built-in ChatGPT/Grok browser origins, additive custom origins, unrelated-origin preflight rejection, no CORS response sharing for unrelated or opaque origins, opaque-origin authorization-form routing, exact per-request redirect-origin CSP with a Microsoft consent-domain regional handoff exception, accessible credential-error rendering, protocol negotiation, HMAC-bound MCP session issuance, two-session same-id concurrency, sessionless same-id independence, session-scoped cancellation isolation, same-session duplicate rejection, daemon-backed session bootstrap, dynamic tool advertisement, rich content, daemon replacement, cancellation, malformed daemon JSON/non-object rejection, duplicate hello rejection, and unknown-message closure. The metadata/refresh contract is the path used by Claude DCR and Copilot Studio Dynamic discovery. The same integration runs an `editor` account against a canonical `full` daemon and proves that `server_info` and remote `project_overview` report effective `edit` authority while retaining the full daemon ceiling only in explicitly scoped fields.
|
|
59
59
|
- local runtime proof that one blocked tool handler does not serialize an independent handler, plus relay fault injection proving an undeliverable terminal result interrupts the ambiguous socket and enters reconnect backoff.
|
|
60
|
-
- a real headless-Chrome OAuth navigation regression with
|
|
60
|
+
- a real headless-Chrome OAuth navigation regression with three cases: `form-action 'self'` blocks the first cross-origin callback, allowing only the registered callback still blocks a second cross-origin redirect, and allowing both callback origins completes the two-hop chain while preserving `code` and `state`. Linux CI fails if Chrome is unavailable; other environments skip only this browser executable check while retaining the Worker CSP assertions.
|
|
61
61
|
|
|
62
62
|
## Opt-in live desktop and browser validation
|
|
63
63
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "machine-bridge-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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/http.ts
CHANGED
|
@@ -64,14 +64,7 @@ export function json(value: unknown, status = 200, extraHeaders: HeadersInit = {
|
|
|
64
64
|
|
|
65
65
|
export function html(value: string, status = 200, formActionOrigin = ""): Response {
|
|
66
66
|
const allowedFormActions = new Set(["'self'"]);
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const origin = new URL(formActionOrigin).origin;
|
|
70
|
-
if (origin !== "null") allowedFormActions.add(origin);
|
|
71
|
-
} catch {
|
|
72
|
-
// Invalid values are ignored; callers pass a validated OAuth redirect origin.
|
|
73
|
-
}
|
|
74
|
-
}
|
|
67
|
+
for (const source of authorizationFormActionSources(formActionOrigin)) allowedFormActions.add(source);
|
|
75
68
|
const formAction = [...allowedFormActions].join(" ");
|
|
76
69
|
return new Response(value, {
|
|
77
70
|
status,
|
|
@@ -86,6 +79,24 @@ export function html(value: string, status = 200, formActionOrigin = ""): Respon
|
|
|
86
79
|
});
|
|
87
80
|
}
|
|
88
81
|
|
|
82
|
+
function authorizationFormActionSources(value: string): string[] {
|
|
83
|
+
if (!value) return [];
|
|
84
|
+
try {
|
|
85
|
+
const url = new URL(value);
|
|
86
|
+
if (url.origin === "null") return [];
|
|
87
|
+
const sources = [url.origin];
|
|
88
|
+
const microsoftConsentHost = url.hostname === "consent.azure-apim.net" || url.hostname.endsWith(".consent.azure-apim.net");
|
|
89
|
+
if (url.protocol === "https:" && !url.port && microsoftConsentHost) {
|
|
90
|
+
// Power Platform redirects its global consent callback to a regional subdomain, and CSP checks every hop.
|
|
91
|
+
sources.push("https://*.consent.azure-apim.net");
|
|
92
|
+
}
|
|
93
|
+
return sources;
|
|
94
|
+
} catch {
|
|
95
|
+
// Invalid values are ignored; callers pass a validated OAuth redirect origin.
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
89
100
|
export function baseUrl(request: Request): string {
|
|
90
101
|
return new URL(request.url).origin;
|
|
91
102
|
}
|
package/src/worker/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "./http";
|
|
25
25
|
|
|
26
26
|
const SERVER_NAME = String(serverMetadata.name);
|
|
27
|
-
const SERVER_VERSION = "1.1.
|
|
27
|
+
const SERVER_VERSION = "1.1.2";
|
|
28
28
|
const MCP_PROTOCOL_VERSION = String(serverMetadata.protocolVersion);
|
|
29
29
|
const MCP_SUPPORTED_PROTOCOL_VERSIONS = serverMetadata.supportedProtocolVersions.map((value) => String(value));
|
|
30
30
|
const JSONRPC_VERSION = "2.0";
|