@cyanmycelium/mcp-broker-provider 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -95,6 +95,106 @@ Either form sends the registration notification with `params: { aggregate: true
95
95
 
96
96
  **Wire the message handler before you connect.** The broker runs `initialize` against a newly aggregated provider immediately, and a provider that does not answer is dropped from `_all` silently. Handing the transport to an MCP server does this for you, since the server assigns `onMessage` before calling `connect()`. Assigning it yourself, after connecting, loses the handshake and the provider never appears in the aggregate.
97
97
 
98
+ ## Letting the broker decide (broker 1.5.0)
99
+
100
+ A provider that serves its own kind of resource can declare an authorization
101
+ domain and ask the broker for decisions, instead of carrying a policy of its
102
+ own. Both transports expose the broker's methods as `transport.broker`:
103
+
104
+ ```ts
105
+ import { DirectTransport, callerReferenceOf } from "@cyanmycelium/mcp-broker-provider";
106
+
107
+ const transport = new DirectTransport("ws://broker:3000/provider/scada");
108
+ // ... start the MCP server on it, then:
109
+ await transport.broker.declare({
110
+ version: "2026-10-01.1",
111
+ domain: "scada",
112
+ namespace: { resource: "/production/site1" },
113
+ capabilities: ["scada.observe", "scada.control"],
114
+ resources: [
115
+ {
116
+ resource: "uns://production/site1/line1/motor01/speed_sp",
117
+ resourcePath: "/production/site1/line1/motor01/speed_sp",
118
+ limits: { minValue: 0, maxValue: 1500 },
119
+ },
120
+ ],
121
+ resultsRequired: ["scada.control"],
122
+ });
123
+
124
+ // In a tool handler (mcp-core 1.4.0 hands the adapter the request's _meta):
125
+ const caller = callerReferenceOf(request?.meta);
126
+ const { decisions } = await transport.broker.authorize({
127
+ principal: { type: "caller-ref", ref: caller!.ref },
128
+ checks: [{ capability: "scada.control", resource: "uns://production/site1/line1/motor01/speed_sp", resourcePath: "/production/site1/line1/motor01/speed_sp" }],
129
+ });
130
+ // decisions[0]: { effect: "allow-with-constraints", allowed: false, obligations: { constraints: { minValue: 0, maxValue: 1500 } }, ... }
131
+
132
+ // Once the write is done, or refused by a constraint:
133
+ transport.broker.reportResult({ decisionId: decisions[0].decisionId, result: "success", nativeStatus: "Good" });
134
+ ```
135
+
136
+ - `effect` is `allow`, `deny` or `allow-with-constraints`. The last one comes
137
+ with the resource's declared limits in `obligations.constraints`; apply them
138
+ right before executing. `allowed` is `true` only for a plain `allow`, so code
139
+ that reads `allowed` alone refuses a constrained allow instead of ignoring
140
+ its limits.
141
+ - `reportResult()` is a notification: nothing comes back. The broker writes the
142
+ outcome next to the decision in its audit, and `broker_diagnose` reports
143
+ decisions of `resultsRequired` capabilities still unreported.
144
+
145
+ - The provider needs its own identity on the broker (an entry in the security
146
+ file's `providers` table), which means presenting a secret:
147
+ `new DirectTransport(url, { secret })`, sent as `X-Provider-Token`. Node 22
148
+ and later can; a browser cannot, so this is for Node and native providers.
149
+ - To test all of this without an authorization server, start the broker with
150
+ `startTestBroker()` from `@cyanmycelium/mcp-broker/testing`
151
+ ([guide](../broker/docs/testing.md)).
152
+ - The broker's answers are taken off the socket before the MCP server sees
153
+ them. A refused request rejects with `BrokerRequestError` (`code`, `data`).
154
+ - There is no timeout by default: a broker from 1.4.1 on answers at once. Set
155
+ `brokerRequestTimeoutMs` only to talk to an older one, which drops methods it
156
+ does not know.
157
+
158
+ ## Distributed traces (broker 1.5.0)
159
+
160
+ Every addressed request arriving from broker 1.5 carries W3C Trace Context in
161
+ `params._meta.traceparent`. Read it from the request metadata, create a SERVER
162
+ span, then propagate a child context when this provider calls another slot:
163
+
164
+ ```ts
165
+ import {
166
+ childTraceparent,
167
+ traceparentOf,
168
+ withTraceparent,
169
+ } from "@cyanmycelium/mcp-broker-provider";
170
+
171
+ const incoming = traceparentOf(request?.meta);
172
+ const downstreamMeta = incoming
173
+ ? withTraceparent(undefined, childTraceparent(incoming, clientSpanId))
174
+ : undefined;
175
+ ```
176
+
177
+ Completed spans are validated before emission:
178
+
179
+ ```ts
180
+ const emitted = transport.broker.span({
181
+ traceId,
182
+ spanId,
183
+ parentSpanId,
184
+ name: "modbus.read",
185
+ kind: 3,
186
+ startTimeUnixNano,
187
+ endTimeUnixNano,
188
+ attributes: { "modbus.unit_id": 1 },
189
+ });
190
+ ```
191
+
192
+ The call is a notification and never waits for an answer. The broker consumes
193
+ `broker/telemetry`, sends it through its bounded exporter queue, and never
194
+ broadcasts it to MCP clients. It returns `false` and drops the span when the
195
+ provider link is down, so telemetry never occupies the transport queue ahead
196
+ of MCP control traffic. See [the complete telemetry contract](../../../docs/telemetry.md).
197
+
98
198
  ## Diagnostics
99
199
 
100
200
  This stack used to fail quietly. The transports now name what went wrong, on the console, because a browser-hosted provider has nowhere else to report:
package/dist/index.d.ts CHANGED
@@ -1,6 +1,238 @@
1
1
  export { ITunnelRegisterOptions, TUNNEL_REGISTER_METHOD, TunnelEnvelope, TunnelError, TunnelErrorCode, TunnelErrorCodes, decodeEnvelope, encodeEnvelope, encodeEnvelopeMessage, encodeErrorEnvelope, encodeRegisterEnvelope, encodeRegisterFrame, envelopeFrame, tunnelErrorOf } from './protocol/index.js';
2
2
  import { IMessageTransport } from '@cyanmycelium/mcp-core';
3
3
 
4
+ /**
5
+ * Talks to the broker itself, over the provider's own socket: declaring an
6
+ * authorization domain, and asking for decisions.
7
+ *
8
+ * Requires broker 1.5.0 or later. An older broker does not know these methods:
9
+ * from 1.4.1 it refuses them at once with `-32601`; 1.4.0 and earlier drop
10
+ * them and never answer, which is why {@link IBrokerClientOptions.requestTimeoutMs}
11
+ * exists, off by default.
12
+ */
13
+ /** The `params._meta` key under which the broker passes the caller reference with each request. */
14
+ declare const CALLER_META_KEY = "io.cyanmycelium/caller";
15
+ /** W3C Trace Context carrier used by MCP requests. */
16
+ declare const TRACEPARENT_META_KEY = "traceparent";
17
+ /** Provider-to-broker telemetry notification. */
18
+ declare const TELEMETRY_NOTIFICATION_METHOD = "broker/telemetry";
19
+ /** Provider-to-broker notification reporting what happened after a decision. */
20
+ declare const AUDIT_RESULT_NOTIFICATION_METHOD = "broker/audit/result";
21
+ interface ITraceParent {
22
+ readonly version: "00";
23
+ readonly traceId: string;
24
+ readonly parentId: string;
25
+ readonly traceFlags: string;
26
+ }
27
+ type TelemetryAttributeValue = string | number | boolean;
28
+ interface ITelemetryEvent {
29
+ readonly name: string;
30
+ readonly timeUnixNano: string;
31
+ readonly attributes?: Readonly<Record<string, TelemetryAttributeValue>>;
32
+ }
33
+ interface ITelemetrySpan {
34
+ readonly traceId: string;
35
+ readonly spanId: string;
36
+ readonly parentSpanId?: string;
37
+ readonly name: string;
38
+ readonly kind?: number;
39
+ readonly startTimeUnixNano: string;
40
+ readonly endTimeUnixNano: string;
41
+ readonly attributes?: Readonly<Record<string, TelemetryAttributeValue>>;
42
+ readonly events?: readonly ITelemetryEvent[];
43
+ readonly status?: {
44
+ readonly code: number;
45
+ readonly message?: string;
46
+ };
47
+ }
48
+ /** Parses the W3C version 00 traceparent representation used in MCP metadata. */
49
+ declare function parseTraceparent(value: unknown): ITraceParent | undefined;
50
+ declare function formatTraceparent(context: ITraceParent): string;
51
+ /** Reads `params._meta.traceparent`, returning `undefined` when it is malformed. */
52
+ declare function traceparentOf(meta: Readonly<Record<string, unknown>> | undefined): ITraceParent | undefined;
53
+ /** Returns a metadata copy carrying the supplied validated trace context. */
54
+ declare function withTraceparent(meta: Readonly<Record<string, unknown>> | undefined, context: ITraceParent): Readonly<Record<string, unknown>>;
55
+ /** Continues a trace using the caller's span as the new W3C parent id. */
56
+ declare function childTraceparent(parent: ITraceParent, spanId: string): ITraceParent;
57
+ /** What the broker hands a declaring provider with each request, under {@link CALLER_META_KEY}. */
58
+ interface ICallerReference {
59
+ /** Opaque. Valid on this slot, while the request it came with is pending. */
60
+ readonly ref: string;
61
+ /** Ties the decisions and the eventual report to the client request. */
62
+ readonly correlationId: string;
63
+ /** W3C trace id carried by the MCP request. */
64
+ readonly traceId?: string;
65
+ }
66
+ /**
67
+ * Reads the caller reference out of a request's `params._meta`, or
68
+ * `undefined` when there is none (the broker only adds it once a declaration
69
+ * was accepted). With mcp-core 1.4.0, pass `request?.meta` from the context
70
+ * an adapter receives.
71
+ */
72
+ declare function callerReferenceOf(meta: Readonly<Record<string, unknown>> | undefined): ICallerReference | undefined;
73
+ /**
74
+ * Engineering limits of one resource. They hold for every caller, and the
75
+ * broker returns them with each allow on that resource as
76
+ * `obligations.constraints`, for the provider to apply.
77
+ */
78
+ interface IResourceLimits {
79
+ readonly minValue?: number;
80
+ readonly maxValue?: number;
81
+ readonly allowedValues?: readonly (string | number | boolean | null)[];
82
+ readonly destinations?: readonly string[];
83
+ }
84
+ /** One resource of a declaration: the provider's own identifier, and the path the broker evaluates. */
85
+ interface IDeclaredResource {
86
+ readonly resource: string;
87
+ readonly resourcePath: string;
88
+ readonly effect?: string;
89
+ readonly limits?: IResourceLimits;
90
+ }
91
+ /** `broker/authorization/declare` parameters. Describes; grants nothing. */
92
+ interface IAuthorizationDeclaration {
93
+ readonly version: string;
94
+ readonly domain: string;
95
+ readonly namespace: {
96
+ readonly resource: string;
97
+ };
98
+ readonly capabilities: readonly string[];
99
+ readonly resources?: readonly IDeclaredResource[];
100
+ readonly protects?: readonly string[];
101
+ /**
102
+ * Declared capabilities whose allowed decisions this provider promises to
103
+ * report with {@link BrokerClient.reportResult}. One not reported in time
104
+ * shows up in `broker_diagnose`.
105
+ */
106
+ readonly resultsRequired?: readonly string[];
107
+ }
108
+ interface IDeclarationAccepted {
109
+ readonly accepted: true;
110
+ readonly version: string;
111
+ readonly policyVersion: string;
112
+ }
113
+ /** One question: may (the caller) do `capability` on this resource? */
114
+ interface IAuthorizationCheck {
115
+ readonly capability: string;
116
+ readonly resource: string;
117
+ readonly resourcePath: string;
118
+ readonly attributes?: Readonly<Record<string, unknown>>;
119
+ }
120
+ /** `broker/authorize` parameters. */
121
+ interface IAuthorizationQuery {
122
+ /** On whose behalf: the caller of a pending request, or the provider itself. Never an identity. */
123
+ readonly principal: {
124
+ readonly type: "caller-ref";
125
+ readonly ref: string;
126
+ } | {
127
+ readonly type: "provider";
128
+ };
129
+ /** Only for `{ type: "provider" }`; a caller reference carries its own. */
130
+ readonly correlationId?: string;
131
+ /** Optional W3C trace id for provider-initiated work. */
132
+ readonly traceId?: string;
133
+ readonly checks: readonly IAuthorizationCheck[];
134
+ }
135
+ /** What an allow comes with. */
136
+ interface IAuthorizationObligations {
137
+ /** The declared limits of the resource. Apply them right before executing. */
138
+ readonly constraints?: IResourceLimits;
139
+ }
140
+ interface IAuthorizationDecision {
141
+ readonly decisionId: string;
142
+ /** `allow-with-constraints`: allowed, within `obligations`. */
143
+ readonly effect: "allow" | "deny" | "allow-with-constraints";
144
+ /**
145
+ * `true` only for an unconditional `allow`. A provider that reads only
146
+ * this field therefore refuses a constrained allow rather than ignoring
147
+ * its constraints; read `effect` to apply them.
148
+ */
149
+ readonly allowed: boolean;
150
+ readonly reason: string;
151
+ readonly policies?: readonly string[];
152
+ readonly obligations?: IAuthorizationObligations;
153
+ }
154
+ /** `broker/audit/result` parameters: the outcome of what a decision allowed or refused. */
155
+ interface IAuditResult {
156
+ /** The `decisionId` the broker returned. */
157
+ readonly decisionId: string;
158
+ readonly result: "success" | "failure" | "refused";
159
+ /** The protocol's own status (`Good`, an exception code, ...). */
160
+ readonly nativeStatus?: string;
161
+ /** The provider's error code, when it failed or refused. */
162
+ readonly errorCode?: string;
163
+ }
164
+ interface IAuthorizationAnswer {
165
+ readonly policyVersion: string;
166
+ /** One per check, in the same order. */
167
+ readonly decisions: readonly IAuthorizationDecision[];
168
+ }
169
+ /** The broker refused a request, or never answered it. */
170
+ declare class BrokerRequestError extends Error {
171
+ /** JSON-RPC error code; `undefined` for a timeout or a closed socket. */
172
+ readonly code?: number | undefined;
173
+ /** The error's `data`, e.g. `{ errors: [...] }` for a refused declaration. */
174
+ readonly data?: unknown | undefined;
175
+ constructor(message: string,
176
+ /** JSON-RPC error code; `undefined` for a timeout or a closed socket. */
177
+ code?: number | undefined,
178
+ /** The error's `data`, e.g. `{ errors: [...] }` for a refused declaration. */
179
+ data?: unknown | undefined);
180
+ }
181
+ interface IBrokerClientOptions {
182
+ /**
183
+ * Rejects a request the broker did not answer within this many ms. Off by
184
+ * default: a broker from 1.4.1 on answers every request at once, refusals
185
+ * included, so waiting is never the normal path. Set it only to talk to an
186
+ * older broker, which drops what it does not know.
187
+ */
188
+ readonly requestTimeoutMs?: number;
189
+ }
190
+ /**
191
+ * The `broker/*` methods of one provider slot. Reached as `transport.broker`
192
+ * on {@link DirectTransport} and {@link MultiplexTransport}; the transport
193
+ * routes the broker's answers here before anything reaches the MCP server.
194
+ */
195
+ declare class BrokerClient {
196
+ private readonly _write;
197
+ private readonly _writeTelemetry;
198
+ private readonly _timeoutMs;
199
+ private readonly _waiting;
200
+ private _next;
201
+ constructor(write: (frame: string) => void, options?: IBrokerClientOptions, writeTelemetry?: (frame: string) => boolean);
202
+ /**
203
+ * Declares this provider's authorization domain. Resolves when the broker
204
+ * accepted it; rejects with a {@link BrokerRequestError} whose `data.errors`
205
+ * lists every problem otherwise. Until it resolves, serve nothing that
206
+ * needs a decision.
207
+ */
208
+ declare(declaration: IAuthorizationDeclaration): Promise<IDeclarationAccepted>;
209
+ /** Asks for one decision per check. */
210
+ authorize(query: IAuthorizationQuery): Promise<IAuthorizationAnswer>;
211
+ /**
212
+ * Reports what happened after a decision, so the broker's audit shows the
213
+ * outcome next to the decision. A notification: nothing comes back, and a
214
+ * report the broker cannot match is counted on its side.
215
+ */
216
+ reportResult(report: IAuditResult): void;
217
+ /** Emits one complete provider span, or returns false when the link is down. */
218
+ span(span: ITelemetrySpan): boolean;
219
+ /** Number of requests still waiting for the broker. */
220
+ get pendingCount(): number;
221
+ /**
222
+ * Consumes a frame when it answers one of this client's requests. Returns
223
+ * `true` when it did, and the frame must not reach the MCP server.
224
+ * @internal Called by the transports.
225
+ */
226
+ handleIncoming(frame: string): boolean;
227
+ /**
228
+ * Fails every waiting request: the socket they went out on is gone, and a
229
+ * reconnected one will not carry their answers.
230
+ * @internal Called by the transports.
231
+ */
232
+ rejectAll(reason: string): void;
233
+ private _request;
234
+ }
235
+
4
236
  /** Options accepted by {@link DirectTransport}. */
