machine-bridge-mcp 3.0.0-beta.20 → 3.0.0-beta.21

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -0
  3. package/SECURITY.md +4 -4
  4. package/browser-extension/manifest.json +1 -1
  5. package/docs/ARCHITECTURE.md +11 -9
  6. package/docs/AUDIT.md +16 -0
  7. package/docs/ENGINEERING.md +1 -1
  8. package/docs/LOCAL_AUTHORIZATION.md +1 -1
  9. package/docs/LOGGING.md +3 -1
  10. package/docs/MULTI_ACCOUNT.md +1 -1
  11. package/docs/OPERATIONS.md +5 -3
  12. package/docs/OVERVIEW.md +1 -1
  13. package/docs/PRIVACY.md +1 -1
  14. package/docs/TESTING.md +9 -7
  15. package/docs/THREAT_MODEL.md +3 -2
  16. package/docs/UPGRADING.md +7 -1
  17. package/package.json +1 -1
  18. package/src/local/process-tree-ownership.mjs +17 -5
  19. package/src/local/process-tree.mjs +6 -2
  20. package/src/local/relay-connection.mjs +24 -11
  21. package/src/local/runtime-activation.mjs +16 -0
  22. package/src/local/runtime.mjs +3 -1
  23. package/src/local/worker-deployment.mjs +16 -6
  24. package/src/worker/daemon-socket-attachment.ts +52 -0
  25. package/src/worker/daemon-sockets.ts +17 -48
  26. package/src/worker/durable-stream-calls.ts +129 -0
  27. package/src/worker/durable-stream-result.ts +22 -0
  28. package/src/worker/index.ts +120 -88
  29. package/src/worker/mcp-pending-call-expiry.ts +20 -0
  30. package/src/worker/mcp-pending-call-inspection.ts +33 -0
  31. package/src/worker/mcp-pending-call-records.ts +58 -0
  32. package/src/worker/mcp-pending-call-storage.ts +18 -0
  33. package/src/worker/mcp-pending-call-store.ts +285 -0
  34. package/src/worker/mcp-resumption-index.ts +41 -0
  35. package/src/worker/mcp-resumption-records.ts +8 -2
  36. package/src/worker/mcp-resumption.ts +80 -87
  37. package/src/worker/mcp-stream-dispatch.ts +42 -54
  38. package/src/worker/pending-admission.ts +15 -0
  39. package/src/worker/pending-call-contract.ts +4 -7
  40. package/src/worker/pending-calls.ts +4 -10
  41. package/src/worker/runtime-alarm-storage.ts +35 -0
  42. package/src/worker/runtime-alarm.ts +29 -39
  43. package/src/worker/websocket-protocol.ts +4 -0
  44. package/src/worker/worker-entry.ts +50 -0
@@ -1,10 +1,13 @@
1
- import type { PendingCallOutcome, PendingCallRecord } from "./pending-call-contract.ts";
2
- import { PendingCallRegistrationError, PendingCallRegistry } from "./pending-calls.ts";
3
- import type { DaemonSocketRegistry } from "./daemon-sockets.ts";
1
+ import type { PendingCallOutcome } from "./pending-call-contract.ts";
2
+ import type { JsonRpcMessage, McpResumptionStore } from "./mcp-resumption.ts";
3
+ import {
4
+ McpPendingCallConflictError,
5
+ McpPendingCallLimitError,
6
+ } from "./mcp-pending-call-store.ts";
7
+ import type { PendingStreamTransform } from "./mcp-pending-call-records.ts";
4
8
  import { publicWorkerToolError, WorkerToolError } from "./errors.ts";
5
9
  import { workerErrorClass } from "./http.ts";
6
10
  import { rpcResult, textToolResult } from "./mcp-jsonrpc.ts";
7
- import type { JsonRpcMessage, McpResumptionStore } from "./mcp-resumption.ts";
8
11
  import { randomToken } from "./oauth-state.ts";
9
12
  import type { WorkerObservability } from "./observability.ts";
10
13
 
@@ -17,7 +20,6 @@ export type StreamCallAuthorization = {
17
20
  };
18
21
 
