@cyanmycelium/mcp-broker-provider 0.3.0 → 0.4.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/dist/index.d.ts CHANGED
@@ -90,6 +90,7 @@ interface IDeclaredResource {
90
90
  }
91
91
  /** `broker/authorization/declare` parameters. Describes; grants nothing. */
92
92
  interface IAuthorizationDeclaration {
93
+ readonly budgetUnits?: readonly string[];
93
94
  readonly version: string;
94
95
  readonly domain: string;
95
96
  readonly namespace: {
@@ -192,6 +193,31 @@ interface IBrokerClientOptions {
192
193
  * on {@link DirectTransport} and {@link MultiplexTransport}; the transport
193
194
  * routes the broker's answers here before anything reaches the MCP server.
194
195
  */
196
+ interface IBudgetReservationQuery extends Pick<IAuthorizationCheck, "capability" | "resource" | "resourcePath"> {
197
+ readonly principal: {
198
+ readonly type: "caller-ref";
199
+ readonly ref: string;
200
+ };
201
+ readonly unit: string;
202
+ readonly quantity: number;
203
+ readonly idempotencyKey: string;
204
+ }
205
+ interface IBudgetReservation {
206
+ readonly reservationId: string;
207
+ readonly expiresAt: number;
208
+ readonly quantity: number;
209
+ readonly decisionId: string;
210
+ readonly replayed: boolean;
211
+ /** `"allow-with-constraints"` when the resource has declared engineering limits (broker 1.6.1 and later). */
212
+ readonly effect?: "allow" | "allow-with-constraints";
213
+ /** The constraints the native work must respect, e.g. `{ minValue, maxValue }`. Apply them before acting. */
214
+ readonly obligations?: IAuthorizationDecision["obligations"];
215
+ }
216
+ interface IBudgetSettlement {
217
+ readonly reservationId: string;
218
+ readonly used: number;
219
+ readonly result: "success" | "failure" | "refused";
220
+ }
195
221
  declare class BrokerClient {
196
222
  private readonly _write;
197
223
  private readonly _writeTelemetry;
@@ -206,6 +232,12 @@ declare class BrokerClient {
206
232
  * needs a decision.
207
233
  */
208
234
  declare(declaration: IAuthorizationDeclaration): Promise<IDeclarationAccepted>;
235
+ reserveBudget(query: IBudgetReservationQuery): Promise<IBudgetReservation>;
236
+ settleBudget(report: IBudgetSettlement): Promise<{
237
+ readonly settled: true;
238
+ }>;
239
+ /** Executes only a fresh reservation. A retry must never repeat native work. */
240
+ withBudget<T>(query: IBudgetReservationQuery, work: (grant: IBudgetReservation) => Promise<T>): Promise<T>;
209
241
  /** Asks for one decision per check. */
210
242
  authorize(query: IAuthorizationQuery): Promise<IAuthorizationAnswer>;
211
243
  /**
@@ -505,4 +537,4 @@ declare class MultiplexTransport implements IMessageTransport {
505
537
  close(): void;
506
538
  }
507
539
 
508
- export { AUDIT_RESULT_NOTIFICATION_METHOD, BrokerClient, BrokerRequestError, CALLER_META_KEY, DirectTransport, type IAuditResult, type IAuthorizationAnswer, type IAuthorizationCheck, type IAuthorizationDecision, type IAuthorizationDeclaration, type IAuthorizationObligations, type IAuthorizationQuery, type IBrokerClientOptions, type ICallerReference, type IDeclarationAccepted, type IDeclaredResource, type IDirectTransportOptions, type IMultiplexTransportOptions, type IResourceLimits, type ITelemetryEvent, type ITelemetrySpan, type ITraceParent, MultiplexTransport, TELEMETRY_NOTIFICATION_METHOD, TRACEPARENT_META_KEY, type TelemetryAttributeValue, callerReferenceOf, childTraceparent, formatTraceparent, parseTraceparent, traceparentOf, withTraceparent };
540
+ export { AUDIT_RESULT_NOTIFICATION_METHOD, BrokerClient, BrokerRequestError, CALLER_META_KEY, DirectTransport, type IAuditResult, type IAuthorizationAnswer, type IAuthorizationCheck, type IAuthorizationDecision, type IAuthorizationDeclaration, type IAuthorizationObligations, type IAuthorizationQuery, type IBrokerClientOptions, type IBudgetReservation, type IBudgetReservationQuery, type IBudgetSettlement, type ICallerReference, type IDeclarationAccepted, type IDeclaredResource, type IDirectTransportOptions, type IMultiplexTransportOptions, type IResourceLimits, type ITelemetryEvent, type ITelemetrySpan, type ITraceParent, MultiplexTransport, TELEMETRY_NOTIFICATION_METHOD, TRACEPARENT_META_KEY, type TelemetryAttributeValue, callerReferenceOf, childTraceparent, formatTraceparent, parseTraceparent, traceparentOf, withTraceparent };
package/dist/index.js CHANGED
@@ -133,6 +133,29 @@ var BrokerClient = class {
133
133
  declare(declaration) {
134
134
  return this._request("broker/authorization/declare", declaration);
135
135
  }
136
+ reserveBudget(query) {
137
+ return this._request("broker/budget/reserve", query);
138
+ }
139
+ settleBudget(report) {
140
+ return this._request("broker/budget/settle", report);
141
+ }
142
+ /** Executes only a fresh reservation. A retry must never repeat native work. */
143
+ async withBudget(query, work) {
144
+ const grant = await this.reserveBudget(query);
145
+ if (grant.replayed || Date.now() >= grant.expiresAt) throw new Error("Budget reservation was replayed or expired; native work was not started");
146
+ let value;
147
+ try {
148
+ value = await work(grant);
149
+ } catch (error) {
150
+ try {
151
+ await this.settleBudget({ reservationId: grant.reservationId, used: grant.quantity, result: "failure" });
152
+ } catch {
153
+ }
154
+ throw error;
155
+ }
156
+ await this.settleBudget({ reservationId: grant.reservationId, used: grant.quantity, result: "success" });
157
+ return value;
158
+ }
136
159
  /** Asks for one decision per check. */
137
160
  authorize(query) {
138
161
  return this._request("broker/authorize", query);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protocol/envelope.ts","../src/broker.client.ts","../src/transport.support.ts","../src/direct.transport.ts","../src/multiplex.transport.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The CyanMycelium tunnel envelope protocol.\n *\n * A multiplexed tunnel socket carries traffic for several providers at once, so\n * every JSON-RPC message is wrapped with the name of the provider slot it\n * belongs to. Both ends of the tunnel encode and decode with the helpers here:\n * the client transports that publish a provider, and the broker that routes\n * between providers and MCP clients.\n *\n * This module is the single definition of that wire format. It is deliberately\n * dependency-free and isomorphic, so the browser side and the Node broker share\n * exactly one implementation rather than two that drift apart.\n *\n * Wire format:\n * ```json\n * { \"provider\": \"scene-1\", \"payload\": { \"jsonrpc\": \"2.0\", \"id\": 1, \"result\": {} } }\n * ```\n */\n\n/** One framed message on a multiplexed tunnel socket. */\nexport interface TunnelEnvelope {\n /** Name of the provider slot this message belongs to. */\n provider: string;\n\n /** The JSON-RPC message itself, already parsed. */\n payload: unknown;\n}\n\n/**\n * Notification a client sends to claim a provider slot as soon as the tunnel\n * opens, before any MCP client shows up.\n *\n * Without it the broker only discovers a provider name on its first real\n * message, so an MCP client connecting in between is told the provider is not\n * connected. It is a plain JSON-RPC notification, which any peer that does not\n * recognize it ignores.\n */\nexport const TUNNEL_REGISTER_METHOD = \"notifications/register\";\n\n/** Options carried by the registration notification, as its `params`. */\nexport interface ITunnelRegisterOptions {\n /**\n * Join the broker's `_all` aggregate slot in addition to the provider's own\n * slot, so a single MCP client sees every opted-in provider's tools and\n * prompts through one connection.\n *\n * Opt-in on purpose: `_all` is a confidentiality boundary, and a provider\n * that never asks for it stays reachable only on its own slot.\n */\n aggregate?: boolean;\n}\n\n/**\n * Builds the registration notification as a plain JSON-RPC frame, for the\n * slot-scoped path `/provider/<name>`, which carries no envelope.\n *\n * The slot name is not in the frame: on that path the broker takes it from the\n * URL at connect time, before any frame exists.\n *\n * @param options When `aggregate` is set, it is carried as `params.aggregate`.\n * Omitted entirely otherwise, which keeps the frame identical to\n * the parameterless form older brokers already accept.\n */\nexport function encodeRegisterFrame(options?: ITunnelRegisterOptions): string {\n return JSON.stringify(registerMessage(options));\n}\n\n/** The registration notification body, shared by both encoders. */\nfunction registerMessage(options?: ITunnelRegisterOptions): Record<string, unknown> {\n const message: Record<string, unknown> = { jsonrpc: \"2.0\", method: TUNNEL_REGISTER_METHOD };\n if (options?.aggregate !== undefined) {\n message.params = { aggregate: options.aggregate };\n }\n return message;\n}\n\n/** JSON-RPC error codes the broker returns on the tunnel itself. */\nexport const TunnelErrorCodes = {\n /** The slot is taken by another upstream, or the provider is not connected. */\n ProviderUnavailable: -32000,\n\n /** The provider's credentials do not allow publishing on this slot. */\n RegistrationForbidden: -32001,\n} as const;\n\nexport type TunnelErrorCode = (typeof TunnelErrorCodes)[keyof typeof TunnelErrorCodes];\n\n/** A JSON-RPC error as carried inside an envelope payload. */\nexport interface TunnelError {\n code: number;\n message: string;\n data?: unknown;\n}\n\n/**\n * Wraps an already-serialized JSON-RPC frame for `provider`.\n *\n * @throws SyntaxError when `frame` is not valid JSON. Callers hold a frame they\n * just serialized, so a failure here is a bug rather than bad input.\n */\nexport function encodeEnvelope(provider: string, frame: string): string {\n return encodeEnvelopeMessage(provider, JSON.parse(frame));\n}\n\n/** Wraps an already-parsed JSON-RPC message for `provider`. */\nexport function encodeEnvelopeMessage(provider: string, payload: unknown): string {\n const envelope: TunnelEnvelope = { provider, payload };\n return JSON.stringify(envelope);\n}\n\n/**\n * Parses a raw tunnel frame.\n *\n * Returns `undefined` for anything malformed rather than throwing: a tunnel\n * socket is a public surface, and a peer sending garbage must not take the\n * receiver down. Both ends drop such frames silently.\n */\nexport function decodeEnvelope(raw: string): TunnelEnvelope | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return undefined;\n\n const { provider, payload } = parsed as Partial<TunnelEnvelope>;\n if (typeof provider !== \"string\" || provider.length === 0 || payload === undefined) return undefined;\n\n return { provider, payload };\n}\n\n/** Serializes an envelope's payload back into a plain JSON-RPC frame. */\nexport function envelopeFrame(envelope: TunnelEnvelope): string {\n return JSON.stringify(envelope.payload);\n}\n\n/**\n * Builds the registration notification that claims `provider`, wrapped for the\n * multiplexed tunnel.\n *\n * @param options When `aggregate` is set, it is carried as `params.aggregate`.\n * Omitted entirely otherwise: the parameterless frame is the one\n * brokers have always received here, and it stays byte-identical.\n */\nexport function encodeRegisterEnvelope(provider: string, options?: ITunnelRegisterOptions): string {\n return encodeEnvelopeMessage(provider, registerMessage(options));\n}\n\n/**\n * Builds the error envelope the broker returns when it refuses a slot.\n *\n * The id is `null` because the refusal answers no particular request: it\n * reacts to the registration itself.\n */\nexport function encodeErrorEnvelope(provider: string, code: TunnelErrorCode | number, message: string): string {\n return encodeEnvelopeMessage(provider, { jsonrpc: \"2.0\", id: null, error: { code, message } });\n}\n\n/**\n * Reads the JSON-RPC error out of an envelope payload, when there is one.\n *\n * Lets the client side notice a refused registration instead of handing an\n * `id: null` error frame to an MCP server, which would classify it as an\n * unknown notification and drop it without a word.\n */\nexport function tunnelErrorOf(payload: unknown): TunnelError | undefined {\n if (typeof payload !== \"object\" || payload === null) return undefined;\n\n const { error } = payload as { error?: unknown };\n if (typeof error !== \"object\" || error === null) return undefined;\n\n const { code, message } = error as Partial<TunnelError>;\n if (typeof code !== \"number\" || typeof message !== \"string\") return undefined;\n\n return error as TunnelError;\n}\n","/**\n * Talks to the broker itself, over the provider's own socket: declaring an\n * authorization domain, and asking for decisions.\n *\n * Requires broker 1.5.0 or later. An older broker does not know these methods:\n * from 1.4.1 it refuses them at once with `-32601`; 1.4.0 and earlier drop\n * them and never answer, which is why {@link IBrokerClientOptions.requestTimeoutMs}\n * exists, off by default.\n */\n\n/** The `params._meta` key under which the broker passes the caller reference with each request. */\nexport const CALLER_META_KEY = \"io.cyanmycelium/caller\";\n\n/** W3C Trace Context carrier used by MCP requests. */\nexport const TRACEPARENT_META_KEY = \"traceparent\";\n\n/** Provider-to-broker telemetry notification. */\nexport const TELEMETRY_NOTIFICATION_METHOD = \"broker/telemetry\";\n\n/** Provider-to-broker notification reporting what happened after a decision. */\nexport const AUDIT_RESULT_NOTIFICATION_METHOD = \"broker/audit/result\";\n\nconst TRACEPARENT = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;\nconst TRACE_ID = /^[0-9a-f]{32}$/;\nconst SPAN_ID = /^[0-9a-f]{16}$/;\nconst UNIX_NANO = /^(0|[1-9][0-9]{0,19})$/;\n\nexport interface ITraceParent {\n readonly version: \"00\";\n readonly traceId: string;\n readonly parentId: string;\n readonly traceFlags: string;\n}\n\nexport type TelemetryAttributeValue = string | number | boolean;\n\nexport interface ITelemetryEvent {\n readonly name: string;\n readonly timeUnixNano: string;\n readonly attributes?: Readonly<Record<string, TelemetryAttributeValue>>;\n}\n\nexport interface ITelemetrySpan {\n readonly traceId: string;\n readonly spanId: string;\n readonly parentSpanId?: string;\n readonly name: string;\n readonly kind?: number;\n readonly startTimeUnixNano: string;\n readonly endTimeUnixNano: string;\n readonly attributes?: Readonly<Record<string, TelemetryAttributeValue>>;\n readonly events?: readonly ITelemetryEvent[];\n readonly status?: { readonly code: number; readonly message?: string };\n}\n\n/** Parses the W3C version 00 traceparent representation used in MCP metadata. */\nexport function parseTraceparent(value: unknown): ITraceParent | undefined {\n if (typeof value !== \"string\") return undefined;\n const match = TRACEPARENT.exec(value);\n if (!match || /^0+$/.test(match[1]!) || /^0+$/.test(match[2]!)) return undefined;\n return { version: \"00\", traceId: match[1]!, parentId: match[2]!, traceFlags: match[3]! };\n}\n\nexport function formatTraceparent(context: ITraceParent): string {\n return `${context.version}-${context.traceId}-${context.parentId}-${context.traceFlags}`;\n}\n\n/** Reads `params._meta.traceparent`, returning `undefined` when it is malformed. */\nexport function traceparentOf(meta: Readonly<Record<string, unknown>> | undefined): ITraceParent | undefined {\n return parseTraceparent(meta?.[TRACEPARENT_META_KEY]);\n}\n\n/** Returns a metadata copy carrying the supplied validated trace context. */\nexport function withTraceparent(meta: Readonly<Record<string, unknown>> | undefined, context: ITraceParent): Readonly<Record<string, unknown>> {\n const encoded = formatTraceparent(context);\n if (!parseTraceparent(encoded)) throw new TypeError(\"traceparent must be a valid W3C version 00 context\");\n return { ...(meta ?? {}), [TRACEPARENT_META_KEY]: encoded };\n}\n\n/** Continues a trace using the caller's span as the new W3C parent id. */\nexport function childTraceparent(parent: ITraceParent, spanId: string): ITraceParent {\n if (!SPAN_ID.test(spanId) || /^0+$/.test(spanId)) throw new TypeError(\"spanId must be 16 lowercase hexadecimal characters and not all zero\");\n return { ...parent, parentId: spanId };\n}\n\nfunction validAttributes(attributes: Readonly<Record<string, TelemetryAttributeValue>> | undefined): boolean {\n if (attributes === undefined) return true;\n const entries = Object.entries(attributes);\n return (\n entries.length <= 64 &&\n entries.every(\n ([key, value]) =>\n key.length > 0 &&\n key.length <= 128 &&\n (typeof value === \"boolean\" || (typeof value === \"string\" && value.length <= 4096) || (typeof value === \"number\" && Number.isFinite(value)))\n )\n );\n}\n\nfunction validSpan(span: ITelemetrySpan): boolean {\n return (\n TRACE_ID.test(span.traceId) &&\n !/^0+$/.test(span.traceId) &&\n SPAN_ID.test(span.spanId) &&\n !/^0+$/.test(span.spanId) &&\n (span.parentSpanId === undefined || (SPAN_ID.test(span.parentSpanId) && !/^0+$/.test(span.parentSpanId))) &&\n span.name.length > 0 &&\n span.name.length <= 256 &&\n UNIX_NANO.test(span.startTimeUnixNano) &&\n UNIX_NANO.test(span.endTimeUnixNano) &&\n (span.kind === undefined || (Number.isInteger(span.kind) && span.kind >= 0 && span.kind <= 5)) &&\n validAttributes(span.attributes) &&\n (span.events === undefined ||\n (span.events.length <= 32 &&\n span.events.every((event) => event.name.length > 0 && event.name.length <= 256 && UNIX_NANO.test(event.timeUnixNano) && validAttributes(event.attributes)))) &&\n (span.status === undefined ||\n (Number.isInteger(span.status.code) && span.status.code >= 0 && span.status.code <= 2 && (span.status.message === undefined || span.status.message.length <= 1024)))\n );\n}\n\n/** What the broker hands a declaring provider with each request, under {@link CALLER_META_KEY}. */\nexport interface ICallerReference {\n /** Opaque. Valid on this slot, while the request it came with is pending. */\n readonly ref: string;\n /** Ties the decisions and the eventual report to the client request. */\n readonly correlationId: string;\n /** W3C trace id carried by the MCP request. */\n readonly traceId?: string;\n}\n\n/**\n * Reads the caller reference out of a request's `params._meta`, or\n * `undefined` when there is none (the broker only adds it once a declaration\n * was accepted). With mcp-core 1.4.0, pass `request?.meta` from the context\n * an adapter receives.\n */\nexport function callerReferenceOf(meta: Readonly<Record<string, unknown>> | undefined): ICallerReference | undefined {\n const value = meta?.[CALLER_META_KEY];\n if (typeof value !== \"object\" || value === null) return undefined;\n const { ref, correlationId, traceId } = value as { ref?: unknown; correlationId?: unknown; traceId?: unknown };\n return typeof ref === \"string\" && typeof correlationId === \"string\" ? { ref, correlationId, ...(typeof traceId === \"string\" ? { traceId } : {}) } : undefined;\n}\n\n/**\n * Engineering limits of one resource. They hold for every caller, and the\n * broker returns them with each allow on that resource as\n * `obligations.constraints`, for the provider to apply.\n */\nexport interface IResourceLimits {\n readonly minValue?: number;\n readonly maxValue?: number;\n readonly allowedValues?: readonly (string | number | boolean | null)[];\n readonly destinations?: readonly string[];\n}\n\n/** One resource of a declaration: the provider's own identifier, and the path the broker evaluates. */\nexport interface IDeclaredResource {\n readonly resource: string;\n readonly resourcePath: string;\n readonly effect?: string;\n readonly limits?: IResourceLimits;\n}\n\n/** `broker/authorization/declare` parameters. Describes; grants nothing. */\nexport interface IAuthorizationDeclaration {\n readonly version: string;\n readonly domain: string;\n readonly namespace: { readonly resource: string };\n readonly capabilities: readonly string[];\n readonly resources?: readonly IDeclaredResource[];\n readonly protects?: readonly string[];\n /**\n * Declared capabilities whose allowed decisions this provider promises to\n * report with {@link BrokerClient.reportResult}. One not reported in time\n * shows up in `broker_diagnose`.\n */\n readonly resultsRequired?: readonly string[];\n}\n\nexport interface IDeclarationAccepted {\n readonly accepted: true;\n readonly version: string;\n readonly policyVersion: string;\n}\n\n/** One question: may (the caller) do `capability` on this resource? */\nexport interface IAuthorizationCheck {\n readonly capability: string;\n readonly resource: string;\n readonly resourcePath: string;\n readonly attributes?: Readonly<Record<string, unknown>>;\n}\n\n/** `broker/authorize` parameters. */\nexport interface IAuthorizationQuery {\n /** On whose behalf: the caller of a pending request, or the provider itself. Never an identity. */\n readonly principal: { readonly type: \"caller-ref\"; readonly ref: string } | { readonly type: \"provider\" };\n /** Only for `{ type: \"provider\" }`; a caller reference carries its own. */\n readonly correlationId?: string;\n /** Optional W3C trace id for provider-initiated work. */\n readonly traceId?: string;\n readonly checks: readonly IAuthorizationCheck[];\n}\n\n/** What an allow comes with. */\nexport interface IAuthorizationObligations {\n /** The declared limits of the resource. Apply them right before executing. */\n readonly constraints?: IResourceLimits;\n}\n\nexport interface IAuthorizationDecision {\n readonly decisionId: string;\n /** `allow-with-constraints`: allowed, within `obligations`. */\n readonly effect: \"allow\" | \"deny\" | \"allow-with-constraints\";\n /**\n * `true` only for an unconditional `allow`. A provider that reads only\n * this field therefore refuses a constrained allow rather than ignoring\n * its constraints; read `effect` to apply them.\n */\n readonly allowed: boolean;\n readonly reason: string;\n readonly policies?: readonly string[];\n readonly obligations?: IAuthorizationObligations;\n}\n\n/** `broker/audit/result` parameters: the outcome of what a decision allowed or refused. */\nexport interface IAuditResult {\n /** The `decisionId` the broker returned. */\n readonly decisionId: string;\n readonly result: \"success\" | \"failure\" | \"refused\";\n /** The protocol's own status (`Good`, an exception code, ...). */\n readonly nativeStatus?: string;\n /** The provider's error code, when it failed or refused. */\n readonly errorCode?: string;\n}\n\nexport interface IAuthorizationAnswer {\n readonly policyVersion: string;\n /** One per check, in the same order. */\n readonly decisions: readonly IAuthorizationDecision[];\n}\n\n/** The broker refused a request, or never answered it. */\nexport class BrokerRequestError extends Error {\n constructor(\n message: string,\n /** JSON-RPC error code; `undefined` for a timeout or a closed socket. */\n public readonly code?: number,\n /** The error's `data`, e.g. `{ errors: [...] }` for a refused declaration. */\n public readonly data?: unknown\n ) {\n super(message);\n this.name = \"BrokerRequestError\";\n }\n}\n\nexport interface IBrokerClientOptions {\n /**\n * Rejects a request the broker did not answer within this many ms. Off by\n * default: a broker from 1.4.1 on answers every request at once, refusals\n * included, so waiting is never the normal path. Set it only to talk to an\n * older broker, which drops what it does not know.\n */\n readonly requestTimeoutMs?: number;\n}\n\ninterface IWaiting {\n readonly resolve: (result: unknown) => void;\n readonly reject: (error: BrokerRequestError) => void;\n readonly timer: ReturnType<typeof setTimeout> | null;\n}\n\n/** Ids of the provider's own requests; never confused with a client's, which the broker numbers `brk-N`. */\nconst ID_PREFIX = \"provider-broker-\";\n\n/**\n * The `broker/*` methods of one provider slot. Reached as `transport.broker`\n * on {@link DirectTransport} and {@link MultiplexTransport}; the transport\n * routes the broker's answers here before anything reaches the MCP server.\n */\nexport class BrokerClient {\n private readonly _write: (frame: string) => void;\n private readonly _writeTelemetry: (frame: string) => boolean;\n private readonly _timeoutMs: number;\n private readonly _waiting = new Map<string, IWaiting>();\n private _next = 1;\n\n constructor(write: (frame: string) => void, options: IBrokerClientOptions = {}, writeTelemetry?: (frame: string) => boolean) {\n this._write = write;\n this._writeTelemetry =\n writeTelemetry ??\n ((frame) => {\n write(frame);\n return true;\n });\n this._timeoutMs = Math.max(0, options.requestTimeoutMs ?? 0);\n }\n\n /**\n * Declares this provider's authorization domain. Resolves when the broker\n * accepted it; rejects with a {@link BrokerRequestError} whose `data.errors`\n * lists every problem otherwise. Until it resolves, serve nothing that\n * needs a decision.\n */\n declare(declaration: IAuthorizationDeclaration): Promise<IDeclarationAccepted> {\n return this._request(\"broker/authorization/declare\", declaration) as Promise<IDeclarationAccepted>;\n }\n\n /** Asks for one decision per check. */\n authorize(query: IAuthorizationQuery): Promise<IAuthorizationAnswer> {\n return this._request(\"broker/authorize\", query) as Promise<IAuthorizationAnswer>;\n }\n\n /**\n * Reports what happened after a decision, so the broker's audit shows the\n * outcome next to the decision. A notification: nothing comes back, and a\n * report the broker cannot match is counted on its side.\n */\n reportResult(report: IAuditResult): void {\n if (typeof report?.decisionId !== \"string\" || report.decisionId.length === 0) throw new TypeError(\"decisionId must be the non-empty id the broker returned\");\n if (report.result !== \"success\" && report.result !== \"failure\" && report.result !== \"refused\") throw new TypeError('result must be \"success\", \"failure\" or \"refused\"');\n const params: IAuditResult = {\n decisionId: report.decisionId,\n result: report.result,\n ...(report.nativeStatus !== undefined ? { nativeStatus: report.nativeStatus } : {}),\n ...(report.errorCode !== undefined ? { errorCode: report.errorCode } : {}),\n };\n this._write(JSON.stringify({ jsonrpc: \"2.0\", method: AUDIT_RESULT_NOTIFICATION_METHOD, params }));\n }\n\n /** Emits one complete provider span, or returns false when the link is down. */\n span(span: ITelemetrySpan): boolean {\n if (!validSpan(span)) throw new TypeError(\"span is not a valid broker telemetry span\");\n return this._writeTelemetry(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: TELEMETRY_NOTIFICATION_METHOD,\n params: { version: 1, signal: \"traces\", span },\n })\n );\n }\n\n /** Number of requests still waiting for the broker. */\n get pendingCount(): number {\n return this._waiting.size;\n }\n\n /**\n * Consumes a frame when it answers one of this client's requests. Returns\n * `true` when it did, and the frame must not reach the MCP server.\n * @internal Called by the transports.\n */\n handleIncoming(frame: string): boolean {\n if (this._waiting.size === 0 || !frame.includes(ID_PREFIX)) return false;\n let message: { id?: unknown; method?: unknown; result?: unknown; error?: { code?: unknown; message?: unknown; data?: unknown } };\n try {\n message = JSON.parse(frame) as typeof message;\n } catch {\n return false;\n }\n if (typeof message.id !== \"string\" || message.method !== undefined) return false;\n const waiting = this._waiting.get(message.id);\n if (!waiting) return false;\n this._waiting.delete(message.id);\n if (waiting.timer) clearTimeout(waiting.timer);\n if (message.error) {\n waiting.reject(\n new BrokerRequestError(String(message.error.message ?? \"broker error\"), typeof message.error.code === \"number\" ? message.error.code : undefined, message.error.data)\n );\n } else {\n waiting.resolve(message.result);\n }\n return true;\n }\n\n /**\n * Fails every waiting request: the socket they went out on is gone, and a\n * reconnected one will not carry their answers.\n * @internal Called by the transports.\n */\n rejectAll(reason: string): void {\n for (const [id, waiting] of this._waiting) {\n if (waiting.timer) clearTimeout(waiting.timer);\n waiting.reject(new BrokerRequestError(`${reason} (request ${id})`));\n }\n this._waiting.clear();\n }\n\n private _request(method: string, params: unknown): Promise<unknown> {\n const id = `${ID_PREFIX}${this._next++}`;\n return new Promise((resolve, reject) => {\n const timer =\n this._timeoutMs > 0\n ? setTimeout(() => {\n this._waiting.delete(id);\n reject(\n new BrokerRequestError(\n `The broker did not answer ${method} within ${this._timeoutMs}ms. A broker older than 1.4.1 drops methods it does not know; ${method} needs broker 1.5.0 or later.`\n )\n );\n }, this._timeoutMs)\n : null;\n this._waiting.set(id, { resolve, reject, timer });\n this._write(JSON.stringify({ jsonrpc: \"2.0\", id, method, params }));\n });\n }\n}\n","/**\n * Internal plumbing shared by the two tunnel transports.\n *\n * Three concerns live here, all of them about the same thing: a tunnel that\n * misbehaves must say so instead of going quiet.\n *\n * - {@link PendingFrames}, the bounded outbound queue that covers the window\n * between `connect()` and `open` (and, for the multiplexed socket, the whole\n * reconnect back-off). Without it every frame written in that window is\n * discarded with no error and no log, and the peer simply never answers.\n * - {@link ThrottledNotice}, so a mis-wired tunnel reports itself once in full\n * rather than once per frame forever.\n * - The URL guards, which catch the single most common wiring mistake: a\n * transport pointed at the endpoint the *other* transport speaks to.\n *\n * Nothing here is exported from the package root: it is internal to the\n * transports, which are the only things that can produce these situations.\n */\n\n// ---------------------------------------------------------------------------\n// Outbound frame queue\n// ---------------------------------------------------------------------------\n\n/**\n * How many outbound frames a transport holds while its socket is not open.\n *\n * Sized for a handshake, not for a backlog: an MCP server writes a handful of\n * frames before its transport reports open (an `initialize` result, a couple of\n * `list_changed` notifications), and anything beyond that is a symptom rather\n * than traffic worth keeping. A cap also matters because the multiplexed socket\n * queues across a reconnect back-off of up to 30 seconds, during which an\n * unbounded queue would grow without limit.\n */\nexport const PENDING_FRAME_LIMIT = 64;\n\n/**\n * A bounded FIFO of frames waiting for a socket to open.\n *\n * Overflow drops the *oldest* frame, because the newest one is the one still\n * worth answering, and says so on the console: a dropped request never gets a\n * response, so the caller would otherwise wait forever on a frame nothing sent.\n */\nexport class PendingFrames {\n private readonly _label: string;\n private readonly _limit: number;\n private _frames: string[] = [];\n\n /**\n * @param label Identifies the owner in log lines, e.g. `DirectTransport ws://host/provider/x`.\n * @param limit Maximum queued frames, defaults to {@link PENDING_FRAME_LIMIT}.\n */\n constructor(label: string, limit: number = PENDING_FRAME_LIMIT) {\n this._label = label;\n this._limit = Math.max(1, limit);\n }\n\n /** Number of frames currently waiting. */\n get size(): number {\n return this._frames.length;\n }\n\n /** Queues one frame, evicting the oldest with a warning when full. */\n push(frame: string): void {\n if (this._frames.length >= this._limit) {\n const dropped = this._frames.shift();\n console.warn(\n `[mcp-provider] ${this._label}: outbound queue full at ${this._limit} frames, dropping the oldest (${describeFrame(dropped)}). ` +\n `The socket is still connecting or reconnecting. Nothing resends a dropped frame, so a request lost here never gets a response.`\n );\n }\n this._frames.push(frame);\n }\n\n /** Hands back everything queued and empties the queue. */\n drain(): string[] {\n const frames = this._frames;\n this._frames = [];\n return frames;\n }\n\n /** Empties the queue, returning how many frames were discarded. */\n clear(): number {\n const discarded = this._frames.length;\n this._frames = [];\n return discarded;\n }\n}\n\n/**\n * Best-effort one-line description of a frame, for a log line about losing it.\n *\n * Reads through a tunnel envelope when there is one, so a multiplexed frame\n * reports the JSON-RPC method the caller recognizes rather than the wrapper.\n * Never throws: it is only ever called from an error path.\n */\nexport function describeFrame(frame: string | undefined): string {\n if (frame === undefined) return \"an empty frame\";\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(frame);\n } catch {\n return `${frame.length} bytes that are not JSON`;\n }\n\n let message = parsed;\n if (typeof message === \"object\" && message !== null && \"payload\" in message) {\n message = (message as { payload: unknown }).payload;\n }\n if (typeof message !== \"object\" || message === null) return `${frame.length} bytes`;\n\n const { method, id } = message as { method?: unknown; id?: unknown };\n const methodPart = typeof method === \"string\" ? `method \"${method}\"` : \"no method\";\n const idPart = id === undefined ? \"no id\" : `id ${JSON.stringify(id)}`;\n return `${methodPart}, ${idPart}`;\n}\n\n/** How much of an offending frame a diagnostic quotes back. */\nconst QUOTED_FRAME_LIMIT = 200;\n\n/**\n * Quotes a frame for a log line, shortened so one oversized message cannot fill\n * a console. The reader needs enough to recognize the shape, not the payload.\n */\nexport function truncate(raw: string, limit: number = QUOTED_FRAME_LIMIT): string {\n return raw.length <= limit ? raw : `${raw.slice(0, limit)}... (${raw.length} bytes total)`;\n}\n\n// ---------------------------------------------------------------------------\n// Console throttling\n// ---------------------------------------------------------------------------\n\n/**\n * One in how many repeats of the same diagnostic reaches the console.\n *\n * A mis-wired tunnel does not fail once, it fails on every frame. Logging each\n * occurrence would bury the first, most useful message under thousands of\n * copies of itself, which is how a browser console becomes unreadable.\n */\nexport const NOTICE_SAMPLE_RATE = 50;\n\n/**\n * Counts occurrences of one diagnostic on one socket and decides which of them\n * are logged: the first always, then one in every {@link NOTICE_SAMPLE_RATE}.\n *\n * Only console output is sampled. Callbacks such as `onError` still fire on\n * every occurrence, so nothing an application observes changes.\n */\nexport class ThrottledNotice {\n private readonly _sampleRate: number;\n private _count = 0;\n\n constructor(sampleRate: number = NOTICE_SAMPLE_RATE) {\n this._sampleRate = Math.max(1, sampleRate);\n }\n\n /** Occurrences reported so far, logged or suppressed. */\n get count(): number {\n return this._count;\n }\n\n /** Records one occurrence and answers whether it should be logged. */\n hit(): boolean {\n this._count++;\n return this._count === 1 || this._count % this._sampleRate === 0;\n }\n\n /**\n * Suffix naming how many occurrences the current line stands for.\n *\n * Empty on the first occurrence, so the message a reader is meant to act on\n * arrives unadorned.\n */\n suffix(): string {\n return this._count <= 1 ? \"\" : ` [occurrence ${this._count} on this socket, logging one in ${this._sampleRate}]`;\n }\n}\n\n// ---------------------------------------------------------------------------\n// URL guards\n// ---------------------------------------------------------------------------\n\n/** The broker's default slot-scoped provider path, `/provider/<name>`. */\nexport const DEFAULT_PROVIDER_PATH = \"/provider\";\n\n/** The broker's default shared multiplex path, on which envelopes are spoken. */\nexport const DEFAULT_MULTIPLEX_PATH = \"/providers\";\n\n/**\n * Appended to both guards. The broker's paths are configurable\n * (`withProviderPath` / `withProvidersPath`), so the heuristic can legitimately\n * be wrong, and a warning that cannot be dismissed is a warning people learn to\n * ignore.\n */\nconst RECONFIGURED_HINT = \"If you have reconfigured the broker's paths.provider / paths.providers, ignore this warning.\";\n\n/** Parses a WebSocket URL, or `undefined` when it is not a URL at all. */\nfunction parseWsUrl(wsUrl: string): URL | undefined {\n try {\n // `ws:` and `wss:` are special schemes for the URL parser, so host and\n // pathname come out exactly as they would for `http:`.\n return new URL(wsUrl);\n } catch {\n return undefined;\n }\n}\n\n/**\n * The same URL with its path replaced, so a suggestion can be pasted as-is.\n *\n * Assembled by hand rather than through the `pathname` setter, which percent-\n * encodes: a suggested path carrying a `<name>` placeholder would come back as\n * `%3Cname%3E` and read as a typo.\n */\nfunction withPath(url: URL, path: string): string {\n return `${url.protocol}//${url.host}${path}${url.search}`;\n}\n\n/**\n * Warns when a {@link DirectTransport} is aimed at the shared multiplex base.\n *\n * This is one half of the mismatch that costs an integrator a day: the broker\n * decides framing by which endpoint the socket landed on, so a plain JSON-RPC\n * frame arriving on `/providers` is not an envelope, is dropped, and nothing is\n * logged on either side.\n *\n * Warns, never throws: a false positive must not break a deployment that has\n * reconfigured its paths.\n */\nexport function warnIfMultiplexPath(wsUrl: string): void {\n const url = parseWsUrl(wsUrl);\n if (!url) return;\n\n const path = url.pathname;\n if (path !== DEFAULT_MULTIPLEX_PATH && !path.startsWith(`${DEFAULT_MULTIPLEX_PATH}/`)) return;\n\n console.warn(\n `[mcp-provider] DirectTransport is connecting to \"${wsUrl}\", whose path \"${path}\" is the broker's shared multiplex endpoint. ` +\n `That endpoint carries multiplex envelopes { provider, payload }, while DirectTransport writes plain JSON-RPC frames, so the broker will not route what this transport sends. ` +\n `Either point DirectTransport at the slot-scoped path \"${withPath(url, `${DEFAULT_PROVIDER_PATH}/<name>`)}\", or publish through MultiplexTransport.create(\"<name>\", \"${wsUrl}\"). ` +\n RECONFIGURED_HINT\n );\n}\n\n/**\n * Warns when a {@link MultiplexTransport} is aimed at a slot-scoped path.\n *\n * The mirror of {@link warnIfMultiplexPath}: envelopes sent to\n * `/provider/<name>` are taken for opaque provider messages and rebroadcast to\n * clients as notifications, so the publisher waits for answers that never come.\n */\nexport function warnIfSlotScopedPath(wsUrl: string): void {\n const url = parseWsUrl(wsUrl);\n if (!url) return;\n\n const path = url.pathname;\n if (path !== DEFAULT_PROVIDER_PATH && !path.startsWith(`${DEFAULT_PROVIDER_PATH}/`)) return;\n\n console.warn(\n `[mcp-provider] MultiplexTransport is connecting to \"${wsUrl}\", whose path \"${path}\" is a slot-scoped provider endpoint. ` +\n `That endpoint carries plain JSON-RPC frames, while MultiplexTransport writes multiplex envelopes { provider, payload }, which the broker never unwraps, so nothing sent here is answered. ` +\n `Either publish through new DirectTransport(\"${wsUrl}\"), or point MultiplexTransport at the shared multiplex base \"${withPath(url, DEFAULT_MULTIPLEX_PATH)}\". ` +\n RECONFIGURED_HINT\n );\n}\n\n/**\n * The handshake headers a transport sends: `headers`, plus `X-Provider-Token`\n * from `secret`. `undefined` when there are none, so the socket is built with\n * the one-argument constructor every runtime has.\n */\nexport function handshakeHeaders(options: { readonly secret?: string; readonly headers?: Readonly<Record<string, string>> } | undefined): Record<string, string> | undefined {\n const headers: Record<string, string> = { ...(options?.headers ?? {}) };\n if (options?.secret) headers[\"x-provider-token\"] = options.secret;\n return Object.keys(headers).length > 0 ? headers : undefined;\n}\n\n/**\n * Opens a WebSocket, with handshake headers when there are any. Headers need\n * Node's `WebSocket` (22+); a browser cannot send them, and is told so.\n */\nexport function openWebSocket(url: string, headers: Record<string, string> | undefined): WebSocket {\n // Node 20 has no global WebSocket. Said plainly, rather than surfacing as\n // a ReferenceError or as a claim that headers are unsupported.\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n `Cannot open ${url}: this runtime has no global WebSocket (Node before 22). ` +\n `Assign one before connecting: import { WebSocket } from \"ws\"; globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket;`\n );\n }\n if (!headers) return new WebSocket(url);\n try {\n return new (WebSocket as unknown as new (url: string, init: { headers: Record<string, string> }) => WebSocket)(url, { headers });\n } catch (error) {\n throw new Error(\n `Cannot open ${url} with handshake headers (secret / headers): this runtime's WebSocket does not accept them. ` +\n `Node 22 and later do; a browser never does, so a browser provider cannot authenticate to the broker. ${(error as Error).message}`\n );\n }\n}\n","import type { IMessageTransport } from \"@cyanmycelium/mcp-core\";\nimport { encodeRegisterFrame } from \"./protocol/index\";\nimport { BrokerClient } from \"./broker.client\";\nimport { describeFrame, handshakeHeaders, openWebSocket, PendingFrames, ThrottledNotice, warnIfMultiplexPath } from \"./transport.support\";\n\n/** Options accepted by {@link DirectTransport}. */\nexport interface IDirectTransportOptions {\n /**\n * Join the broker's `_all` aggregate slot as well as this provider's own\n * slot, by sending the registration notification\n * `{\"jsonrpc\":\"2.0\",\"method\":\"notifications/register\",\"params\":{\"aggregate\":true}}`\n * as the first frame on the socket.\n *\n * Opt-in on purpose: `_all` exposes this provider's tools and prompts to\n * every client of the aggregate slot, so a provider that does not ask for it\n * stays reachable only on its own slot.\n *\n * ORDERING: the broker runs `initialize` against a newly aggregated provider\n * immediately, and drops it from `_all` without a word if the handshake times\n * out. The frame therefore goes out on `open`, never from the constructor, so\n * assign `onMessage` (or hand this transport to an MCP server, which assigns\n * it for you) *before* calling {@link DirectTransport.connect}. Connecting\n * first and wiring the handler afterwards loses the broker's `initialize` and\n * the provider silently never appears in `_all`.\n */\n aggregate?: boolean;\n\n /**\n * Rejects a `broker.declare()` / `broker.authorize()` the broker did not\n * answer within this many ms. Off by default: a broker from 1.4.1 on answers\n * at once. Only for an older broker, which drops methods it does not know.\n */\n brokerRequestTimeoutMs?: number;\n\n /**\n * The provider secret, sent as the `X-Provider-Token` header of the\n * WebSocket handshake. Required by a broker that authenticates providers\n * (`providerSecret`, or the security file's `providers` table, where it is\n * what gives this provider its own identity).\n *\n * **Node only.** Node's `WebSocket` (22 and later) accepts handshake\n * headers; a browser's does not, and a browser provider cannot\n * authenticate (terminate provider auth in a reverse proxy instead).\n */\n secret?: string;\n\n /** Extra handshake headers, Node only, like {@link secret}. */\n headers?: Readonly<Record<string, string>>;\n}\n\n/**\n * 1:1 WebSocket transport, wraps a single `WebSocket` connection to a broker\n * provider slot, typically `ws://<broker>/provider/<name>`.\n *\n * One server owns one socket. When an application publishes several servers\n * through the same broker, prefer {@link MultiplexTransport}, which shares a\n * single socket between them.\n *\n * This transport does **not** reconnect: when the socket closes, it stays\n * closed until the application calls {@link connect} again. Only\n * {@link MultiplexTransport}'s shared socket reconnects on its own.\n *\n * Call {@link connect} after setting the event callbacks to open the socket.\n */\nexport class DirectTransport implements IMessageTransport {\n private readonly _wsUrl: string;\n private readonly _aggregate: boolean | undefined;\n private readonly _headers: Record<string, string> | undefined;\n private readonly _pending: PendingFrames;\n\n /** Throttles the \"wrote to a closed transport\" line, which repeats per frame. */\n private readonly _afterCloseNotice = new ThrottledNotice();\n\n private _ws: WebSocket | null = null;\n private _closed = false;\n\n onMessage: ((data: string) => void) | null = null;\n onOpen: (() => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n /**\n * The broker's own methods for this slot: declaring an authorization\n * domain, asking for decisions. Its answers are taken off the socket\n * before {@link onMessage} sees anything.\n */\n readonly broker: BrokerClient;\n\n constructor(wsUrl: string, options?: IDirectTransportOptions) {\n this._wsUrl = wsUrl;\n this._aggregate = options?.aggregate;\n this._headers = handshakeHeaders(options);\n this._pending = new PendingFrames(`DirectTransport ${wsUrl}`);\n this.broker = new BrokerClient(\n (frame) => this.send(frame),\n { requestTimeoutMs: options?.brokerRequestTimeoutMs },\n (frame) => {\n if (this._ws?.readyState !== WebSocket.OPEN) return false;\n this._ws.send(frame);\n return true;\n }\n );\n }\n\n get isOpen(): boolean {\n return this._ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Opens the WebSocket connection and wires its events to the transport\n * callbacks. Must be called after assigning `onOpen` / `onMessage` / etc.\n */\n connect(): void {\n // Fires before the socket exists, so the mismatch is named even if the\n // handshake succeeds, which it does: the broker accepts the connection\n // and then drops every frame this transport writes.\n warnIfMultiplexPath(this._wsUrl);\n\n const ws = openWebSocket(this._wsUrl, this._headers);\n\n this._closed = false;\n\n // Held from construction, not from `onopen`, the way MultiplexSocket\n // already does it: everything written between `connect()` and open is\n // otherwise sent to a `null` socket and discarded without a trace.\n // Readiness is decided by `readyState`, so a connecting socket is never\n // mistaken for a usable one.\n this._ws = ws;\n\n // Every handler below starts by checking that this socket is still the\n // current one. A second `connect()` supersedes the first, and a stale\n // socket's late `onclose` would otherwise null the live one out from\n // under the server, which then goes quiet with no error anywhere.\n ws.onopen = () => {\n if (this._ws !== ws) return;\n this._sendRegistration(ws);\n this._flush(ws);\n this.onOpen?.();\n };\n\n ws.onerror = () => {\n if (this._ws !== ws) return;\n this.onError?.(new Error(`DirectTransport: WebSocket error on ${this._wsUrl}`));\n };\n\n ws.onclose = (event?: CloseEvent) => {\n if (this._ws !== ws) return;\n this._ws = null;\n\n const discarded = this._pending.clear();\n this.broker.rejectAll(`DirectTransport: the socket to ${this._wsUrl} closed before the broker answered`);\n\n // ORDER IS LOAD-BEARING: `onError` must fire before `onClose`.\n // An MCP server's `onClose` clears its running flag, after which it\n // treats a later `onError` as a pre-open failure and rejects an\n // already-settled promise, so the message is thrown away. Reporting\n // first is what makes a broker refusal (1008, with the reason in the\n // close frame) readable instead of an undifferentiated disconnect.\n const error = this._closeError(event, discarded);\n if (error) this.onError?.(new Error(error));\n\n this.onClose?.();\n };\n\n ws.onmessage = (event: MessageEvent<string>) => {\n if (this.broker.handleIncoming(event.data)) return;\n this.onMessage?.(event.data);\n };\n }\n\n send(data: string): void {\n if (this._ws?.readyState === WebSocket.OPEN) {\n this._ws.send(data);\n return;\n }\n\n if (this._closed) {\n if (this._afterCloseNotice.hit()) {\n console.warn(\n `[mcp-provider] DirectTransport ${this._wsUrl}: dropping a frame written after close() (${describeFrame(data)}). ` +\n `This transport does not reconnect, call connect() again before sending.${this._afterCloseNotice.suffix()}`\n );\n }\n return;\n }\n\n // The socket is still connecting. Queue rather than drop: an MCP server\n // writes its handshake as soon as it is told to start, and the socket is\n // usually not open yet.\n this._pending.push(data);\n }\n\n close(): void {\n this._closed = true;\n\n const discarded = this._pending.clear();\n if (discarded > 0) {\n console.warn(`[mcp-provider] DirectTransport ${this._wsUrl}: closed with ${discarded} frame(s) still queued, they were never sent.`);\n }\n\n // `_ws` is deliberately left in place: the browser fires `onclose`\n // asynchronously, and clearing it here would make the handler's staleness\n // check reject its own socket and swallow `onClose`. The handler nulls it.\n this._ws?.close();\n }\n\n /**\n * Sends the registration notification when the caller asked for a specific\n * aggregate membership. The slot name is not in the frame: on the\n * slot-scoped path the broker takes it from the URL at connect time.\n */\n private _sendRegistration(ws: WebSocket): void {\n if (this._aggregate === undefined) return;\n ws.send(encodeRegisterFrame({ aggregate: this._aggregate }));\n }\n\n /** Writes out everything queued while the socket was connecting. */\n private _flush(ws: WebSocket): void {\n for (const frame of this._pending.drain()) {\n ws.send(frame);\n }\n }\n\n /**\n * Builds the message for a close worth reporting, or `undefined` when there\n * is nothing to say.\n *\n * A code of 1000 is a normal close and stays silent. An environment that\n * calls `onclose` with no event at all leaves nothing to distinguish a\n * refusal from a clean shutdown, so that stays silent too.\n */\n private _closeError(event: CloseEvent | undefined, discarded: number): string | undefined {\n const code = event?.code;\n if (code === undefined || code === 1000) return undefined;\n\n const reason = event?.reason ? `: \"${event.reason}\"` : \" (no reason given)\";\n const hint =\n code === 1008\n ? \"Code 1008 is a policy refusal from the broker, not a network drop: the slot is already connected, is reserved, or provider authentication rejected it. The reason above is the broker's own wording.\"\n : \"DirectTransport does not reconnect, call connect() again to retry.\";\n const lost = discarded > 0 ? ` ${discarded} queued frame(s) were discarded.` : \"\";\n\n return `DirectTransport: the socket to ${this._wsUrl} closed with code ${code}${reason}. ${hint}${lost}`;\n }\n}\n","import type { IMessageTransport } from \"@cyanmycelium/mcp-core\";\nimport { decodeEnvelope, encodeEnvelope, encodeRegisterEnvelope, envelopeFrame, tunnelErrorOf } from \"./protocol/index\";\nimport { describeFrame, handshakeHeaders, openWebSocket, PendingFrames, ThrottledNotice, truncate, warnIfSlotScopedPath } from \"./transport.support\";\nimport { BrokerClient } from \"./broker.client\";\n\n/** The diagnostics one socket may repeat, counted per socket so each is said once in full. */\ninterface ISocketNotices {\n /** A frame arrived that is not an envelope at all, the classic framing mismatch. */\n readonly notEnvelope: ThrottledNotice;\n\n /** An envelope arrived for a slot no transport on this socket publishes. */\n readonly unknownProvider: ThrottledNotice;\n\n /** The broker refused something and said so in an error envelope. */\n readonly tunnelError: ThrottledNotice;\n}\n\n// ---------------------------------------------------------------------------\n// MultiplexSocket, shared WebSocket singleton (internal)\n// ---------------------------------------------------------------------------\n\n/**\n * Manages a single WebSocket connection shared by multiple {@link MultiplexTransport}\n * instances. All traffic goes through the tunnel envelope protocol, whose\n * definition lives in `./protocol` and is shared with the broker.\n *\n * Reconnection is handled centrally here, individual transports do not reconnect.\n * Use {@link getOrCreate} to obtain a per-URL singleton.\n */\nclass MultiplexSocket {\n /** Per-URL cache so all transports targeting the same tunnel share one socket. */\n private static readonly _instances = new Map<string, MultiplexSocket>();\n\n private readonly _wsUrl: string;\n /** Cache key: the URL, and the handshake headers, since two secrets are two identities and need two sockets. */\n private readonly _key: string;\n private readonly _headers: Record<string, string> | undefined;\n private readonly _transports = new Map<string, MultiplexTransport>();\n\n /** Aggregate opt-in per slot, absent when the caller did not express one. */\n private readonly _aggregates = new Map<string, boolean>();\n\n /** Frames written while the socket is connecting, reconnecting or backing off. */\n private readonly _pending: PendingFrames;\n\n private _ws: WebSocket | null = null;\n private _reconnectAttempts = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _stopped = false;\n\n /** Set once the last transport left and this instance gave up its URL. */\n private _dead = false;\n\n /** The URL guard is about the URL, not the socket, so it is said once. */\n private _pathWarned = false;\n\n /** Throttles the \"wrote to a closed tunnel\" line, which repeats per frame. */\n private readonly _afterStopNotice = new ThrottledNotice();\n\n private constructor(wsUrl: string, key: string, headers: Record<string, string> | undefined) {\n this._wsUrl = wsUrl;\n this._key = key;\n this._headers = headers;\n this._pending = new PendingFrames(`MultiplexSocket ${wsUrl}`);\n }\n\n /** Returns (or creates) the singleton socket for a given tunnel URL and handshake headers. */\n static getOrCreate(wsUrl: string, headers?: Record<string, string>): MultiplexSocket {\n const key = headers ? `${wsUrl}\\u0000${JSON.stringify(Object.entries(headers).sort())}` : wsUrl;\n let instance = MultiplexSocket._instances.get(key);\n if (!instance) {\n instance = new MultiplexSocket(wsUrl, key, headers);\n MultiplexSocket._instances.set(key, instance);\n }\n return instance;\n }\n\n get isOpen(): boolean {\n return this._ws?.readyState === WebSocket.OPEN;\n }\n\n // ── Registration ────────────────────────────────────────────────────────\n\n register(name: string, transport: MultiplexTransport, aggregate?: boolean): void {\n if (this._dead) this._revive();\n\n this._transports.set(name, transport);\n if (aggregate !== undefined) this._aggregates.set(name, aggregate);\n\n // If the shared socket is already open, announce the new provider and\n // notify the transport immediately.\n if (this.isOpen) {\n this._announceProvider(name);\n transport.onOpen?.();\n } else if (!this._ws) {\n // First registration, open the connection. A reconnect may already\n // be armed from an earlier teardown, and letting it fire as well\n // would open a rival socket that the broker refuses.\n this._cancelReconnect();\n this._stopped = false;\n this._connect();\n }\n }\n\n unregister(name: string): void {\n this._transports.delete(name);\n this._aggregates.delete(name);\n\n // Tear down the shared socket when no transports remain.\n if (this._transports.size === 0) {\n this._stopped = true;\n this._cancelReconnect();\n this._pending.clear();\n this._ws?.close();\n this._ws = null;\n\n // Marked dead as well as evicted: a MultiplexTransport captures its\n // socket at construction, so one built before the teardown can still\n // call `register()` on this instance long after `getOrCreate` started\n // handing out a different one. Without the flag that reactivation\n // silently opens a second socket to the same URL, and the broker\n // refuses whichever of the two loses the race.\n this._dead = true;\n MultiplexSocket._instances.delete(this._key);\n }\n }\n\n /**\n * Brings a torn-down instance back into service when a transport that\n * captured it is reactivated.\n *\n * Reclaims the URL when nothing else holds it. When another instance already\n * does, the two would race for the same slot names, so this says exactly that\n * rather than letting the loser fail with a refusal nobody reads.\n */\n private _revive(): void {\n this._dead = false;\n this._stopped = false;\n\n const live = MultiplexSocket._instances.get(this._key);\n if (!live) {\n MultiplexSocket._instances.set(this._key, this);\n return;\n }\n if (live !== this) {\n console.warn(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: a transport built before this tunnel was closed is being reactivated, so it will open a second socket to the same URL. ` +\n `The broker admits one socket per slot and refuses the other. Rebuild the transport with MultiplexTransport.create(name, url) instead of reusing one you closed.`\n );\n }\n }\n\n /**\n * Claims the slot for `name` so the broker eagerly creates its provider\n * state before any MCP client connects. Without it the broker only learns\n * about a provider on its first real message, and a client connecting in\n * between is told the provider is not connected.\n *\n * Carries the aggregate opt-in when the caller expressed one, which is why\n * it must go out before any traffic: the broker runs `initialize` against a\n * newly aggregated provider straight away.\n */\n private _announceProvider(name: string): void {\n if (this._ws?.readyState !== WebSocket.OPEN) return;\n\n const aggregate = this._aggregates.get(name);\n this._ws.send(aggregate === undefined ? encodeRegisterEnvelope(name) : encodeRegisterEnvelope(name, { aggregate }));\n }\n\n // ── Sending ─────────────────────────────────────────────────────────────\n\n send(provider: string, data: string): void {\n const frame = encodeEnvelope(provider, data);\n\n if (this._ws?.readyState === WebSocket.OPEN) {\n this._ws.send(frame);\n return;\n }\n\n // The tunnel was closed on purpose, so nothing will ever flush a queue.\n if (this._stopped) {\n if (this._afterStopNotice.hit()) {\n console.warn(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: dropping a frame for provider \"${provider}\" written after the tunnel was closed (${describeFrame(frame)}). ` +\n `Call connect() on a transport to reopen it.${this._afterStopNotice.suffix()}`\n );\n }\n return;\n }\n\n // Connecting, or waiting out a reconnect back-off of up to 30 seconds.\n // Dropping here is what makes a provider look alive while answering\n // nothing, so the frame waits for the socket instead.\n this._pending.push(frame);\n }\n\n /** Sends low-priority telemetry only while the shared link is open. */\n sendTelemetry(provider: string, data: string): boolean {\n if (this._ws?.readyState !== WebSocket.OPEN) return false;\n this._ws.send(encodeEnvelope(provider, data));\n return true;\n }\n\n // ── Connection lifecycle ────────────────────────────────────────────────\n\n private _connect(): void {\n if (!this._pathWarned) {\n this._pathWarned = true;\n warnIfSlotScopedPath(this._wsUrl);\n }\n\n const ws = openWebSocket(this._wsUrl, this._headers);\n\n // Counted per socket so a mismatch reports itself once in full rather\n // than once per frame for the life of the page.\n const notices: ISocketNotices = {\n notEnvelope: new ThrottledNotice(),\n unknownProvider: new ThrottledNotice(),\n tunnelError: new ThrottledNotice(),\n };\n\n // Held from construction, not from `onopen`: a transport registering\n // while the handshake is still in flight must find this socket rather\n // than open a second one. Readiness is decided by `readyState`, so a\n // connecting socket is never mistaken for a usable one.\n this._ws = ws;\n\n // Every handler below starts by checking that this socket is still the\n // current one. An orphaned socket, one superseded by a reconnect or by a\n // reactivated transport, otherwise keeps speaking for the instance: its\n // `onclose` nulls `_ws` and fires `onClose` on every transport while a\n // newer socket is live, after which `isOpen` reads false, an MCP server\n // gates every send on it, and the provider goes silent on a working\n // tunnel with nothing logged anywhere.\n ws.onopen = () => {\n if (this._ws !== ws) return;\n\n this._reconnectAttempts = 0;\n // Announce all registered providers to the broker so it eagerly\n // creates their slots before any MCP client connects.\n for (const name of this._transports.keys()) {\n this._announceProvider(name);\n }\n // Then whatever was written while the socket was down, after the\n // registrations and before the transports are told they are open, so\n // the broker sees the slots claimed before any traffic on them.\n this._flush(ws);\n for (const transport of this._transports.values()) {\n transport.onOpen?.();\n }\n };\n\n ws.onerror = () => {\n if (this._ws !== ws) return;\n\n for (const transport of this._transports.values()) {\n transport.onError?.(new Error(`MultiplexSocket: WebSocket error on ${this._wsUrl}`));\n }\n };\n\n ws.onclose = () => {\n if (this._ws !== ws) return;\n\n this._ws = null;\n for (const transport of this._transports.values()) {\n transport.broker.rejectAll(`MultiplexTransport: the shared socket to ${this._wsUrl} closed before the broker answered`);\n transport.onClose?.();\n }\n if (!this._stopped) {\n this._scheduleReconnect();\n }\n };\n\n ws.onmessage = (event: MessageEvent<string>) => {\n if (this._ws !== ws) return;\n this._routeIncoming(event.data, notices);\n };\n }\n\n /** Writes out everything queued while the socket was down. */\n private _flush(ws: WebSocket): void {\n for (const frame of this._pending.drain()) {\n ws.send(frame);\n }\n }\n\n private _routeIncoming(raw: string, notices: ISocketNotices): void {\n const envelope = decodeEnvelope(raw);\n if (!envelope) {\n // The single most expensive silent failure in this stack, and the\n // one the field report lost a day to. The broker decides framing\n // from the endpoint the socket landed on, so a slot-scoped path\n // answers in plain JSON-RPC, which is not an envelope and used to be\n // dropped here without a word while the tunnel looked healthy.\n if (notices.notEnvelope.hit()) {\n console.error(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: dropped an incoming frame that is not a tunnel envelope { provider, payload }: ${truncate(raw)}. ` +\n `Only the broker's shared multiplex base (/providers) speaks envelopes. A slot-scoped path (/provider/<name>) carries plain JSON-RPC and needs new DirectTransport(url) instead. ` +\n `If this URL is already the multiplex base, the frame came from something else writing on the socket, such as a proxy error page.${notices.notEnvelope.suffix()}`\n );\n }\n return;\n }\n\n const transport = this._transports.get(envelope.provider);\n if (!transport) {\n if (notices.unknownProvider.hit()) {\n const known = [...this._transports.keys()].map((name) => `\"${name}\"`).join(\", \") || \"none\";\n console.error(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: dropped an envelope for provider \"${envelope.provider}\", which no transport on this socket publishes. Registered here: ${known}. ` +\n `Check that the slot name passed to MultiplexTransport.create matches the one the broker routes to.${notices.unknownProvider.suffix()}`\n );\n }\n return;\n }\n\n // A tunnel-level refusal (a rejected slot, an unavailable provider)\n // carries no request id. Handing it to an MCP server would get it\n // classified as an unknown notification and dropped without a word, so\n // surface it as a transport error instead.\n const payload = envelope.payload as { id?: unknown } | null;\n if (payload !== null && (payload.id === null || payload.id === undefined)) {\n const error = tunnelErrorOf(envelope.payload);\n if (error) {\n const message = `Tunnel error ${error.code} on provider \"${envelope.provider}\": ${error.message}`;\n transport.onError?.(new Error(message));\n\n // `onError` alone is not enough to be heard: an MCP server\n // overwrites it when it starts and only reports through it while\n // it is not yet running, and this socket reports itself open\n // before any refusal can arrive. So the console is the only place\n // a browser-hosted provider learns it was refused.\n if (notices.tunnelError.hit()) {\n console.error(`[mcp-provider] ${message}${notices.tunnelError.suffix()}`);\n }\n return;\n }\n }\n\n transport._receive(envelopeFrame(envelope));\n }\n\n private _scheduleReconnect(): void {\n const base = 1_000;\n const max = 30_000;\n const jitter = 0.5 + Math.random() * 0.5;\n const delay = Math.min(base * 2 ** this._reconnectAttempts, max) * jitter;\n\n this._reconnectAttempts++;\n this._reconnectTimer = setTimeout(() => {\n this._reconnectTimer = null;\n // A registration during the back-off may already have opened a\n // socket. Reconnecting on top of it would leave two live sockets\n // announcing the same slots, one of which the broker refuses.\n if (this._stopped || this._ws) return;\n this._connect();\n }, delay);\n }\n\n /** Disarms a pending reconnect, so nothing opens a socket behind our back. */\n private _cancelReconnect(): void {\n if (this._reconnectTimer === null) return;\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// MultiplexTransport, per-server transport (public)\n// ---------------------------------------------------------------------------\n\n/** Options accepted by {@link MultiplexTransport.create}. */\nexport interface IMultiplexTransportOptions {\n /**\n * Join the broker's `_all` aggregate slot as well as this provider's own\n * slot, by carrying `params: { aggregate: true }` on the registration\n * notification the shared socket sends when it claims the slot.\n *\n * Opt-in on purpose: `_all` exposes this provider's tools and prompts to\n * every client of the aggregate slot, so a provider that does not ask for it\n * stays reachable only on its own slot.\n *\n * ORDERING: the broker runs `initialize` against a newly aggregated provider\n * immediately, and drops it from `_all` without a word if the handshake times\n * out. The registration goes out from {@link MultiplexTransport.connect}, so\n * assign `onMessage` (or hand this transport to an MCP server, which assigns\n * it for you) *before* connecting. Connecting first and wiring the handler\n * afterwards loses the broker's `initialize`, and the provider silently never\n * appears in `_all`.\n */\n aggregate?: boolean;\n\n /**\n * Rejects a `broker.declare()` / `broker.authorize()` the broker did not\n * answer within this many ms. Off by default: a broker from 1.4.1 on answers\n * at once. Only for an older broker, which drops methods it does not know.\n */\n brokerRequestTimeoutMs?: number;\n\n /**\n * The provider secret, sent as the `X-Provider-Token` header of the\n * WebSocket handshake. Required by a broker that authenticates providers\n * (`providerSecret`, or the security file's `providers` table, where it is\n * what gives this provider its own identity).\n *\n * **Node only.** Node's `WebSocket` (22 and later) accepts handshake\n * headers; a browser's does not, and a browser provider cannot\n * authenticate (terminate provider auth in a reverse proxy instead).\n */\n secret?: string;\n\n /** Extra handshake headers, Node only, like {@link secret}. */\n headers?: Readonly<Record<string, string>>;\n}\n\n/**\n * A transport that multiplexes multiple MCP servers over a single shared\n * WebSocket connection using the envelope protocol `{ provider, payload }`.\n *\n * Use the static {@link create} factory to obtain an instance:\n * ```typescript\n * const t1 = MultiplexTransport.create(\"scene-1\", \"ws://localhost:3000/providers\");\n * const t2 = MultiplexTransport.create(\"scene-2\", \"ws://localhost:3000/providers\");\n * // t1 and t2 share a single WebSocket under the hood.\n * ```\n */\nexport class MultiplexTransport implements IMessageTransport {\n private readonly _name: string;\n private readonly _socket: MultiplexSocket;\n private readonly _aggregate: boolean | undefined;\n private _registered = false;\n\n onMessage: ((data: string) => void) | null = null;\n onOpen: (() => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n /**\n * The broker's own methods for this slot: declaring an authorization\n * domain, asking for decisions. Its answers are taken off the socket\n * before {@link onMessage} sees anything.\n */\n readonly broker: BrokerClient;\n\n constructor(name: string, socket: MultiplexSocket, options?: IMultiplexTransportOptions) {\n this._name = name;\n this._socket = socket;\n this._aggregate = options?.aggregate;\n this.broker = new BrokerClient(\n (frame) => this.send(frame),\n { requestTimeoutMs: options?.brokerRequestTimeoutMs },\n (frame) => this._socket.sendTelemetry(this._name, frame)\n );\n }\n\n /**\n * One frame from the broker for this slot: the broker's answer to one of\n * {@link broker}'s requests, or MCP traffic for the server.\n * @internal Called by the shared socket.\n */\n _receive(frame: string): void {\n if (this.broker.handleIncoming(frame)) return;\n this.onMessage?.(frame);\n }\n\n /**\n * Convenience factory: creates a {@link MultiplexTransport} backed by a\n * shared {@link MultiplexSocket} for the given tunnel URL.\n *\n * Transports targeting the same `wsUrl` automatically share one WebSocket.\n *\n * @param wsUrl The broker's shared multiplex base, `ws://<broker>/providers`.\n * A slot-scoped `/provider/<name>` URL belongs to\n * {@link DirectTransport} and is warned about on connect.\n */\n static create(name: string, wsUrl: string, options?: IMultiplexTransportOptions): MultiplexTransport {\n return new MultiplexTransport(name, MultiplexSocket.getOrCreate(wsUrl, handshakeHeaders(options)), options);\n }\n\n get isOpen(): boolean {\n return this._socket.isOpen;\n }\n\n /**\n * Registers this transport with the shared socket.\n *\n * Safe to call multiple times, subsequent calls are no-ops.\n */\n activate(): void {\n if (!this._registered) {\n this._registered = true;\n this._socket.register(this._name, this, this._aggregate);\n }\n }\n\n /**\n * Opens the transport, the same way every other transport does.\n *\n * An alias of {@link activate} so callers never have to special-case this\n * class: an MCP server or client just calls `connect()` on whatever\n * transport it was handed.\n */\n connect(): void {\n this.activate();\n }\n\n send(data: string): void {\n this._socket.send(this._name, data);\n }\n\n close(): void {\n if (this._registered) {\n this._registered = false;\n this._socket.unregister(this._name);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/protocol/envelope.ts","../src/broker.client.ts","../src/transport.support.ts","../src/direct.transport.ts","../src/multiplex.transport.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The CyanMycelium tunnel envelope protocol.\n *\n * A multiplexed tunnel socket carries traffic for several providers at once, so\n * every JSON-RPC message is wrapped with the name of the provider slot it\n * belongs to. Both ends of the tunnel encode and decode with the helpers here:\n * the client transports that publish a provider, and the broker that routes\n * between providers and MCP clients.\n *\n * This module is the single definition of that wire format. It is deliberately\n * dependency-free and isomorphic, so the browser side and the Node broker share\n * exactly one implementation rather than two that drift apart.\n *\n * Wire format:\n * ```json\n * { \"provider\": \"scene-1\", \"payload\": { \"jsonrpc\": \"2.0\", \"id\": 1, \"result\": {} } }\n * ```\n */\n\n/** One framed message on a multiplexed tunnel socket. */\nexport interface TunnelEnvelope {\n /** Name of the provider slot this message belongs to. */\n provider: string;\n\n /** The JSON-RPC message itself, already parsed. */\n payload: unknown;\n}\n\n/**\n * Notification a client sends to claim a provider slot as soon as the tunnel\n * opens, before any MCP client shows up.\n *\n * Without it the broker only discovers a provider name on its first real\n * message, so an MCP client connecting in between is told the provider is not\n * connected. It is a plain JSON-RPC notification, which any peer that does not\n * recognize it ignores.\n */\nexport const TUNNEL_REGISTER_METHOD = \"notifications/register\";\n\n/** Options carried by the registration notification, as its `params`. */\nexport interface ITunnelRegisterOptions {\n /**\n * Join the broker's `_all` aggregate slot in addition to the provider's own\n * slot, so a single MCP client sees every opted-in provider's tools and\n * prompts through one connection.\n *\n * Opt-in on purpose: `_all` is a confidentiality boundary, and a provider\n * that never asks for it stays reachable only on its own slot.\n */\n aggregate?: boolean;\n}\n\n/**\n * Builds the registration notification as a plain JSON-RPC frame, for the\n * slot-scoped path `/provider/<name>`, which carries no envelope.\n *\n * The slot name is not in the frame: on that path the broker takes it from the\n * URL at connect time, before any frame exists.\n *\n * @param options When `aggregate` is set, it is carried as `params.aggregate`.\n * Omitted entirely otherwise, which keeps the frame identical to\n * the parameterless form older brokers already accept.\n */\nexport function encodeRegisterFrame(options?: ITunnelRegisterOptions): string {\n return JSON.stringify(registerMessage(options));\n}\n\n/** The registration notification body, shared by both encoders. */\nfunction registerMessage(options?: ITunnelRegisterOptions): Record<string, unknown> {\n const message: Record<string, unknown> = { jsonrpc: \"2.0\", method: TUNNEL_REGISTER_METHOD };\n if (options?.aggregate !== undefined) {\n message.params = { aggregate: options.aggregate };\n }\n return message;\n}\n\n/** JSON-RPC error codes the broker returns on the tunnel itself. */\nexport const TunnelErrorCodes = {\n /** The slot is taken by another upstream, or the provider is not connected. */\n ProviderUnavailable: -32000,\n\n /** The provider's credentials do not allow publishing on this slot. */\n RegistrationForbidden: -32001,\n} as const;\n\nexport type TunnelErrorCode = (typeof TunnelErrorCodes)[keyof typeof TunnelErrorCodes];\n\n/** A JSON-RPC error as carried inside an envelope payload. */\nexport interface TunnelError {\n code: number;\n message: string;\n data?: unknown;\n}\n\n/**\n * Wraps an already-serialized JSON-RPC frame for `provider`.\n *\n * @throws SyntaxError when `frame` is not valid JSON. Callers hold a frame they\n * just serialized, so a failure here is a bug rather than bad input.\n */\nexport function encodeEnvelope(provider: string, frame: string): string {\n return encodeEnvelopeMessage(provider, JSON.parse(frame));\n}\n\n/** Wraps an already-parsed JSON-RPC message for `provider`. */\nexport function encodeEnvelopeMessage(provider: string, payload: unknown): string {\n const envelope: TunnelEnvelope = { provider, payload };\n return JSON.stringify(envelope);\n}\n\n/**\n * Parses a raw tunnel frame.\n *\n * Returns `undefined` for anything malformed rather than throwing: a tunnel\n * socket is a public surface, and a peer sending garbage must not take the\n * receiver down. Both ends drop such frames silently.\n */\nexport function decodeEnvelope(raw: string): TunnelEnvelope | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return undefined;\n\n const { provider, payload } = parsed as Partial<TunnelEnvelope>;\n if (typeof provider !== \"string\" || provider.length === 0 || payload === undefined) return undefined;\n\n return { provider, payload };\n}\n\n/** Serializes an envelope's payload back into a plain JSON-RPC frame. */\nexport function envelopeFrame(envelope: TunnelEnvelope): string {\n return JSON.stringify(envelope.payload);\n}\n\n/**\n * Builds the registration notification that claims `provider`, wrapped for the\n * multiplexed tunnel.\n *\n * @param options When `aggregate` is set, it is carried as `params.aggregate`.\n * Omitted entirely otherwise: the parameterless frame is the one\n * brokers have always received here, and it stays byte-identical.\n */\nexport function encodeRegisterEnvelope(provider: string, options?: ITunnelRegisterOptions): string {\n return encodeEnvelopeMessage(provider, registerMessage(options));\n}\n\n/**\n * Builds the error envelope the broker returns when it refuses a slot.\n *\n * The id is `null` because the refusal answers no particular request: it\n * reacts to the registration itself.\n */\nexport function encodeErrorEnvelope(provider: string, code: TunnelErrorCode | number, message: string): string {\n return encodeEnvelopeMessage(provider, { jsonrpc: \"2.0\", id: null, error: { code, message } });\n}\n\n/**\n * Reads the JSON-RPC error out of an envelope payload, when there is one.\n *\n * Lets the client side notice a refused registration instead of handing an\n * `id: null` error frame to an MCP server, which would classify it as an\n * unknown notification and drop it without a word.\n */\nexport function tunnelErrorOf(payload: unknown): TunnelError | undefined {\n if (typeof payload !== \"object\" || payload === null) return undefined;\n\n const { error } = payload as { error?: unknown };\n if (typeof error !== \"object\" || error === null) return undefined;\n\n const { code, message } = error as Partial<TunnelError>;\n if (typeof code !== \"number\" || typeof message !== \"string\") return undefined;\n\n return error as TunnelError;\n}\n","/**\n * Talks to the broker itself, over the provider's own socket: declaring an\n * authorization domain, and asking for decisions.\n *\n * Requires broker 1.5.0 or later. An older broker does not know these methods:\n * from 1.4.1 it refuses them at once with `-32601`; 1.4.0 and earlier drop\n * them and never answer, which is why {@link IBrokerClientOptions.requestTimeoutMs}\n * exists, off by default.\n */\n\n/** The `params._meta` key under which the broker passes the caller reference with each request. */\nexport const CALLER_META_KEY = \"io.cyanmycelium/caller\";\n\n/** W3C Trace Context carrier used by MCP requests. */\nexport const TRACEPARENT_META_KEY = \"traceparent\";\n\n/** Provider-to-broker telemetry notification. */\nexport const TELEMETRY_NOTIFICATION_METHOD = \"broker/telemetry\";\n\n/** Provider-to-broker notification reporting what happened after a decision. */\nexport const AUDIT_RESULT_NOTIFICATION_METHOD = \"broker/audit/result\";\n\nconst TRACEPARENT = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;\nconst TRACE_ID = /^[0-9a-f]{32}$/;\nconst SPAN_ID = /^[0-9a-f]{16}$/;\nconst UNIX_NANO = /^(0|[1-9][0-9]{0,19})$/;\n\nexport interface ITraceParent {\n readonly version: \"00\";\n readonly traceId: string;\n readonly parentId: string;\n readonly traceFlags: string;\n}\n\nexport type TelemetryAttributeValue = string | number | boolean;\n\nexport interface ITelemetryEvent {\n readonly name: string;\n readonly timeUnixNano: string;\n readonly attributes?: Readonly<Record<string, TelemetryAttributeValue>>;\n}\n\nexport interface ITelemetrySpan {\n readonly traceId: string;\n readonly spanId: string;\n readonly parentSpanId?: string;\n readonly name: string;\n readonly kind?: number;\n readonly startTimeUnixNano: string;\n readonly endTimeUnixNano: string;\n readonly attributes?: Readonly<Record<string, TelemetryAttributeValue>>;\n readonly events?: readonly ITelemetryEvent[];\n readonly status?: { readonly code: number; readonly message?: string };\n}\n\n/** Parses the W3C version 00 traceparent representation used in MCP metadata. */\nexport function parseTraceparent(value: unknown): ITraceParent | undefined {\n if (typeof value !== \"string\") return undefined;\n const match = TRACEPARENT.exec(value);\n if (!match || /^0+$/.test(match[1]!) || /^0+$/.test(match[2]!)) return undefined;\n return { version: \"00\", traceId: match[1]!, parentId: match[2]!, traceFlags: match[3]! };\n}\n\nexport function formatTraceparent(context: ITraceParent): string {\n return `${context.version}-${context.traceId}-${context.parentId}-${context.traceFlags}`;\n}\n\n/** Reads `params._meta.traceparent`, returning `undefined` when it is malformed. */\nexport function traceparentOf(meta: Readonly<Record<string, unknown>> | undefined): ITraceParent | undefined {\n return parseTraceparent(meta?.[TRACEPARENT_META_KEY]);\n}\n\n/** Returns a metadata copy carrying the supplied validated trace context. */\nexport function withTraceparent(meta: Readonly<Record<string, unknown>> | undefined, context: ITraceParent): Readonly<Record<string, unknown>> {\n const encoded = formatTraceparent(context);\n if (!parseTraceparent(encoded)) throw new TypeError(\"traceparent must be a valid W3C version 00 context\");\n return { ...(meta ?? {}), [TRACEPARENT_META_KEY]: encoded };\n}\n\n/** Continues a trace using the caller's span as the new W3C parent id. */\nexport function childTraceparent(parent: ITraceParent, spanId: string): ITraceParent {\n if (!SPAN_ID.test(spanId) || /^0+$/.test(spanId)) throw new TypeError(\"spanId must be 16 lowercase hexadecimal characters and not all zero\");\n return { ...parent, parentId: spanId };\n}\n\nfunction validAttributes(attributes: Readonly<Record<string, TelemetryAttributeValue>> | undefined): boolean {\n if (attributes === undefined) return true;\n const entries = Object.entries(attributes);\n return (\n entries.length <= 64 &&\n entries.every(\n ([key, value]) =>\n key.length > 0 &&\n key.length <= 128 &&\n (typeof value === \"boolean\" || (typeof value === \"string\" && value.length <= 4096) || (typeof value === \"number\" && Number.isFinite(value)))\n )\n );\n}\n\nfunction validSpan(span: ITelemetrySpan): boolean {\n return (\n TRACE_ID.test(span.traceId) &&\n !/^0+$/.test(span.traceId) &&\n SPAN_ID.test(span.spanId) &&\n !/^0+$/.test(span.spanId) &&\n (span.parentSpanId === undefined || (SPAN_ID.test(span.parentSpanId) && !/^0+$/.test(span.parentSpanId))) &&\n span.name.length > 0 &&\n span.name.length <= 256 &&\n UNIX_NANO.test(span.startTimeUnixNano) &&\n UNIX_NANO.test(span.endTimeUnixNano) &&\n (span.kind === undefined || (Number.isInteger(span.kind) && span.kind >= 0 && span.kind <= 5)) &&\n validAttributes(span.attributes) &&\n (span.events === undefined ||\n (span.events.length <= 32 &&\n span.events.every((event) => event.name.length > 0 && event.name.length <= 256 && UNIX_NANO.test(event.timeUnixNano) && validAttributes(event.attributes)))) &&\n (span.status === undefined ||\n (Number.isInteger(span.status.code) && span.status.code >= 0 && span.status.code <= 2 && (span.status.message === undefined || span.status.message.length <= 1024)))\n );\n}\n\n/** What the broker hands a declaring provider with each request, under {@link CALLER_META_KEY}. */\nexport interface ICallerReference {\n /** Opaque. Valid on this slot, while the request it came with is pending. */\n readonly ref: string;\n /** Ties the decisions and the eventual report to the client request. */\n readonly correlationId: string;\n /** W3C trace id carried by the MCP request. */\n readonly traceId?: string;\n}\n\n/**\n * Reads the caller reference out of a request's `params._meta`, or\n * `undefined` when there is none (the broker only adds it once a declaration\n * was accepted). With mcp-core 1.4.0, pass `request?.meta` from the context\n * an adapter receives.\n */\nexport function callerReferenceOf(meta: Readonly<Record<string, unknown>> | undefined): ICallerReference | undefined {\n const value = meta?.[CALLER_META_KEY];\n if (typeof value !== \"object\" || value === null) return undefined;\n const { ref, correlationId, traceId } = value as { ref?: unknown; correlationId?: unknown; traceId?: unknown };\n return typeof ref === \"string\" && typeof correlationId === \"string\" ? { ref, correlationId, ...(typeof traceId === \"string\" ? { traceId } : {}) } : undefined;\n}\n\n/**\n * Engineering limits of one resource. They hold for every caller, and the\n * broker returns them with each allow on that resource as\n * `obligations.constraints`, for the provider to apply.\n */\nexport interface IResourceLimits {\n readonly minValue?: number;\n readonly maxValue?: number;\n readonly allowedValues?: readonly (string | number | boolean | null)[];\n readonly destinations?: readonly string[];\n}\n\n/** One resource of a declaration: the provider's own identifier, and the path the broker evaluates. */\nexport interface IDeclaredResource {\n readonly resource: string;\n readonly resourcePath: string;\n readonly effect?: string;\n readonly limits?: IResourceLimits;\n}\n\n/** `broker/authorization/declare` parameters. Describes; grants nothing. */\nexport interface IAuthorizationDeclaration {\n readonly budgetUnits?: readonly string[];\n readonly version: string;\n readonly domain: string;\n readonly namespace: { readonly resource: string };\n readonly capabilities: readonly string[];\n readonly resources?: readonly IDeclaredResource[];\n readonly protects?: readonly string[];\n /**\n * Declared capabilities whose allowed decisions this provider promises to\n * report with {@link BrokerClient.reportResult}. One not reported in time\n * shows up in `broker_diagnose`.\n */\n readonly resultsRequired?: readonly string[];\n}\n\nexport interface IDeclarationAccepted {\n readonly accepted: true;\n readonly version: string;\n readonly policyVersion: string;\n}\n\n/** One question: may (the caller) do `capability` on this resource? */\nexport interface IAuthorizationCheck {\n readonly capability: string;\n readonly resource: string;\n readonly resourcePath: string;\n readonly attributes?: Readonly<Record<string, unknown>>;\n}\n\n/** `broker/authorize` parameters. */\nexport interface IAuthorizationQuery {\n /** On whose behalf: the caller of a pending request, or the provider itself. Never an identity. */\n readonly principal: { readonly type: \"caller-ref\"; readonly ref: string } | { readonly type: \"provider\" };\n /** Only for `{ type: \"provider\" }`; a caller reference carries its own. */\n readonly correlationId?: string;\n /** Optional W3C trace id for provider-initiated work. */\n readonly traceId?: string;\n readonly checks: readonly IAuthorizationCheck[];\n}\n\n/** What an allow comes with. */\nexport interface IAuthorizationObligations {\n /** The declared limits of the resource. Apply them right before executing. */\n readonly constraints?: IResourceLimits;\n}\n\nexport interface IAuthorizationDecision {\n readonly decisionId: string;\n /** `allow-with-constraints`: allowed, within `obligations`. */\n readonly effect: \"allow\" | \"deny\" | \"allow-with-constraints\";\n /**\n * `true` only for an unconditional `allow`. A provider that reads only\n * this field therefore refuses a constrained allow rather than ignoring\n * its constraints; read `effect` to apply them.\n */\n readonly allowed: boolean;\n readonly reason: string;\n readonly policies?: readonly string[];\n readonly obligations?: IAuthorizationObligations;\n}\n\n/** `broker/audit/result` parameters: the outcome of what a decision allowed or refused. */\nexport interface IAuditResult {\n /** The `decisionId` the broker returned. */\n readonly decisionId: string;\n readonly result: \"success\" | \"failure\" | \"refused\";\n /** The protocol's own status (`Good`, an exception code, ...). */\n readonly nativeStatus?: string;\n /** The provider's error code, when it failed or refused. */\n readonly errorCode?: string;\n}\n\nexport interface IAuthorizationAnswer {\n readonly policyVersion: string;\n /** One per check, in the same order. */\n readonly decisions: readonly IAuthorizationDecision[];\n}\n\n/** The broker refused a request, or never answered it. */\nexport class BrokerRequestError extends Error {\n constructor(\n message: string,\n /** JSON-RPC error code; `undefined` for a timeout or a closed socket. */\n public readonly code?: number,\n /** The error's `data`, e.g. `{ errors: [...] }` for a refused declaration. */\n public readonly data?: unknown\n ) {\n super(message);\n this.name = \"BrokerRequestError\";\n }\n}\n\nexport interface IBrokerClientOptions {\n /**\n * Rejects a request the broker did not answer within this many ms. Off by\n * default: a broker from 1.4.1 on answers every request at once, refusals\n * included, so waiting is never the normal path. Set it only to talk to an\n * older broker, which drops what it does not know.\n */\n readonly requestTimeoutMs?: number;\n}\n\ninterface IWaiting {\n readonly resolve: (result: unknown) => void;\n readonly reject: (error: BrokerRequestError) => void;\n readonly timer: ReturnType<typeof setTimeout> | null;\n}\n\n/** Ids of the provider's own requests; never confused with a client's, which the broker numbers `brk-N`. */\nconst ID_PREFIX = \"provider-broker-\";\n\n/**\n * The `broker/*` methods of one provider slot. Reached as `transport.broker`\n * on {@link DirectTransport} and {@link MultiplexTransport}; the transport\n * routes the broker's answers here before anything reaches the MCP server.\n */\n\nexport interface IBudgetReservationQuery extends Pick<IAuthorizationCheck, \"capability\" | \"resource\" | \"resourcePath\"> {\n readonly principal: { readonly type: \"caller-ref\"; readonly ref: string };\n readonly unit: string;\n readonly quantity: number;\n readonly idempotencyKey: string;\n}\nexport interface IBudgetReservation {\n readonly reservationId: string;\n readonly expiresAt: number;\n readonly quantity: number;\n readonly decisionId: string;\n readonly replayed: boolean;\n /** `\"allow-with-constraints\"` when the resource has declared engineering limits (broker 1.6.1 and later). */\n readonly effect?: \"allow\" | \"allow-with-constraints\";\n /** The constraints the native work must respect, e.g. `{ minValue, maxValue }`. Apply them before acting. */\n readonly obligations?: IAuthorizationDecision[\"obligations\"];\n}\nexport interface IBudgetSettlement {\n readonly reservationId: string;\n readonly used: number;\n readonly result: \"success\" | \"failure\" | \"refused\";\n}\n\nexport class BrokerClient {\n private readonly _write: (frame: string) => void;\n private readonly _writeTelemetry: (frame: string) => boolean;\n private readonly _timeoutMs: number;\n private readonly _waiting = new Map<string, IWaiting>();\n private _next = 1;\n\n constructor(write: (frame: string) => void, options: IBrokerClientOptions = {}, writeTelemetry?: (frame: string) => boolean) {\n this._write = write;\n this._writeTelemetry =\n writeTelemetry ??\n ((frame) => {\n write(frame);\n return true;\n });\n this._timeoutMs = Math.max(0, options.requestTimeoutMs ?? 0);\n }\n\n /**\n * Declares this provider's authorization domain. Resolves when the broker\n * accepted it; rejects with a {@link BrokerRequestError} whose `data.errors`\n * lists every problem otherwise. Until it resolves, serve nothing that\n * needs a decision.\n */\n declare(declaration: IAuthorizationDeclaration): Promise<IDeclarationAccepted> {\n return this._request(\"broker/authorization/declare\", declaration) as Promise<IDeclarationAccepted>;\n }\n\n reserveBudget(query: IBudgetReservationQuery): Promise<IBudgetReservation> {\n return this._request(\"broker/budget/reserve\", query) as Promise<IBudgetReservation>;\n }\n\n settleBudget(report: IBudgetSettlement): Promise<{ readonly settled: true }> {\n return this._request(\"broker/budget/settle\", report) as Promise<{ readonly settled: true }>;\n }\n\n /** Executes only a fresh reservation. A retry must never repeat native work. */\n async withBudget<T>(query: IBudgetReservationQuery, work: (grant: IBudgetReservation) => Promise<T>): Promise<T> {\n const grant = await this.reserveBudget(query);\n if (grant.replayed || Date.now() >= grant.expiresAt) throw new Error(\"Budget reservation was replayed or expired; native work was not started\");\n let value: T;\n try {\n value = await work(grant);\n } catch (error) {\n // A failed operation may already have emitted all reserved work.\n try {\n await this.settleBudget({ reservationId: grant.reservationId, used: grant.quantity, result: \"failure\" });\n } catch {\n /* debit remains */\n }\n throw error;\n }\n await this.settleBudget({ reservationId: grant.reservationId, used: grant.quantity, result: \"success\" });\n return value;\n }\n\n /** Asks for one decision per check. */\n authorize(query: IAuthorizationQuery): Promise<IAuthorizationAnswer> {\n return this._request(\"broker/authorize\", query) as Promise<IAuthorizationAnswer>;\n }\n\n /**\n * Reports what happened after a decision, so the broker's audit shows the\n * outcome next to the decision. A notification: nothing comes back, and a\n * report the broker cannot match is counted on its side.\n */\n reportResult(report: IAuditResult): void {\n if (typeof report?.decisionId !== \"string\" || report.decisionId.length === 0) throw new TypeError(\"decisionId must be the non-empty id the broker returned\");\n if (report.result !== \"success\" && report.result !== \"failure\" && report.result !== \"refused\") throw new TypeError('result must be \"success\", \"failure\" or \"refused\"');\n const params: IAuditResult = {\n decisionId: report.decisionId,\n result: report.result,\n ...(report.nativeStatus !== undefined ? { nativeStatus: report.nativeStatus } : {}),\n ...(report.errorCode !== undefined ? { errorCode: report.errorCode } : {}),\n };\n this._write(JSON.stringify({ jsonrpc: \"2.0\", method: AUDIT_RESULT_NOTIFICATION_METHOD, params }));\n }\n\n /** Emits one complete provider span, or returns false when the link is down. */\n span(span: ITelemetrySpan): boolean {\n if (!validSpan(span)) throw new TypeError(\"span is not a valid broker telemetry span\");\n return this._writeTelemetry(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: TELEMETRY_NOTIFICATION_METHOD,\n params: { version: 1, signal: \"traces\", span },\n })\n );\n }\n\n /** Number of requests still waiting for the broker. */\n get pendingCount(): number {\n return this._waiting.size;\n }\n\n /**\n * Consumes a frame when it answers one of this client's requests. Returns\n * `true` when it did, and the frame must not reach the MCP server.\n * @internal Called by the transports.\n */\n handleIncoming(frame: string): boolean {\n if (this._waiting.size === 0 || !frame.includes(ID_PREFIX)) return false;\n let message: { id?: unknown; method?: unknown; result?: unknown; error?: { code?: unknown; message?: unknown; data?: unknown } };\n try {\n message = JSON.parse(frame) as typeof message;\n } catch {\n return false;\n }\n if (typeof message.id !== \"string\" || message.method !== undefined) return false;\n const waiting = this._waiting.get(message.id);\n if (!waiting) return false;\n this._waiting.delete(message.id);\n if (waiting.timer) clearTimeout(waiting.timer);\n if (message.error) {\n waiting.reject(\n new BrokerRequestError(String(message.error.message ?? \"broker error\"), typeof message.error.code === \"number\" ? message.error.code : undefined, message.error.data)\n );\n } else {\n waiting.resolve(message.result);\n }\n return true;\n }\n\n /**\n * Fails every waiting request: the socket they went out on is gone, and a\n * reconnected one will not carry their answers.\n * @internal Called by the transports.\n */\n rejectAll(reason: string): void {\n for (const [id, waiting] of this._waiting) {\n if (waiting.timer) clearTimeout(waiting.timer);\n waiting.reject(new BrokerRequestError(`${reason} (request ${id})`));\n }\n this._waiting.clear();\n }\n\n private _request(method: string, params: unknown): Promise<unknown> {\n const id = `${ID_PREFIX}${this._next++}`;\n return new Promise((resolve, reject) => {\n const timer =\n this._timeoutMs > 0\n ? setTimeout(() => {\n this._waiting.delete(id);\n reject(\n new BrokerRequestError(\n `The broker did not answer ${method} within ${this._timeoutMs}ms. A broker older than 1.4.1 drops methods it does not know; ${method} needs broker 1.5.0 or later.`\n )\n );\n }, this._timeoutMs)\n : null;\n this._waiting.set(id, { resolve, reject, timer });\n this._write(JSON.stringify({ jsonrpc: \"2.0\", id, method, params }));\n });\n }\n}\n","/**\n * Internal plumbing shared by the two tunnel transports.\n *\n * Three concerns live here, all of them about the same thing: a tunnel that\n * misbehaves must say so instead of going quiet.\n *\n * - {@link PendingFrames}, the bounded outbound queue that covers the window\n * between `connect()` and `open` (and, for the multiplexed socket, the whole\n * reconnect back-off). Without it every frame written in that window is\n * discarded with no error and no log, and the peer simply never answers.\n * - {@link ThrottledNotice}, so a mis-wired tunnel reports itself once in full\n * rather than once per frame forever.\n * - The URL guards, which catch the single most common wiring mistake: a\n * transport pointed at the endpoint the *other* transport speaks to.\n *\n * Nothing here is exported from the package root: it is internal to the\n * transports, which are the only things that can produce these situations.\n */\n\n// ---------------------------------------------------------------------------\n// Outbound frame queue\n// ---------------------------------------------------------------------------\n\n/**\n * How many outbound frames a transport holds while its socket is not open.\n *\n * Sized for a handshake, not for a backlog: an MCP server writes a handful of\n * frames before its transport reports open (an `initialize` result, a couple of\n * `list_changed` notifications), and anything beyond that is a symptom rather\n * than traffic worth keeping. A cap also matters because the multiplexed socket\n * queues across a reconnect back-off of up to 30 seconds, during which an\n * unbounded queue would grow without limit.\n */\nexport const PENDING_FRAME_LIMIT = 64;\n\n/**\n * A bounded FIFO of frames waiting for a socket to open.\n *\n * Overflow drops the *oldest* frame, because the newest one is the one still\n * worth answering, and says so on the console: a dropped request never gets a\n * response, so the caller would otherwise wait forever on a frame nothing sent.\n */\nexport class PendingFrames {\n private readonly _label: string;\n private readonly _limit: number;\n private _frames: string[] = [];\n\n /**\n * @param label Identifies the owner in log lines, e.g. `DirectTransport ws://host/provider/x`.\n * @param limit Maximum queued frames, defaults to {@link PENDING_FRAME_LIMIT}.\n */\n constructor(label: string, limit: number = PENDING_FRAME_LIMIT) {\n this._label = label;\n this._limit = Math.max(1, limit);\n }\n\n /** Number of frames currently waiting. */\n get size(): number {\n return this._frames.length;\n }\n\n /** Queues one frame, evicting the oldest with a warning when full. */\n push(frame: string): void {\n if (this._frames.length >= this._limit) {\n const dropped = this._frames.shift();\n console.warn(\n `[mcp-provider] ${this._label}: outbound queue full at ${this._limit} frames, dropping the oldest (${describeFrame(dropped)}). ` +\n `The socket is still connecting or reconnecting. Nothing resends a dropped frame, so a request lost here never gets a response.`\n );\n }\n this._frames.push(frame);\n }\n\n /** Hands back everything queued and empties the queue. */\n drain(): string[] {\n const frames = this._frames;\n this._frames = [];\n return frames;\n }\n\n /** Empties the queue, returning how many frames were discarded. */\n clear(): number {\n const discarded = this._frames.length;\n this._frames = [];\n return discarded;\n }\n}\n\n/**\n * Best-effort one-line description of a frame, for a log line about losing it.\n *\n * Reads through a tunnel envelope when there is one, so a multiplexed frame\n * reports the JSON-RPC method the caller recognizes rather than the wrapper.\n * Never throws: it is only ever called from an error path.\n */\nexport function describeFrame(frame: string | undefined): string {\n if (frame === undefined) return \"an empty frame\";\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(frame);\n } catch {\n return `${frame.length} bytes that are not JSON`;\n }\n\n let message = parsed;\n if (typeof message === \"object\" && message !== null && \"payload\" in message) {\n message = (message as { payload: unknown }).payload;\n }\n if (typeof message !== \"object\" || message === null) return `${frame.length} bytes`;\n\n const { method, id } = message as { method?: unknown; id?: unknown };\n const methodPart = typeof method === \"string\" ? `method \"${method}\"` : \"no method\";\n const idPart = id === undefined ? \"no id\" : `id ${JSON.stringify(id)}`;\n return `${methodPart}, ${idPart}`;\n}\n\n/** How much of an offending frame a diagnostic quotes back. */\nconst QUOTED_FRAME_LIMIT = 200;\n\n/**\n * Quotes a frame for a log line, shortened so one oversized message cannot fill\n * a console. The reader needs enough to recognize the shape, not the payload.\n */\nexport function truncate(raw: string, limit: number = QUOTED_FRAME_LIMIT): string {\n return raw.length <= limit ? raw : `${raw.slice(0, limit)}... (${raw.length} bytes total)`;\n}\n\n// ---------------------------------------------------------------------------\n// Console throttling\n// ---------------------------------------------------------------------------\n\n/**\n * One in how many repeats of the same diagnostic reaches the console.\n *\n * A mis-wired tunnel does not fail once, it fails on every frame. Logging each\n * occurrence would bury the first, most useful message under thousands of\n * copies of itself, which is how a browser console becomes unreadable.\n */\nexport const NOTICE_SAMPLE_RATE = 50;\n\n/**\n * Counts occurrences of one diagnostic on one socket and decides which of them\n * are logged: the first always, then one in every {@link NOTICE_SAMPLE_RATE}.\n *\n * Only console output is sampled. Callbacks such as `onError` still fire on\n * every occurrence, so nothing an application observes changes.\n */\nexport class ThrottledNotice {\n private readonly _sampleRate: number;\n private _count = 0;\n\n constructor(sampleRate: number = NOTICE_SAMPLE_RATE) {\n this._sampleRate = Math.max(1, sampleRate);\n }\n\n /** Occurrences reported so far, logged or suppressed. */\n get count(): number {\n return this._count;\n }\n\n /** Records one occurrence and answers whether it should be logged. */\n hit(): boolean {\n this._count++;\n return this._count === 1 || this._count % this._sampleRate === 0;\n }\n\n /**\n * Suffix naming how many occurrences the current line stands for.\n *\n * Empty on the first occurrence, so the message a reader is meant to act on\n * arrives unadorned.\n */\n suffix(): string {\n return this._count <= 1 ? \"\" : ` [occurrence ${this._count} on this socket, logging one in ${this._sampleRate}]`;\n }\n}\n\n// ---------------------------------------------------------------------------\n// URL guards\n// ---------------------------------------------------------------------------\n\n/** The broker's default slot-scoped provider path, `/provider/<name>`. */\nexport const DEFAULT_PROVIDER_PATH = \"/provider\";\n\n/** The broker's default shared multiplex path, on which envelopes are spoken. */\nexport const DEFAULT_MULTIPLEX_PATH = \"/providers\";\n\n/**\n * Appended to both guards. The broker's paths are configurable\n * (`withProviderPath` / `withProvidersPath`), so the heuristic can legitimately\n * be wrong, and a warning that cannot be dismissed is a warning people learn to\n * ignore.\n */\nconst RECONFIGURED_HINT = \"If you have reconfigured the broker's paths.provider / paths.providers, ignore this warning.\";\n\n/** Parses a WebSocket URL, or `undefined` when it is not a URL at all. */\nfunction parseWsUrl(wsUrl: string): URL | undefined {\n try {\n // `ws:` and `wss:` are special schemes for the URL parser, so host and\n // pathname come out exactly as they would for `http:`.\n return new URL(wsUrl);\n } catch {\n return undefined;\n }\n}\n\n/**\n * The same URL with its path replaced, so a suggestion can be pasted as-is.\n *\n * Assembled by hand rather than through the `pathname` setter, which percent-\n * encodes: a suggested path carrying a `<name>` placeholder would come back as\n * `%3Cname%3E` and read as a typo.\n */\nfunction withPath(url: URL, path: string): string {\n return `${url.protocol}//${url.host}${path}${url.search}`;\n}\n\n/**\n * Warns when a {@link DirectTransport} is aimed at the shared multiplex base.\n *\n * This is one half of the mismatch that costs an integrator a day: the broker\n * decides framing by which endpoint the socket landed on, so a plain JSON-RPC\n * frame arriving on `/providers` is not an envelope, is dropped, and nothing is\n * logged on either side.\n *\n * Warns, never throws: a false positive must not break a deployment that has\n * reconfigured its paths.\n */\nexport function warnIfMultiplexPath(wsUrl: string): void {\n const url = parseWsUrl(wsUrl);\n if (!url) return;\n\n const path = url.pathname;\n if (path !== DEFAULT_MULTIPLEX_PATH && !path.startsWith(`${DEFAULT_MULTIPLEX_PATH}/`)) return;\n\n console.warn(\n `[mcp-provider] DirectTransport is connecting to \"${wsUrl}\", whose path \"${path}\" is the broker's shared multiplex endpoint. ` +\n `That endpoint carries multiplex envelopes { provider, payload }, while DirectTransport writes plain JSON-RPC frames, so the broker will not route what this transport sends. ` +\n `Either point DirectTransport at the slot-scoped path \"${withPath(url, `${DEFAULT_PROVIDER_PATH}/<name>`)}\", or publish through MultiplexTransport.create(\"<name>\", \"${wsUrl}\"). ` +\n RECONFIGURED_HINT\n );\n}\n\n/**\n * Warns when a {@link MultiplexTransport} is aimed at a slot-scoped path.\n *\n * The mirror of {@link warnIfMultiplexPath}: envelopes sent to\n * `/provider/<name>` are taken for opaque provider messages and rebroadcast to\n * clients as notifications, so the publisher waits for answers that never come.\n */\nexport function warnIfSlotScopedPath(wsUrl: string): void {\n const url = parseWsUrl(wsUrl);\n if (!url) return;\n\n const path = url.pathname;\n if (path !== DEFAULT_PROVIDER_PATH && !path.startsWith(`${DEFAULT_PROVIDER_PATH}/`)) return;\n\n console.warn(\n `[mcp-provider] MultiplexTransport is connecting to \"${wsUrl}\", whose path \"${path}\" is a slot-scoped provider endpoint. ` +\n `That endpoint carries plain JSON-RPC frames, while MultiplexTransport writes multiplex envelopes { provider, payload }, which the broker never unwraps, so nothing sent here is answered. ` +\n `Either publish through new DirectTransport(\"${wsUrl}\"), or point MultiplexTransport at the shared multiplex base \"${withPath(url, DEFAULT_MULTIPLEX_PATH)}\". ` +\n RECONFIGURED_HINT\n );\n}\n\n/**\n * The handshake headers a transport sends: `headers`, plus `X-Provider-Token`\n * from `secret`. `undefined` when there are none, so the socket is built with\n * the one-argument constructor every runtime has.\n */\nexport function handshakeHeaders(options: { readonly secret?: string; readonly headers?: Readonly<Record<string, string>> } | undefined): Record<string, string> | undefined {\n const headers: Record<string, string> = { ...(options?.headers ?? {}) };\n if (options?.secret) headers[\"x-provider-token\"] = options.secret;\n return Object.keys(headers).length > 0 ? headers : undefined;\n}\n\n/**\n * Opens a WebSocket, with handshake headers when there are any. Headers need\n * Node's `WebSocket` (22+); a browser cannot send them, and is told so.\n */\nexport function openWebSocket(url: string, headers: Record<string, string> | undefined): WebSocket {\n // Node 20 has no global WebSocket. Said plainly, rather than surfacing as\n // a ReferenceError or as a claim that headers are unsupported.\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n `Cannot open ${url}: this runtime has no global WebSocket (Node before 22). ` +\n `Assign one before connecting: import { WebSocket } from \"ws\"; globalThis.WebSocket = WebSocket as unknown as typeof globalThis.WebSocket;`\n );\n }\n if (!headers) return new WebSocket(url);\n try {\n return new (WebSocket as unknown as new (url: string, init: { headers: Record<string, string> }) => WebSocket)(url, { headers });\n } catch (error) {\n throw new Error(\n `Cannot open ${url} with handshake headers (secret / headers): this runtime's WebSocket does not accept them. ` +\n `Node 22 and later do; a browser never does, so a browser provider cannot authenticate to the broker. ${(error as Error).message}`\n );\n }\n}\n","import type { IMessageTransport } from \"@cyanmycelium/mcp-core\";\nimport { encodeRegisterFrame } from \"./protocol/index\";\nimport { BrokerClient } from \"./broker.client\";\nimport { describeFrame, handshakeHeaders, openWebSocket, PendingFrames, ThrottledNotice, warnIfMultiplexPath } from \"./transport.support\";\n\n/** Options accepted by {@link DirectTransport}. */\nexport interface IDirectTransportOptions {\n /**\n * Join the broker's `_all` aggregate slot as well as this provider's own\n * slot, by sending the registration notification\n * `{\"jsonrpc\":\"2.0\",\"method\":\"notifications/register\",\"params\":{\"aggregate\":true}}`\n * as the first frame on the socket.\n *\n * Opt-in on purpose: `_all` exposes this provider's tools and prompts to\n * every client of the aggregate slot, so a provider that does not ask for it\n * stays reachable only on its own slot.\n *\n * ORDERING: the broker runs `initialize` against a newly aggregated provider\n * immediately, and drops it from `_all` without a word if the handshake times\n * out. The frame therefore goes out on `open`, never from the constructor, so\n * assign `onMessage` (or hand this transport to an MCP server, which assigns\n * it for you) *before* calling {@link DirectTransport.connect}. Connecting\n * first and wiring the handler afterwards loses the broker's `initialize` and\n * the provider silently never appears in `_all`.\n */\n aggregate?: boolean;\n\n /**\n * Rejects a `broker.declare()` / `broker.authorize()` the broker did not\n * answer within this many ms. Off by default: a broker from 1.4.1 on answers\n * at once. Only for an older broker, which drops methods it does not know.\n */\n brokerRequestTimeoutMs?: number;\n\n /**\n * The provider secret, sent as the `X-Provider-Token` header of the\n * WebSocket handshake. Required by a broker that authenticates providers\n * (`providerSecret`, or the security file's `providers` table, where it is\n * what gives this provider its own identity).\n *\n * **Node only.** Node's `WebSocket` (22 and later) accepts handshake\n * headers; a browser's does not, and a browser provider cannot\n * authenticate (terminate provider auth in a reverse proxy instead).\n */\n secret?: string;\n\n /** Extra handshake headers, Node only, like {@link secret}. */\n headers?: Readonly<Record<string, string>>;\n}\n\n/**\n * 1:1 WebSocket transport, wraps a single `WebSocket` connection to a broker\n * provider slot, typically `ws://<broker>/provider/<name>`.\n *\n * One server owns one socket. When an application publishes several servers\n * through the same broker, prefer {@link MultiplexTransport}, which shares a\n * single socket between them.\n *\n * This transport does **not** reconnect: when the socket closes, it stays\n * closed until the application calls {@link connect} again. Only\n * {@link MultiplexTransport}'s shared socket reconnects on its own.\n *\n * Call {@link connect} after setting the event callbacks to open the socket.\n */\nexport class DirectTransport implements IMessageTransport {\n private readonly _wsUrl: string;\n private readonly _aggregate: boolean | undefined;\n private readonly _headers: Record<string, string> | undefined;\n private readonly _pending: PendingFrames;\n\n /** Throttles the \"wrote to a closed transport\" line, which repeats per frame. */\n private readonly _afterCloseNotice = new ThrottledNotice();\n\n private _ws: WebSocket | null = null;\n private _closed = false;\n\n onMessage: ((data: string) => void) | null = null;\n onOpen: (() => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n /**\n * The broker's own methods for this slot: declaring an authorization\n * domain, asking for decisions. Its answers are taken off the socket\n * before {@link onMessage} sees anything.\n */\n readonly broker: BrokerClient;\n\n constructor(wsUrl: string, options?: IDirectTransportOptions) {\n this._wsUrl = wsUrl;\n this._aggregate = options?.aggregate;\n this._headers = handshakeHeaders(options);\n this._pending = new PendingFrames(`DirectTransport ${wsUrl}`);\n this.broker = new BrokerClient(\n (frame) => this.send(frame),\n { requestTimeoutMs: options?.brokerRequestTimeoutMs },\n (frame) => {\n if (this._ws?.readyState !== WebSocket.OPEN) return false;\n this._ws.send(frame);\n return true;\n }\n );\n }\n\n get isOpen(): boolean {\n return this._ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Opens the WebSocket connection and wires its events to the transport\n * callbacks. Must be called after assigning `onOpen` / `onMessage` / etc.\n */\n connect(): void {\n // Fires before the socket exists, so the mismatch is named even if the\n // handshake succeeds, which it does: the broker accepts the connection\n // and then drops every frame this transport writes.\n warnIfMultiplexPath(this._wsUrl);\n\n const ws = openWebSocket(this._wsUrl, this._headers);\n\n this._closed = false;\n\n // Held from construction, not from `onopen`, the way MultiplexSocket\n // already does it: everything written between `connect()` and open is\n // otherwise sent to a `null` socket and discarded without a trace.\n // Readiness is decided by `readyState`, so a connecting socket is never\n // mistaken for a usable one.\n this._ws = ws;\n\n // Every handler below starts by checking that this socket is still the\n // current one. A second `connect()` supersedes the first, and a stale\n // socket's late `onclose` would otherwise null the live one out from\n // under the server, which then goes quiet with no error anywhere.\n ws.onopen = () => {\n if (this._ws !== ws) return;\n this._sendRegistration(ws);\n this._flush(ws);\n this.onOpen?.();\n };\n\n ws.onerror = () => {\n if (this._ws !== ws) return;\n this.onError?.(new Error(`DirectTransport: WebSocket error on ${this._wsUrl}`));\n };\n\n ws.onclose = (event?: CloseEvent) => {\n if (this._ws !== ws) return;\n this._ws = null;\n\n const discarded = this._pending.clear();\n this.broker.rejectAll(`DirectTransport: the socket to ${this._wsUrl} closed before the broker answered`);\n\n // ORDER IS LOAD-BEARING: `onError` must fire before `onClose`.\n // An MCP server's `onClose` clears its running flag, after which it\n // treats a later `onError` as a pre-open failure and rejects an\n // already-settled promise, so the message is thrown away. Reporting\n // first is what makes a broker refusal (1008, with the reason in the\n // close frame) readable instead of an undifferentiated disconnect.\n const error = this._closeError(event, discarded);\n if (error) this.onError?.(new Error(error));\n\n this.onClose?.();\n };\n\n ws.onmessage = (event: MessageEvent<string>) => {\n if (this.broker.handleIncoming(event.data)) return;\n this.onMessage?.(event.data);\n };\n }\n\n send(data: string): void {\n if (this._ws?.readyState === WebSocket.OPEN) {\n this._ws.send(data);\n return;\n }\n\n if (this._closed) {\n if (this._afterCloseNotice.hit()) {\n console.warn(\n `[mcp-provider] DirectTransport ${this._wsUrl}: dropping a frame written after close() (${describeFrame(data)}). ` +\n `This transport does not reconnect, call connect() again before sending.${this._afterCloseNotice.suffix()}`\n );\n }\n return;\n }\n\n // The socket is still connecting. Queue rather than drop: an MCP server\n // writes its handshake as soon as it is told to start, and the socket is\n // usually not open yet.\n this._pending.push(data);\n }\n\n close(): void {\n this._closed = true;\n\n const discarded = this._pending.clear();\n if (discarded > 0) {\n console.warn(`[mcp-provider] DirectTransport ${this._wsUrl}: closed with ${discarded} frame(s) still queued, they were never sent.`);\n }\n\n // `_ws` is deliberately left in place: the browser fires `onclose`\n // asynchronously, and clearing it here would make the handler's staleness\n // check reject its own socket and swallow `onClose`. The handler nulls it.\n this._ws?.close();\n }\n\n /**\n * Sends the registration notification when the caller asked for a specific\n * aggregate membership. The slot name is not in the frame: on the\n * slot-scoped path the broker takes it from the URL at connect time.\n */\n private _sendRegistration(ws: WebSocket): void {\n if (this._aggregate === undefined) return;\n ws.send(encodeRegisterFrame({ aggregate: this._aggregate }));\n }\n\n /** Writes out everything queued while the socket was connecting. */\n private _flush(ws: WebSocket): void {\n for (const frame of this._pending.drain()) {\n ws.send(frame);\n }\n }\n\n /**\n * Builds the message for a close worth reporting, or `undefined` when there\n * is nothing to say.\n *\n * A code of 1000 is a normal close and stays silent. An environment that\n * calls `onclose` with no event at all leaves nothing to distinguish a\n * refusal from a clean shutdown, so that stays silent too.\n */\n private _closeError(event: CloseEvent | undefined, discarded: number): string | undefined {\n const code = event?.code;\n if (code === undefined || code === 1000) return undefined;\n\n const reason = event?.reason ? `: \"${event.reason}\"` : \" (no reason given)\";\n const hint =\n code === 1008\n ? \"Code 1008 is a policy refusal from the broker, not a network drop: the slot is already connected, is reserved, or provider authentication rejected it. The reason above is the broker's own wording.\"\n : \"DirectTransport does not reconnect, call connect() again to retry.\";\n const lost = discarded > 0 ? ` ${discarded} queued frame(s) were discarded.` : \"\";\n\n return `DirectTransport: the socket to ${this._wsUrl} closed with code ${code}${reason}. ${hint}${lost}`;\n }\n}\n","import type { IMessageTransport } from \"@cyanmycelium/mcp-core\";\nimport { decodeEnvelope, encodeEnvelope, encodeRegisterEnvelope, envelopeFrame, tunnelErrorOf } from \"./protocol/index\";\nimport { describeFrame, handshakeHeaders, openWebSocket, PendingFrames, ThrottledNotice, truncate, warnIfSlotScopedPath } from \"./transport.support\";\nimport { BrokerClient } from \"./broker.client\";\n\n/** The diagnostics one socket may repeat, counted per socket so each is said once in full. */\ninterface ISocketNotices {\n /** A frame arrived that is not an envelope at all, the classic framing mismatch. */\n readonly notEnvelope: ThrottledNotice;\n\n /** An envelope arrived for a slot no transport on this socket publishes. */\n readonly unknownProvider: ThrottledNotice;\n\n /** The broker refused something and said so in an error envelope. */\n readonly tunnelError: ThrottledNotice;\n}\n\n// ---------------------------------------------------------------------------\n// MultiplexSocket, shared WebSocket singleton (internal)\n// ---------------------------------------------------------------------------\n\n/**\n * Manages a single WebSocket connection shared by multiple {@link MultiplexTransport}\n * instances. All traffic goes through the tunnel envelope protocol, whose\n * definition lives in `./protocol` and is shared with the broker.\n *\n * Reconnection is handled centrally here, individual transports do not reconnect.\n * Use {@link getOrCreate} to obtain a per-URL singleton.\n */\nclass MultiplexSocket {\n /** Per-URL cache so all transports targeting the same tunnel share one socket. */\n private static readonly _instances = new Map<string, MultiplexSocket>();\n\n private readonly _wsUrl: string;\n /** Cache key: the URL, and the handshake headers, since two secrets are two identities and need two sockets. */\n private readonly _key: string;\n private readonly _headers: Record<string, string> | undefined;\n private readonly _transports = new Map<string, MultiplexTransport>();\n\n /** Aggregate opt-in per slot, absent when the caller did not express one. */\n private readonly _aggregates = new Map<string, boolean>();\n\n /** Frames written while the socket is connecting, reconnecting or backing off. */\n private readonly _pending: PendingFrames;\n\n private _ws: WebSocket | null = null;\n private _reconnectAttempts = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _stopped = false;\n\n /** Set once the last transport left and this instance gave up its URL. */\n private _dead = false;\n\n /** The URL guard is about the URL, not the socket, so it is said once. */\n private _pathWarned = false;\n\n /** Throttles the \"wrote to a closed tunnel\" line, which repeats per frame. */\n private readonly _afterStopNotice = new ThrottledNotice();\n\n private constructor(wsUrl: string, key: string, headers: Record<string, string> | undefined) {\n this._wsUrl = wsUrl;\n this._key = key;\n this._headers = headers;\n this._pending = new PendingFrames(`MultiplexSocket ${wsUrl}`);\n }\n\n /** Returns (or creates) the singleton socket for a given tunnel URL and handshake headers. */\n static getOrCreate(wsUrl: string, headers?: Record<string, string>): MultiplexSocket {\n const key = headers ? `${wsUrl}\\u0000${JSON.stringify(Object.entries(headers).sort())}` : wsUrl;\n let instance = MultiplexSocket._instances.get(key);\n if (!instance) {\n instance = new MultiplexSocket(wsUrl, key, headers);\n MultiplexSocket._instances.set(key, instance);\n }\n return instance;\n }\n\n get isOpen(): boolean {\n return this._ws?.readyState === WebSocket.OPEN;\n }\n\n // ── Registration ────────────────────────────────────────────────────────\n\n register(name: string, transport: MultiplexTransport, aggregate?: boolean): void {\n if (this._dead) this._revive();\n\n this._transports.set(name, transport);\n if (aggregate !== undefined) this._aggregates.set(name, aggregate);\n\n // If the shared socket is already open, announce the new provider and\n // notify the transport immediately.\n if (this.isOpen) {\n this._announceProvider(name);\n transport.onOpen?.();\n } else if (!this._ws) {\n // First registration, open the connection. A reconnect may already\n // be armed from an earlier teardown, and letting it fire as well\n // would open a rival socket that the broker refuses.\n this._cancelReconnect();\n this._stopped = false;\n this._connect();\n }\n }\n\n unregister(name: string): void {\n this._transports.delete(name);\n this._aggregates.delete(name);\n\n // Tear down the shared socket when no transports remain.\n if (this._transports.size === 0) {\n this._stopped = true;\n this._cancelReconnect();\n this._pending.clear();\n this._ws?.close();\n this._ws = null;\n\n // Marked dead as well as evicted: a MultiplexTransport captures its\n // socket at construction, so one built before the teardown can still\n // call `register()` on this instance long after `getOrCreate` started\n // handing out a different one. Without the flag that reactivation\n // silently opens a second socket to the same URL, and the broker\n // refuses whichever of the two loses the race.\n this._dead = true;\n MultiplexSocket._instances.delete(this._key);\n }\n }\n\n /**\n * Brings a torn-down instance back into service when a transport that\n * captured it is reactivated.\n *\n * Reclaims the URL when nothing else holds it. When another instance already\n * does, the two would race for the same slot names, so this says exactly that\n * rather than letting the loser fail with a refusal nobody reads.\n */\n private _revive(): void {\n this._dead = false;\n this._stopped = false;\n\n const live = MultiplexSocket._instances.get(this._key);\n if (!live) {\n MultiplexSocket._instances.set(this._key, this);\n return;\n }\n if (live !== this) {\n console.warn(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: a transport built before this tunnel was closed is being reactivated, so it will open a second socket to the same URL. ` +\n `The broker admits one socket per slot and refuses the other. Rebuild the transport with MultiplexTransport.create(name, url) instead of reusing one you closed.`\n );\n }\n }\n\n /**\n * Claims the slot for `name` so the broker eagerly creates its provider\n * state before any MCP client connects. Without it the broker only learns\n * about a provider on its first real message, and a client connecting in\n * between is told the provider is not connected.\n *\n * Carries the aggregate opt-in when the caller expressed one, which is why\n * it must go out before any traffic: the broker runs `initialize` against a\n * newly aggregated provider straight away.\n */\n private _announceProvider(name: string): void {\n if (this._ws?.readyState !== WebSocket.OPEN) return;\n\n const aggregate = this._aggregates.get(name);\n this._ws.send(aggregate === undefined ? encodeRegisterEnvelope(name) : encodeRegisterEnvelope(name, { aggregate }));\n }\n\n // ── Sending ─────────────────────────────────────────────────────────────\n\n send(provider: string, data: string): void {\n const frame = encodeEnvelope(provider, data);\n\n if (this._ws?.readyState === WebSocket.OPEN) {\n this._ws.send(frame);\n return;\n }\n\n // The tunnel was closed on purpose, so nothing will ever flush a queue.\n if (this._stopped) {\n if (this._afterStopNotice.hit()) {\n console.warn(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: dropping a frame for provider \"${provider}\" written after the tunnel was closed (${describeFrame(frame)}). ` +\n `Call connect() on a transport to reopen it.${this._afterStopNotice.suffix()}`\n );\n }\n return;\n }\n\n // Connecting, or waiting out a reconnect back-off of up to 30 seconds.\n // Dropping here is what makes a provider look alive while answering\n // nothing, so the frame waits for the socket instead.\n this._pending.push(frame);\n }\n\n /** Sends low-priority telemetry only while the shared link is open. */\n sendTelemetry(provider: string, data: string): boolean {\n if (this._ws?.readyState !== WebSocket.OPEN) return false;\n this._ws.send(encodeEnvelope(provider, data));\n return true;\n }\n\n // ── Connection lifecycle ────────────────────────────────────────────────\n\n private _connect(): void {\n if (!this._pathWarned) {\n this._pathWarned = true;\n warnIfSlotScopedPath(this._wsUrl);\n }\n\n const ws = openWebSocket(this._wsUrl, this._headers);\n\n // Counted per socket so a mismatch reports itself once in full rather\n // than once per frame for the life of the page.\n const notices: ISocketNotices = {\n notEnvelope: new ThrottledNotice(),\n unknownProvider: new ThrottledNotice(),\n tunnelError: new ThrottledNotice(),\n };\n\n // Held from construction, not from `onopen`: a transport registering\n // while the handshake is still in flight must find this socket rather\n // than open a second one. Readiness is decided by `readyState`, so a\n // connecting socket is never mistaken for a usable one.\n this._ws = ws;\n\n // Every handler below starts by checking that this socket is still the\n // current one. An orphaned socket, one superseded by a reconnect or by a\n // reactivated transport, otherwise keeps speaking for the instance: its\n // `onclose` nulls `_ws` and fires `onClose` on every transport while a\n // newer socket is live, after which `isOpen` reads false, an MCP server\n // gates every send on it, and the provider goes silent on a working\n // tunnel with nothing logged anywhere.\n ws.onopen = () => {\n if (this._ws !== ws) return;\n\n this._reconnectAttempts = 0;\n // Announce all registered providers to the broker so it eagerly\n // creates their slots before any MCP client connects.\n for (const name of this._transports.keys()) {\n this._announceProvider(name);\n }\n // Then whatever was written while the socket was down, after the\n // registrations and before the transports are told they are open, so\n // the broker sees the slots claimed before any traffic on them.\n this._flush(ws);\n for (const transport of this._transports.values()) {\n transport.onOpen?.();\n }\n };\n\n ws.onerror = () => {\n if (this._ws !== ws) return;\n\n for (const transport of this._transports.values()) {\n transport.onError?.(new Error(`MultiplexSocket: WebSocket error on ${this._wsUrl}`));\n }\n };\n\n ws.onclose = () => {\n if (this._ws !== ws) return;\n\n this._ws = null;\n for (const transport of this._transports.values()) {\n transport.broker.rejectAll(`MultiplexTransport: the shared socket to ${this._wsUrl} closed before the broker answered`);\n transport.onClose?.();\n }\n if (!this._stopped) {\n this._scheduleReconnect();\n }\n };\n\n ws.onmessage = (event: MessageEvent<string>) => {\n if (this._ws !== ws) return;\n this._routeIncoming(event.data, notices);\n };\n }\n\n /** Writes out everything queued while the socket was down. */\n private _flush(ws: WebSocket): void {\n for (const frame of this._pending.drain()) {\n ws.send(frame);\n }\n }\n\n private _routeIncoming(raw: string, notices: ISocketNotices): void {\n const envelope = decodeEnvelope(raw);\n if (!envelope) {\n // The single most expensive silent failure in this stack, and the\n // one the field report lost a day to. The broker decides framing\n // from the endpoint the socket landed on, so a slot-scoped path\n // answers in plain JSON-RPC, which is not an envelope and used to be\n // dropped here without a word while the tunnel looked healthy.\n if (notices.notEnvelope.hit()) {\n console.error(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: dropped an incoming frame that is not a tunnel envelope { provider, payload }: ${truncate(raw)}. ` +\n `Only the broker's shared multiplex base (/providers) speaks envelopes. A slot-scoped path (/provider/<name>) carries plain JSON-RPC and needs new DirectTransport(url) instead. ` +\n `If this URL is already the multiplex base, the frame came from something else writing on the socket, such as a proxy error page.${notices.notEnvelope.suffix()}`\n );\n }\n return;\n }\n\n const transport = this._transports.get(envelope.provider);\n if (!transport) {\n if (notices.unknownProvider.hit()) {\n const known = [...this._transports.keys()].map((name) => `\"${name}\"`).join(\", \") || \"none\";\n console.error(\n `[mcp-provider] MultiplexSocket ${this._wsUrl}: dropped an envelope for provider \"${envelope.provider}\", which no transport on this socket publishes. Registered here: ${known}. ` +\n `Check that the slot name passed to MultiplexTransport.create matches the one the broker routes to.${notices.unknownProvider.suffix()}`\n );\n }\n return;\n }\n\n // A tunnel-level refusal (a rejected slot, an unavailable provider)\n // carries no request id. Handing it to an MCP server would get it\n // classified as an unknown notification and dropped without a word, so\n // surface it as a transport error instead.\n const payload = envelope.payload as { id?: unknown } | null;\n if (payload !== null && (payload.id === null || payload.id === undefined)) {\n const error = tunnelErrorOf(envelope.payload);\n if (error) {\n const message = `Tunnel error ${error.code} on provider \"${envelope.provider}\": ${error.message}`;\n transport.onError?.(new Error(message));\n\n // `onError` alone is not enough to be heard: an MCP server\n // overwrites it when it starts and only reports through it while\n // it is not yet running, and this socket reports itself open\n // before any refusal can arrive. So the console is the only place\n // a browser-hosted provider learns it was refused.\n if (notices.tunnelError.hit()) {\n console.error(`[mcp-provider] ${message}${notices.tunnelError.suffix()}`);\n }\n return;\n }\n }\n\n transport._receive(envelopeFrame(envelope));\n }\n\n private _scheduleReconnect(): void {\n const base = 1_000;\n const max = 30_000;\n const jitter = 0.5 + Math.random() * 0.5;\n const delay = Math.min(base * 2 ** this._reconnectAttempts, max) * jitter;\n\n this._reconnectAttempts++;\n this._reconnectTimer = setTimeout(() => {\n this._reconnectTimer = null;\n // A registration during the back-off may already have opened a\n // socket. Reconnecting on top of it would leave two live sockets\n // announcing the same slots, one of which the broker refuses.\n if (this._stopped || this._ws) return;\n this._connect();\n }, delay);\n }\n\n /** Disarms a pending reconnect, so nothing opens a socket behind our back. */\n private _cancelReconnect(): void {\n if (this._reconnectTimer === null) return;\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// MultiplexTransport, per-server transport (public)\n// ---------------------------------------------------------------------------\n\n/** Options accepted by {@link MultiplexTransport.create}. */\nexport interface IMultiplexTransportOptions {\n /**\n * Join the broker's `_all` aggregate slot as well as this provider's own\n * slot, by carrying `params: { aggregate: true }` on the registration\n * notification the shared socket sends when it claims the slot.\n *\n * Opt-in on purpose: `_all` exposes this provider's tools and prompts to\n * every client of the aggregate slot, so a provider that does not ask for it\n * stays reachable only on its own slot.\n *\n * ORDERING: the broker runs `initialize` against a newly aggregated provider\n * immediately, and drops it from `_all` without a word if the handshake times\n * out. The registration goes out from {@link MultiplexTransport.connect}, so\n * assign `onMessage` (or hand this transport to an MCP server, which assigns\n * it for you) *before* connecting. Connecting first and wiring the handler\n * afterwards loses the broker's `initialize`, and the provider silently never\n * appears in `_all`.\n */\n aggregate?: boolean;\n\n /**\n * Rejects a `broker.declare()` / `broker.authorize()` the broker did not\n * answer within this many ms. Off by default: a broker from 1.4.1 on answers\n * at once. Only for an older broker, which drops methods it does not know.\n */\n brokerRequestTimeoutMs?: number;\n\n /**\n * The provider secret, sent as the `X-Provider-Token` header of the\n * WebSocket handshake. Required by a broker that authenticates providers\n * (`providerSecret`, or the security file's `providers` table, where it is\n * what gives this provider its own identity).\n *\n * **Node only.** Node's `WebSocket` (22 and later) accepts handshake\n * headers; a browser's does not, and a browser provider cannot\n * authenticate (terminate provider auth in a reverse proxy instead).\n */\n secret?: string;\n\n /** Extra handshake headers, Node only, like {@link secret}. */\n headers?: Readonly<Record<string, string>>;\n}\n\n/**\n * A transport that multiplexes multiple MCP servers over a single shared\n * WebSocket connection using the envelope protocol `{ provider, payload }`.\n *\n * Use the static {@link create} factory to obtain an instance:\n * ```typescript\n * const t1 = MultiplexTransport.create(\"scene-1\", \"ws://localhost:3000/providers\");\n * const t2 = MultiplexTransport.create(\"scene-2\", \"ws://localhost:3000/providers\");\n * // t1 and t2 share a single WebSocket under the hood.\n * ```\n */\nexport class MultiplexTransport implements IMessageTransport {\n private readonly _name: string;\n private readonly _socket: MultiplexSocket;\n private readonly _aggregate: boolean | undefined;\n private _registered = false;\n\n onMessage: ((data: string) => void) | null = null;\n onOpen: (() => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n /**\n * The broker's own methods for this slot: declaring an authorization\n * domain, asking for decisions. Its answers are taken off the socket\n * before {@link onMessage} sees anything.\n */\n readonly broker: BrokerClient;\n\n constructor(name: string, socket: MultiplexSocket, options?: IMultiplexTransportOptions) {\n this._name = name;\n this._socket = socket;\n this._aggregate = options?.aggregate;\n this.broker = new BrokerClient(\n (frame) => this.send(frame),\n { requestTimeoutMs: options?.brokerRequestTimeoutMs },\n (frame) => this._socket.sendTelemetry(this._name, frame)\n );\n }\n\n /**\n * One frame from the broker for this slot: the broker's answer to one of\n * {@link broker}'s requests, or MCP traffic for the server.\n * @internal Called by the shared socket.\n */\n _receive(frame: string): void {\n if (this.broker.handleIncoming(frame)) return;\n this.onMessage?.(frame);\n }\n\n /**\n * Convenience factory: creates a {@link MultiplexTransport} backed by a\n * shared {@link MultiplexSocket} for the given tunnel URL.\n *\n * Transports targeting the same `wsUrl` automatically share one WebSocket.\n *\n * @param wsUrl The broker's shared multiplex base, `ws://<broker>/providers`.\n * A slot-scoped `/provider/<name>` URL belongs to\n * {@link DirectTransport} and is warned about on connect.\n */\n static create(name: string, wsUrl: string, options?: IMultiplexTransportOptions): MultiplexTransport {\n return new MultiplexTransport(name, MultiplexSocket.getOrCreate(wsUrl, handshakeHeaders(options)), options);\n }\n\n get isOpen(): boolean {\n return this._socket.isOpen;\n }\n\n /**\n * Registers this transport with the shared socket.\n *\n * Safe to call multiple times, subsequent calls are no-ops.\n */\n activate(): void {\n if (!this._registered) {\n this._registered = true;\n this._socket.register(this._name, this, this._aggregate);\n }\n }\n\n /**\n * Opens the transport, the same way every other transport does.\n *\n * An alias of {@link activate} so callers never have to special-case this\n * class: an MCP server or client just calls `connect()` on whatever\n * transport it was handed.\n */\n connect(): void {\n this.activate();\n }\n\n send(data: string): void {\n this._socket.send(this._name, data);\n }\n\n close(): void {\n if (this._registered) {\n this._registered = false;\n this._socket.unregister(this._name);\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanmycelium/mcp-broker-provider",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Provider side of the CyanMycelium MCP broker tunnel: publish an MCP server to a broker slot over the shared envelope protocol.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -163,6 +163,7 @@ export interface IDeclaredResource {
163
163
 
164
164
  /** `broker/authorization/declare` parameters. Describes; grants nothing. */
165
165
  export interface IAuthorizationDeclaration {
166
+ readonly budgetUnits?: readonly string[];
166
167
  readonly version: string;
167
168
  readonly domain: string;
168
169
  readonly namespace: { readonly resource: string };
@@ -278,6 +279,30 @@ const ID_PREFIX = "provider-broker-";
278
279
  * on {@link DirectTransport} and {@link MultiplexTransport}; the transport
279
280
  * routes the broker's answers here before anything reaches the MCP server.
280
281
  */
282
+
283
+ export interface IBudgetReservationQuery extends Pick<IAuthorizationCheck, "capability" | "resource" | "resourcePath"> {
284
+ readonly principal: { readonly type: "caller-ref"; readonly ref: string };
285
+ readonly unit: string;
286
+ readonly quantity: number;
287
+ readonly idempotencyKey: string;
288
+ }
289
+ export interface IBudgetReservation {
290
+ readonly reservationId: string;
291
+ readonly expiresAt: number;
292
+ readonly quantity: number;
293
+ readonly decisionId: string;
294
+ readonly replayed: boolean;
295
+ /** `"allow-with-constraints"` when the resource has declared engineering limits (broker 1.6.1 and later). */
296
+ readonly effect?: "allow" | "allow-with-constraints";
297
+ /** The constraints the native work must respect, e.g. `{ minValue, maxValue }`. Apply them before acting. */
298
+ readonly obligations?: IAuthorizationDecision["obligations"];
299
+ }
300
+ export interface IBudgetSettlement {
301
+ readonly reservationId: string;
302
+ readonly used: number;
303
+ readonly result: "success" | "failure" | "refused";
304
+ }
305
+
281
306
  export class BrokerClient {
282
307
  private readonly _write: (frame: string) => void;
283
308
  private readonly _writeTelemetry: (frame: string) => boolean;
@@ -306,6 +331,34 @@ export class BrokerClient {
306
331
  return this._request("broker/authorization/declare", declaration) as Promise<IDeclarationAccepted>;
307
332
  }
308
333
 
334
+ reserveBudget(query: IBudgetReservationQuery): Promise<IBudgetReservation> {
335
+ return this._request("broker/budget/reserve", query) as Promise<IBudgetReservation>;
336
+ }
337
+
338
+ settleBudget(report: IBudgetSettlement): Promise<{ readonly settled: true }> {
339
+ return this._request("broker/budget/settle", report) as Promise<{ readonly settled: true }>;
340
+ }
341
+
342
+ /** Executes only a fresh reservation. A retry must never repeat native work. */
343
+ async withBudget<T>(query: IBudgetReservationQuery, work: (grant: IBudgetReservation) => Promise<T>): Promise<T> {
344
+ const grant = await this.reserveBudget(query);
345
+ if (grant.replayed || Date.now() >= grant.expiresAt) throw new Error("Budget reservation was replayed or expired; native work was not started");
346
+ let value: T;
347
+ try {
348
+ value = await work(grant);
349
+ } catch (error) {
350
+ // A failed operation may already have emitted all reserved work.
351
+ try {
352
+ await this.settleBudget({ reservationId: grant.reservationId, used: grant.quantity, result: "failure" });
353
+ } catch {
354
+ /* debit remains */
355
+ }
356
+ throw error;
357
+ }
358
+ await this.settleBudget({ reservationId: grant.reservationId, used: grant.quantity, result: "success" });
359
+ return value;
360
+ }
361
+
309
362
  /** Asks for one decision per check. */
310
363
  authorize(query: IAuthorizationQuery): Promise<IAuthorizationAnswer> {
311
364
  return this._request("broker/authorize", query) as Promise<IAuthorizationAnswer>;
package/src/index.ts CHANGED
@@ -30,6 +30,9 @@ export {
30
30
  type IAuthorizationObligations,
31
31
  type IAuthorizationQuery,
32
32
  type IBrokerClientOptions,
33
+ type IBudgetReservationQuery,
34
+ type IBudgetReservation,
35
+ type IBudgetSettlement,
33
36
  type ICallerReference,
34
37
  type IDeclarationAccepted,
35
38
  type IDeclaredResource,