5
237
  interface IDirectTransportOptions {
6
238
  /**
@@ -22,6 +254,25 @@ interface IDirectTransportOptions {
22
254
  * the provider silently never appears in `_all`.
23
255
  */
24
256
  aggregate?: boolean;
257
+ /**
258
+ * Rejects a `broker.declare()` / `broker.authorize()` the broker did not
259
+ * answer within this many ms. Off by default: a broker from 1.4.1 on answers
260
+ * at once. Only for an older broker, which drops methods it does not know.
261
+ */
262
+ brokerRequestTimeoutMs?: number;
263
+ /**
264
+ * The provider secret, sent as the `X-Provider-Token` header of the
265
+ * WebSocket handshake. Required by a broker that authenticates providers
266
+ * (`providerSecret`, or the security file's `providers` table, where it is
267
+ * what gives this provider its own identity).
268
+ *
269
+ * **Node only.** Node's `WebSocket` (22 and later) accepts handshake
270
+ * headers; a browser's does not, and a browser provider cannot
271
+ * authenticate (terminate provider auth in a reverse proxy instead).
272
+ */
273
+ secret?: string;
274
+ /** Extra handshake headers, Node only, like {@link secret}. */
275
+ headers?: Readonly<Record<string, string>>;
25
276
  }
26
277
  /**
27
278
  * 1:1 WebSocket transport, wraps a single `WebSocket` connection to a broker
@@ -40,6 +291,7 @@ interface IDirectTransportOptions {
40
291
  declare class DirectTransport implements IMessageTransport {
41
292
  private readonly _wsUrl;
42
293
  private readonly _aggregate;
294
+ private readonly _headers;
43
295
  private readonly _pending;
44
296
  /** Throttles the "wrote to a closed transport" line, which repeats per frame. */