19
22
  type StartStreamCallInput = {
20
- pending: PendingCallRegistry;
21
23
  resumption: McpResumptionStore;
22
24
  observability: WorkerObservability;
23
25
  streamId: string;
@@ -27,11 +29,13 @@ type StartStreamCallInput = {
27
29
  arguments: Record<string, unknown>;
28
30
  socket: WebSocket;
29
31
  daemonInstanceId: string;
32
+ connectionId: string;
30
33
  timeoutMs: number;
31
34
  authorization: StreamCallAuthorization;
32
- onTimeout: (record: PendingCallRecord) => Error;
35
+ transientActiveCount: number;
36
+ maximumPendingCalls: number;
37
+ transform?: PendingStreamTransform;
33
38
  onSendFailure: () => void | Promise<void>;
34
- transformResult?: (value: unknown) => unknown;
35
39
  };
36
40
 
37
41
  type ImmediateOutcomeInput = {
@@ -50,9 +54,10 @@ export function buildServerInfoResult(input: {
50
54
  oauth: Record<string, unknown>;
51
55
  authorization: Record<string, any>;
52
56
  daemon: Record<string, any>;
53
- tools: string[];
54
- pending: PendingCallRegistry;
55
- daemonRegistry: DaemonSocketRegistry;
57
+ effectiveTools: string[];
58
+ advertisedTools: string[];
59
+ pendingSnapshot: Record<string, unknown>;
60
+ daemonRegistry: import("./daemon-sockets.ts").DaemonSocketRegistry;
56
61
  observability: WorkerObservability;
57
62
  }): Record<string, unknown> {
58
63
  const probing = input.daemonRegistry.probingSockets().length;
@@ -68,7 +73,7 @@ export function buildServerInfoResult(input: {
68
73
  authority_summary: input.authorization.summary,
69
74
  daemon: input.daemon,
70
75
  worker: {
71
- pending_calls: input.pending.snapshot(),
76
+ pending_calls: input.pendingSnapshot,
72
77
  daemon_candidates: candidates,
73
78
  daemon_probes: probing,
74
79
  sockets_live: {
@@ -79,14 +84,15 @@ export function buildServerInfoResult(input: {
79
84
  },
80
85
  observability: input.observability.snapshot(),
81
86
  },
82
- tools: input.tools,
87
+ tools: input.effectiveTools,
83
88
  tools_scope: "authenticated_account_effective_tools_before_host_filtering",
84
89
  tool_delivery: {
85
90
  full_profile_scope: "daemon-capability-ceiling-before-account-filtering",
86
91
  daemon_advertised_tool_count: input.daemon.tool_count,
87
- relay_advertised_tool_count: input.tools.length,
88
- effective_account_tool_count: input.tools.length,
89
- relay_advertised_scope: "authenticated_account_effective_tools_before_host_filtering",
92
+ relay_advertised_tool_count: input.advertisedTools.length,
93
+ effective_account_tool_count: input.effectiveTools.length,
94
+ relay_advertised_scope: "stable_authenticated_account_catalog_before_host_filtering",
95
+ effective_scope: "live_daemon_and_account_intersection_before_host_filtering",
90
96
  host_exposed_tools_known_to_server: false,
91
97
  host_may_expose_subset: true,
92
98
  },
@@ -95,30 +101,28 @@ export function buildServerInfoResult(input: {
95
101
 
96
102
  export async function startEventDrivenStreamCall(input: StartStreamCallInput): Promise<void> {
97
103
  const callId = randomToken("call");
98
- const settle = createStreamSettlement({
99
- resumption: input.resumption,
100
- observability: input.observability,
101
- streamId: input.streamId,
102
- requestId: input.requestId,
103
- tool: input.tool,
104
- transformResult: input.transformResult,
105
- });
106
104
  try {
107
- input.pending.registerEvent({
108
- id: callId,
109
- socket: input.socket,
105
+ await input.resumption.calls.activate({
106
+ streamId: input.streamId,
107
+ callId,
110
108
  daemonInstanceId: input.daemonInstanceId,
109
+ connectionId: input.connectionId,
111
110
  clientRequestKey: input.clientRequestKey,
112
111
  tool: input.tool,
113
112
  timeoutMs: input.timeoutMs,
114
- onTimeout: input.onTimeout,
115
- settle,
113
+ transform: input.transform,
114
+ transientActiveCount: input.transientActiveCount,
115
+ maximumPendingCalls: input.maximumPendingCalls,
116
116
  });
117
117
  } catch (error) {
118
- if (error instanceof PendingCallRegistrationError) throw new WorkerToolError(error.code, error.message, error.retryable);
118
+ if (error instanceof McpPendingCallLimitError) {
119
+ throw new WorkerToolError("limit_exceeded", error.message, true);
120
+ }
121
+ if (error instanceof McpPendingCallConflictError) {
122
+ throw new WorkerToolError("conflict", error.message);
123
+ }
119
124
  throw error;
120
125
  }
121
- input.resumption.activate(input.streamId);
122
126
  input.observability.callStarted(input.tool);
123
127
  try {
124
128
  input.socket.send(JSON.stringify({
@@ -130,7 +134,9 @@ export async function startEventDrivenStreamCall(input: StartStreamCallInput): P
130
134
  authorization: input.authorization,
131
135
  }));
132
136
  } catch {
133
- await input.pending.reject(callId, new WorkerToolError("network_error", "failed to send daemon tool call", true), input.socket);
137
+ const error = new WorkerToolError("network_error", "failed to send daemon tool call", true);
138
+ await input.resumption.calls.complete(callId, input.connectionId, streamTerminalMessage(input.requestId, { ok: false, error }));
139
+ input.observability.callFinished(input.tool, error.code);
134
140
  await input.onSendFailure();
135
141
  }
136
142
  }
@@ -144,28 +150,7 @@ export async function persistImmediateStreamOutcome(input: ImmediateOutcomeInput
144
150
  }
145
151
  }
146
152
 
147
- function createStreamSettlement(input: {
148
- resumption: McpResumptionStore;
149
- observability: WorkerObservability;
150
- streamId: string;
151
- requestId: string | number;
152
- tool: string;
153
- transformResult?: (value: unknown) => unknown;
154
- }): (outcome: PendingCallOutcome) => Promise<void> {
155
- return async (outcome) => {
156
- const normalized = normalizeOutcome(outcome, input.transformResult);
157
- const code = normalized.ok ? "" : publicWorkerToolError(normalized.error).code;
158
- const message = streamTerminalMessage(input.requestId, normalized);
159
- try {
160
- await input.resumption.complete(input.streamId, message);
161
- } catch (error) {
162
- input.observability.event("error", "mcp.stream.persist.failed", { error_class: workerErrorClass(error) });
163
- }
164
- input.observability.callFinished(input.tool, code);
165
- };
166
- }
167
-
168
- function streamTerminalMessage(
153
+ export function streamTerminalMessage(
169
154
  requestId: string | number,
170
155
  outcome: PendingCallOutcome,
171
156
  transformResult?: (value: unknown) => unknown,
@@ -179,7 +164,10 @@ function streamTerminalMessage(
179
164
  return message;
180
165
  }
181
166
 
182
- function normalizeOutcome(outcome: PendingCallOutcome, transformResult?: (value: unknown) => unknown): PendingCallOutcome {
167
+ export function normalizeOutcome(
168
+ outcome: PendingCallOutcome,
169
+ transformResult?: (value: unknown) => unknown,
170
+ ): PendingCallOutcome {
183
171
  if (!outcome.ok || !transformResult) return outcome;
184
172
  try { return { ok: true, value: transformResult(outcome.value) }; }
185
173
  catch (error) { return { ok: false, error: error instanceof Error ? error : new Error("stream result transformation failed") }; }
@@ -0,0 +1,15 @@
1
+ export class PendingAdmissionGate {
2
+ private tail: Promise<void> = Promise.resolve();
3
+
4
+ async run<T>(operation: () => T | Promise<T>): Promise<T> {
5
+ let release: () => void = () => {};
6
+ const predecessor = this.tail;
7
+ this.tail = new Promise<void>((resolve) => { release = resolve; });
8
+ await predecessor;
9
+ try {
10
+ return await operation();
11
+ } finally {
12
+ release();
13
+ }
14
+ }
15
+ }
@@ -2,9 +2,10 @@ export type PendingCallOutcome =
2
2
  | { ok: true; value: unknown }
3
3
  | { ok: false; error: Error };
4
4
 
5
- export type PendingCallSettlement =
6
- | { kind: "promise"; resolve: (value: unknown) => void; reject: (error: Error) => void }
7
- | { kind: "event"; settle: (outcome: PendingCallOutcome) => void | Promise<void> };
5
+ export type PendingCallSettlement = {
6
+ resolve: (value: unknown) => void;
7
+ reject: (error: Error) => void;
8
+ };
8
9
 
9
10
  export interface PendingCallRecord {
10
11
  id: string;
@@ -36,7 +37,3 @@ export interface RegisterPendingCall {
36
37
  signal?: AbortSignal;
37
38
  onAbort?: (record: PendingCallRecord) => Error;
38
39
  }
39
-
40
- export interface RegisterEventPendingCall extends RegisterPendingCall {
41
- settle: (outcome: PendingCallOutcome) => void | Promise<void>;
42
- }
@@ -1,4 +1,4 @@
1
- import type { PendingCallOutcome, PendingCallRecord, PendingCallSettlement, RegisterEventPendingCall, RegisterPendingCall } from "./pending-call-contract.ts";
1
+ import type { PendingCallOutcome, PendingCallRecord, PendingCallSettlement, RegisterPendingCall } from "./pending-call-contract.ts";
2
2
  import { PendingCallDeadlines, type PendingCallDeadlineOptions } from "./pending-call-deadlines.ts";
3
3
 
4
4
  export class PendingCallRegistrationError extends Error {
@@ -41,15 +41,10 @@ export class PendingCallRegistry {
41
41
  let resolveResult!: (value: unknown) => void;
42
42
  let rejectResult!: (error: Error) => void;
43
43
  const result = new Promise<unknown>((resolve, reject) => { resolveResult = resolve; rejectResult = reject; });
44
- this.add(input, { kind: "promise", resolve: resolveResult, reject: rejectResult });
44
+ this.add(input, { resolve: resolveResult, reject: rejectResult });
45
45
  return result;
46
46
  }
47
47
 
48
- registerEvent(input: RegisterEventPendingCall): void {
49
- this.assertCanRegister(input);
50
- this.add(input, { kind: "event", settle: input.settle });
51
- }
52
-
53
48
  resolve(id: string, socket: WebSocket, value: unknown): Promise<boolean> {
54
49
  const record = this.byId.get(id);
55
50
  return !record || record.socket !== socket ? Promise.resolve(false) : this.finish(id, { ok: true, value });
@@ -101,7 +96,7 @@ export class PendingCallRegistry {
101
96
 
102
97
  snapshot(): { active: number; detached: number; request_keys: number; maximum: number; oldest_ms: number; by_tool: Record<string, number> } {
103
98
  const now = this.deadlines.now();
104
- const byTool: Record<string, number> = {};
99
+ const byTool = Object.create(null) as Record<string, number>;
105
100
  let detached = 0;
106
101
  let oldestMs = 0;
107
102
  for (const record of this.byId.values()) {
@@ -161,8 +156,7 @@ export class PendingCallRegistry {
161
156
  private async finish(id: string, outcome: PendingCallOutcome): Promise<boolean> {
162
157
  const record = this.take(id);
163
158
  if (!record) return false;
164
- if (record.settlement.kind === "promise") outcome.ok ? record.settlement.resolve(outcome.value) : record.settlement.reject(outcome.error);
165
- else await record.settlement.settle(outcome);
159
+ outcome.ok ? record.settlement.resolve(outcome.value) : record.settlement.reject(outcome.error);
166
160
  return true;
167
161
  }
168
162
 
@@ -0,0 +1,35 @@
1
+ export interface AlarmStorage {
2
+ getAlarm(): Promise<number | null>;
3
+ setAlarm(scheduledTime: number | Date): Promise<void>;
4
+ deleteAlarm(): Promise<void>;
5
+ }
6
+
7
+ export async function writeEarliestRuntimeAlarm(input: {
8
+ storage: AlarmStorage;
9
+ nextDeadline: number;
10
+ now: number;
11
+ onError: (error: unknown) => void;
12
+ onMutation?: (action: "set" | "delete" | "noop") => void;
13
+ }): Promise<void> {
14
+ try {
15
+ const current = await input.storage.getAlarm();
16
+ if (!Number.isFinite(input.nextDeadline)) {
17
+ if (current === null) {
18
+ input.onMutation?.("noop");
19
+ return;
20
+ }
21
+ await input.storage.deleteAlarm();
22
+ input.onMutation?.("delete");
23
+ return;
24
+ }
25
+ const target = Math.max(input.now, input.nextDeadline);
26
+ if (current !== null && current > input.now && current <= target) {
27
+ input.onMutation?.("noop");
28
+ return;
29
+ }
30
+ await input.storage.setAlarm(target);
31
+ input.onMutation?.("set");
32
+ } catch (error) {
33
+ input.onError(error);
34
+ }
35
+ }
@@ -4,14 +4,10 @@ import {
4
4
  daemonReadyDeadlineMs,
5
5
  } from "./daemon-liveness.ts";
6
6
  import type { DaemonSocketRegistry } from "./daemon-sockets.ts";
7
+ import type { McpPendingCallStore, PendingStreamCallView } from "./mcp-pending-call-store.ts";
7
8
  import type { PendingCallRegistry } from "./pending-calls.ts";
8
9
  import { closeWebSocketQuietly, sendWebSocketQuietly } from "./websocket-protocol.ts";
9
-
10
- interface AlarmStorage {
11
- getAlarm(): Promise<number | null>;
12
- setAlarm(scheduledTime: number | Date): Promise<void>;
13
- deleteAlarm(): Promise<void>;
14
- }
10
+ import { writeEarliestRuntimeAlarm, type AlarmStorage } from "./runtime-alarm-storage.ts";
15
11
 
16
12
  type InvalidateDaemonSocket = (
17
13
  socket: WebSocket,
@@ -23,6 +19,8 @@ type InvalidateDaemonSocket = (
23
19
  interface RuntimeAlarmContext {
24
20
  storage: AlarmStorage;
25
21
  pending: PendingCallRegistry;
22
+ durableCalls?: Pick<McpPendingCallStore, "due" | "nextDeadlineDelayMs">;
23
+ expireDurableCall?: (call: PendingStreamCallView) => Promise<void>;
26
24
  daemonRegistry: DaemonSocketRegistry;
27
25
  invalidateDaemonSocket: InvalidateDaemonSocket;
28
26
  onScheduleError: (error: unknown) => void;
@@ -31,7 +29,10 @@ interface RuntimeAlarmContext {
31
29
 
32
30
  export async function processRuntimeAlarm(context: RuntimeAlarmContext, now = Date.now()): Promise<void> {
33
31
  await context.pending.expireDue();
34
- let nextDeadline = pendingAlarmDeadline(context.pending, now);
32
+ if (context.durableCalls && context.expireDurableCall) {
33
+ for (const call of await context.durableCalls.due(now)) await context.expireDurableCall(call);
34
+ }
35
+ let nextDeadline = await pendingAlarmDeadline(context, now);
35
36
  for (const socket of context.daemonRegistry.candidateSockets()) {
36
37
  const attachment = context.daemonRegistry.attachment(socket);
37
38
  const connectedAt = Date.parse(attachment?.connectedAt ?? "");
@@ -67,11 +68,17 @@ export async function processRuntimeAlarm(context: RuntimeAlarmContext, now = Da
67
68
  }
68
69
  nextDeadline = Math.min(nextDeadline, deadline);
69
70
  }
70
- await writeRuntimeAlarm(context, nextDeadline, now);
71
+ await writeEarliestRuntimeAlarm({
72
+ storage: context.storage,
73
+ nextDeadline,
74
+ now,
75
+ onError: context.onScheduleError,
76
+ onMutation: context.onAlarmMutation,
77
+ });
71
78
  }
72
79
 
73
80
  export async function scheduleRuntimeAlarm(context: RuntimeAlarmContext, now = Date.now()): Promise<void> {
74
- let nextDeadline = pendingAlarmDeadline(context.pending, now);
81
+ let nextDeadline = await pendingAlarmDeadline(context, now);
75
82
  for (const socket of context.daemonRegistry.candidateSockets()) {
76
83
  const connectedAt = Date.parse(context.daemonRegistry.attachment(socket)?.connectedAt ?? "");
77
84
  if (!Number.isFinite(connectedAt)) {
@@ -103,37 +110,20 @@ export async function scheduleRuntimeAlarm(context: RuntimeAlarmContext, now = D
103
110
  }
104
111
  nextDeadline = Math.min(nextDeadline, deadline);
105
112
  }
106
- await writeRuntimeAlarm(context, nextDeadline, now);
113
+ await writeEarliestRuntimeAlarm({
114
+ storage: context.storage,
115
+ nextDeadline,
116
+ now,
117
+ onError: context.onScheduleError,
118
+ onMutation: context.onAlarmMutation,
119
+ });
107
120
  }
108
121
 
109
- function pendingAlarmDeadline(pending: PendingCallRegistry, now: number): number {
110
- const delay = pending.nextDeadlineDelayMs();
122
+ async function pendingAlarmDeadline(context: RuntimeAlarmContext, now: number): Promise<number> {
123
+ const transientDelay = context.pending.nextDeadlineDelayMs();
124
+ const durableDelay = context.durableCalls
125
+ ? await context.durableCalls.nextDeadlineDelayMs()
126
+ : Number.POSITIVE_INFINITY;
127
+ const delay = Math.min(transientDelay, durableDelay);
111
128
  return Number.isFinite(delay) ? now + Math.max(1, Math.ceil(delay)) : Number.POSITIVE_INFINITY;
112
129
  }
113
-
114
- async function writeRuntimeAlarm(context: RuntimeAlarmContext, nextDeadline: number, now: number): Promise<void> {
115
- try {
116
- const current = await context.storage.getAlarm();
117
- if (!Number.isFinite(nextDeadline)) {
118
- if (current === null) {
119
- context.onAlarmMutation?.("noop");
120
- return;
121
- }
122
- await context.storage.deleteAlarm();
123
- context.onAlarmMutation?.("delete");
124
- return;
125
- }
126
-
127
- const target = Math.max(now, nextDeadline);
128
- // An earlier future alarm is safe: it will re-evaluate current liveness and
129
- // pending deadlines. Do not rewrite it on every heartbeat merely to move it later.
130
- if (current !== null && current > now && current <= target) {
131
- context.onAlarmMutation?.("noop");
132
- return;
133
- }
134
- await context.storage.setAlarm(target);
135
- context.onAlarmMutation?.("set");
136
- } catch (error) {
137
- context.onScheduleError(error);
138
- }
139
- }
@@ -21,6 +21,10 @@ export function sendWebSocketQuietly(ws: WebSocket, value: unknown): void {
21
21
  trySendWebSocket(ws, value);
22
22
  }
23
23
 
24
+ export function daemonErrorCloseCode(errorCode: string): number {
25
+ return errorCode === "daemon_transport_error" || errorCode === "daemon_liveness_timeout" ? 1012 : 1008;
26
+ }
27
+
24
28
  export function closeWebSocketQuietly(ws: WebSocket, code?: number, reason?: string): void {
25
29
  try {
26
30
  ws.close(code, reason);
@@ -0,0 +1,50 @@
1
+ import {
2
+ proxyMcpEventStream,
3
+ sanitizeBridgeRequest,
4
+ } from "./mcp-stream-proxy.ts";
5
+ import { respondWithoutDurableObject } from "./worker-static-routes.ts";
6
+ import { createThrottledEdgeLogger } from "./worker-edge-log.ts";
7
+ import {
8
+ admitStatefulRequest,
9
+ durableObjectQuotaResponse,
10
+ isDurableObjectQuotaError,
11
+ outerWorkerErrorClass,
12
+ workerGatewayErrorResponse,
13
+ } from "./worker-edge-guard.ts";
14
+
15
+ type OuterBridgeStub = { fetch(request: Request): Promise<Response> };
16
+ type OuterBridgeNamespace = { getByName(name: string): OuterBridgeStub };
17
+
18
+ export interface OuterWorkerEnv {
19
+ BRIDGE: OuterBridgeNamespace;
20
+ STATEFUL_RATE_LIMITER: RateLimit;
21
+ MBM_ALLOWED_ORIGINS?: string;
22
+ }
23
+
24
+ const logOuterFetchFailure = createThrottledEdgeLogger();
25
+
26
+ export async function handleOuterWorkerFetch(
27
+ request: Request,
28
+ env: OuterWorkerEnv,
29
+ ctx: ExecutionContext,
30
+ identity: { server: string; version: string },
31
+ ): Promise<Response> {
32
+ const extraOrigins = env.MBM_ALLOWED_ORIGINS ?? "";
33
+ const staticResponse = respondWithoutDurableObject(request, identity, extraOrigins);
34
+ if (staticResponse) return staticResponse;
35
+
36
+ try {
37
+ const limited = await admitStatefulRequest(request, env.STATEFUL_RATE_LIMITER, extraOrigins);
38
+ if (limited) return limited;
39
+ const stub = env.BRIDGE.getByName("default");
40
+ const streamed = await proxyMcpEventStream({ request, bridge: stub, extraOrigins, ctx });
41
+ return streamed ?? stub.fetch(sanitizeBridgeRequest(request));
42
+ } catch (error) {
43
+ if (isDurableObjectQuotaError(error)) return durableObjectQuotaResponse(request, extraOrigins);
44
+ logOuterFetchFailure("error", "outer.fetch.failed", {
45
+ path: new URL(request.url).pathname,
46
+ error_class: outerWorkerErrorClass(error),
47
+ });
48
+ return workerGatewayErrorResponse(request, extraOrigins);
49
+ }
50
+ }