45
297
  private readonly _afterCloseNotice;
@@ -49,6 +301,12 @@ declare class DirectTransport implements IMessageTransport {
49
301
  onOpen: (() => void) | null;
50
302
  onClose: (() => void) | null;
51
303
  onError: ((error: Error) => void) | null;
304
+ /**
305
+ * The broker's own methods for this slot: declaring an authorization
306
+ * domain, asking for decisions. Its answers are taken off the socket
307
+ * before {@link onMessage} sees anything.
308
+ */
309
+ readonly broker: BrokerClient;
52
310
  constructor(wsUrl: string, options?: IDirectTransportOptions);
53
311
  get isOpen(): boolean;
54
312
  /**
@@ -89,6 +347,9 @@ declare class MultiplexSocket {
89
347
  /** Per-URL cache so all transports targeting the same tunnel share one socket. */
90
348
  private static readonly _instances;
91
349
  private readonly _wsUrl;
350
+ /** Cache key: the URL, and the handshake headers, since two secrets are two identities and need two sockets. */
351
+ private readonly _key;
352
+ private readonly _headers;
92
353
  private readonly _transports;
93
354
  /** Aggregate opt-in per slot, absent when the caller did not express one. */
94
355
  private readonly _aggregates;
@@ -105,8 +366,8 @@ declare class MultiplexSocket {
105
366
  /** Throttles the "wrote to a closed tunnel" line, which repeats per frame. */
106
367
  private readonly _afterStopNotice;
107
368
  private constructor();
108
- /** Returns (or creates) the singleton socket for a given tunnel URL. */
109
- static getOrCreate(wsUrl: string): MultiplexSocket;
369
+ /** Returns (or creates) the singleton socket for a given tunnel URL and handshake headers. */
370
+ static getOrCreate(wsUrl: string, headers?: Record<string, string>): MultiplexSocket;
110
371
  get isOpen(): boolean;
111
372
  register(name: string, transport: MultiplexTransport, aggregate?: boolean): void;
112
373
  unregister(name: string): void;
@@ -131,6 +392,8 @@ declare class MultiplexSocket {
131
392
  */
132
393
  private _announceProvider;
133
394
  send(provider: string, data: string): void;
395
+ /** Sends low-priority telemetry only while the shared link is open. */
396
+ sendTelemetry(provider: string, data: string): boolean;
134
397
  private _connect;
135
398
  /** Writes out everything queued while the socket was down. */
136
399
  private _flush;
@@ -159,6 +422,25 @@ interface IMultiplexTransportOptions {
159
422
  * appears in `_all`.
160
423
  */
161
424
  aggregate?: boolean;
425
+ /**
426
+ * Rejects a `broker.declare()` / `broker.authorize()` the broker did not
427
+ * answer within this many ms. Off by default: a broker from 1.4.1 on answers
428
+ * at once. Only for an older broker, which drops methods it does not know.
429
+ */
430
+ brokerRequestTimeoutMs?: number;
431
+ /**
432
+ * The provider secret, sent as the `X-Provider-Token` header of the
433
+ * WebSocket handshake. Required by a broker that authenticates providers
434
+ * (`providerSecret`, or the security file's `providers` table, where it is
435
+ * what gives this provider its own identity).
436
+ *
437
+ * **Node only.** Node's `WebSocket` (22 and later) accepts handshake
438
+ * headers; a browser's does not, and a browser provider cannot
439
+ * authenticate (terminate provider auth in a reverse proxy instead).
440
+ */
441
+ secret?: string;
442
+ /** Extra handshake headers, Node only, like {@link secret}. */
443
+ headers?: Readonly<Record<string, string>>;
162
444
  }
163
445
  /**
164
446
  * A transport that multiplexes multiple MCP servers over a single shared
@@ -180,7 +462,19 @@ declare class MultiplexTransport implements IMessageTransport {
180
462
  onOpen: (() => void) | null;
181
463
  onClose: (() => void) | null;
182
464
  onError: ((error: Error) => void) | null;
465
+ /**
466
+ * The broker's own methods for this slot: declaring an authorization
467
+ * domain, asking for decisions. Its answers are taken off the socket
468
+ * before {@link onMessage} sees anything.
469
+ */
470
+ readonly broker: BrokerClient;
183
471
  constructor(name: string, socket: MultiplexSocket, options?: IMultiplexTransportOptions);
472
+ /**
473
+ * One frame from the broker for this slot: the broker's answer to one of
474
+ * {@link broker}'s requests, or MCP traffic for the server.
475
+ * @internal Called by the shared socket.
476
+ */
477
+ _receive(frame: string): void;
184
478
  /**
185
479
  * Convenience factory: creates a {@link MultiplexTransport} backed by a
186
480
  * shared {@link MultiplexSocket} for the given tunnel URL.
@@ -211,4 +505,4 @@ declare class MultiplexTransport implements IMessageTransport {
211
505
  close(): void;
212
506
  }
213
507
 
214
- export { DirectTransport, type IDirectTransportOptions, type IMultiplexTransportOptions, MultiplexTransport };
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 };