@adcp/sdk 15.4.0 → 15.5.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/dist/lib/advanced.d.mts +2 -2
- package/dist/lib/advanced.d.ts +2 -2
- package/dist/lib/advanced.js +8 -2
- package/dist/lib/advanced.mjs +8 -2
- package/dist/lib/core/AgentClient.d.mts +4 -0
- package/dist/lib/core/AgentClient.d.ts +4 -0
- package/dist/lib/core/AgentClient.js +8 -0
- package/dist/lib/core/AgentClient.mjs +8 -0
- package/dist/lib/core/SingleAgentClient.d.mts +154 -0
- package/dist/lib/core/SingleAgentClient.d.ts +154 -0
- package/dist/lib/core/SingleAgentClient.js +661 -23
- package/dist/lib/core/SingleAgentClient.mjs +666 -23
- package/dist/lib/core/agent-discovery-cache.d.mts +103 -0
- package/dist/lib/core/agent-discovery-cache.d.ts +103 -0
- package/dist/lib/core/agent-discovery-cache.js +121 -0
- package/dist/lib/core/agent-discovery-cache.mjs +92 -0
- package/dist/lib/index.d.mts +5 -3
- package/dist/lib/index.d.ts +5 -3
- package/dist/lib/index.js +7 -0
- package/dist/lib/index.mjs +6 -0
- package/dist/lib/protocols/index.d.mts +2 -2
- package/dist/lib/protocols/index.d.ts +2 -2
- package/dist/lib/protocols/index.js +1 -1
- package/dist/lib/protocols/index.mjs +1 -1
- package/dist/lib/protocols/mcp-call-context.d.mts +109 -0
- package/dist/lib/protocols/mcp-call-context.d.ts +109 -0
- package/dist/lib/protocols/mcp-call-context.js +221 -0
- package/dist/lib/protocols/mcp-call-context.mjs +186 -0
- package/dist/lib/protocols/mcp-modern.d.mts +9 -1
- package/dist/lib/protocols/mcp-modern.d.ts +9 -1
- package/dist/lib/protocols/mcp-modern.js +180 -104
- package/dist/lib/protocols/mcp-modern.mjs +193 -104
- package/dist/lib/protocols/mcp-scope.d.mts +15 -0
- package/dist/lib/protocols/mcp-scope.d.ts +15 -0
- package/dist/lib/protocols/mcp.d.mts +4 -2
- package/dist/lib/protocols/mcp.d.ts +4 -2
- package/dist/lib/protocols/mcp.js +178 -154
- package/dist/lib/protocols/mcp.mjs +189 -153
- package/dist/lib/protocols/transportDiagnostics.d.mts +33 -1
- package/dist/lib/protocols/transportDiagnostics.d.ts +33 -1
- package/dist/lib/protocols/transportDiagnostics.js +128 -12
- package/dist/lib/protocols/transportDiagnostics.mjs +128 -12
- package/dist/lib/reporting/gcs/index.d.mts +2 -0
- package/dist/lib/reporting/gcs/index.d.ts +2 -0
- package/dist/lib/reporting/gcs/index.js +3 -0
- package/dist/lib/reporting/gcs/index.mjs +2 -0
- package/dist/lib/reporting/gcs/row-provider.d.mts +32 -0
- package/dist/lib/reporting/gcs/row-provider.d.ts +32 -0
- package/dist/lib/reporting/gcs/row-provider.js +205 -0
- package/dist/lib/reporting/gcs/row-provider.mjs +188 -0
- package/dist/lib/reporting/ledger/change-feed.d.mts +41 -0
- package/dist/lib/reporting/ledger/change-feed.d.ts +41 -0
- package/dist/lib/reporting/ledger/change-feed.js +101 -0
- package/dist/lib/reporting/ledger/change-feed.mjs +74 -0
- package/dist/lib/reporting/ledger/handler.js +22 -8
- package/dist/lib/reporting/ledger/handler.mjs +22 -8
- package/dist/lib/reporting/ledger/index.d.mts +4 -0
- package/dist/lib/reporting/ledger/index.d.ts +4 -0
- package/dist/lib/reporting/ledger/index.js +9 -1
- package/dist/lib/reporting/ledger/index.mjs +4 -0
- package/dist/lib/reporting/ledger/managed-postgres.d.mts +2 -0
- package/dist/lib/reporting/ledger/managed-postgres.d.ts +2 -0
- package/dist/lib/reporting/ledger/managed-postgres.js +21 -1
- package/dist/lib/reporting/ledger/managed-postgres.mjs +21 -1
- package/dist/lib/reporting/ledger/managed.d.mts +9 -0
- package/dist/lib/reporting/ledger/managed.d.ts +9 -0
- package/dist/lib/reporting/ledger/managed.js +18 -0
- package/dist/lib/reporting/ledger/managed.mjs +18 -0
- package/dist/lib/reporting/ledger/postgres.d.mts +175 -0
- package/dist/lib/reporting/ledger/postgres.d.ts +175 -0
- package/dist/lib/reporting/ledger/postgres.js +851 -26
- package/dist/lib/reporting/ledger/postgres.mjs +859 -26
- package/dist/lib/reporting/ledger/producer.js +10 -1
- package/dist/lib/reporting/ledger/producer.mjs +10 -1
- package/dist/lib/reporting/ledger/row-storage-errors.d.mts +7 -0
- package/dist/lib/reporting/ledger/row-storage-errors.d.ts +7 -0
- package/dist/lib/reporting/ledger/row-storage-errors.js +41 -0
- package/dist/lib/reporting/ledger/row-storage-errors.mjs +16 -0
- package/dist/lib/reporting/ledger/row-storage-migration.d.mts +41 -0
- package/dist/lib/reporting/ledger/row-storage-migration.d.ts +41 -0
- package/dist/lib/reporting/ledger/row-storage-migration.js +234 -0
- package/dist/lib/reporting/ledger/row-storage-migration.mjs +208 -0
- package/dist/lib/reporting/ledger/row-storage-object.d.mts +112 -0
- package/dist/lib/reporting/ledger/row-storage-object.d.ts +112 -0
- package/dist/lib/reporting/ledger/row-storage-object.js +281 -0
- package/dist/lib/reporting/ledger/row-storage-object.mjs +239 -0
- package/dist/lib/reporting/ledger/row-storage-provider-io.d.mts +51 -0
- package/dist/lib/reporting/ledger/row-storage-provider-io.d.ts +51 -0
- package/dist/lib/reporting/ledger/row-storage-provider-io.js +152 -0
- package/dist/lib/reporting/ledger/row-storage-provider-io.mjs +119 -0
- package/dist/lib/reporting/ledger/row-storage.d.mts +224 -0
- package/dist/lib/reporting/ledger/row-storage.d.ts +224 -0
- package/dist/lib/reporting/ledger/row-storage.js +692 -0
- package/dist/lib/reporting/ledger/row-storage.mjs +680 -0
- package/dist/lib/reporting/ledger/types.d.mts +8 -0
- package/dist/lib/reporting/ledger/types.d.ts +8 -0
- package/dist/lib/reporting/service/index.d.mts +41 -1
- package/dist/lib/reporting/service/index.d.ts +41 -1
- package/dist/lib/reporting/service/index.js +68 -1
- package/dist/lib/reporting/service/index.mjs +70 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/README.md +1 -1
- package/docs/TYPE-SUMMARY.md +35 -2
- package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +142 -0
- package/docs/guides/EXISTING-PLATFORM.md +43 -12
- package/docs/guides/REPORTING-LEDGER.md +209 -0
- package/docs/llms.txt +4 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/package.json +1 -1
|
@@ -20,6 +20,20 @@ import {
|
|
|
20
20
|
registerMCPConnectionScopePending
|
|
21
21
|
} from "./mcp-scope.mjs";
|
|
22
22
|
import { terminateSessionBestEffort } from "./session-termination.mjs";
|
|
23
|
+
import {
|
|
24
|
+
applyMCPCallHeaders,
|
|
25
|
+
isCorrelationHeader,
|
|
26
|
+
withAmbientIdentityHeaders,
|
|
27
|
+
closeWhenIdle,
|
|
28
|
+
linkedCallSignal,
|
|
29
|
+
joinPendingConnection,
|
|
30
|
+
RETRY_CONNECT,
|
|
31
|
+
requestTimeoutFor,
|
|
32
|
+
runCallPhase,
|
|
33
|
+
runWithMCPCallContext,
|
|
34
|
+
splitConnectionHeaders,
|
|
35
|
+
trackConnectionUse
|
|
36
|
+
} from "./mcp-call-context.mjs";
|
|
23
37
|
import { buildAgentSigningFetch, signingContextStorage } from "../signing/client.mjs";
|
|
24
38
|
import {
|
|
25
39
|
isAbortOrTimeoutError,
|
|
@@ -44,10 +58,8 @@ const LEGACY_CLASSIFICATION_TTL_MS = 5 * 60 * 1e3;
|
|
|
44
58
|
const MODERN_DISCOVERY_TTL_MS = 5 * 60 * 1e3;
|
|
45
59
|
const modernOAuthProviderIds = /* @__PURE__ */ new WeakMap();
|
|
46
60
|
const modernFetchFnIds = /* @__PURE__ */ new WeakMap();
|
|
47
|
-
const modernSignalIds = /* @__PURE__ */ new WeakMap();
|
|
48
61
|
let nextModernOAuthProviderId = 0;
|
|
49
62
|
let nextModernFetchFnId = 0;
|
|
50
|
-
let nextModernSignalId = 0;
|
|
51
63
|
let connectionGeneration = 0;
|
|
52
64
|
async function closeModernClient(client, terminateSession = true) {
|
|
53
65
|
const transport = modernTransports.get(client);
|
|
@@ -67,7 +79,7 @@ function buildAuthHeaders(authToken, customHeaders, authProvider) {
|
|
|
67
79
|
return key !== "authorization" && key !== "x-adcp-auth";
|
|
68
80
|
})
|
|
69
81
|
) : customHeaders;
|
|
70
|
-
return createMCPRequestHeaders(filteredHeaders, authProvider ? void 0 : authToken);
|
|
82
|
+
return withAmbientIdentityHeaders(createMCPRequestHeaders(filteredHeaders, authProvider ? void 0 : authToken));
|
|
71
83
|
}
|
|
72
84
|
function oauthProviderCacheKey(provider) {
|
|
73
85
|
if (!provider) return void 0;
|
|
@@ -87,33 +99,26 @@ function fetchFnCacheKey(fetchFn) {
|
|
|
87
99
|
}
|
|
88
100
|
return key;
|
|
89
101
|
}
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
let key = modernSignalIds.get(signal);
|
|
93
|
-
if (!key) {
|
|
94
|
-
key = `signal:${++nextModernSignalId}`;
|
|
95
|
-
modernSignalIds.set(signal, key);
|
|
96
|
-
}
|
|
97
|
-
return key;
|
|
98
|
-
}
|
|
99
|
-
function connectionCacheKey(agentUrl, headers, signingCacheKey, authProvider, fetchFn, signal, requestTimeoutMs, handleLegacy, allowPrivateIp) {
|
|
100
|
-
const normalizedHeaders = Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]).sort(([left], [right]) => left.localeCompare(right));
|
|
102
|
+
function connectionCacheKey(agentUrl, headers, signingCacheKey, authProvider, fetchFn, handleLegacy, allowPrivateIp) {
|
|
103
|
+
const normalizedHeaders = Object.entries(splitConnectionHeaders(headers).identity).map(([key, value]) => [key.toLowerCase(), value]).sort(([left], [right]) => left.localeCompare(right));
|
|
101
104
|
const parts = [agentUrl, `headers:${cacheDisambiguator(JSON.stringify(normalizedHeaders))}`];
|
|
102
105
|
if (signingCacheKey) parts.push(signingCacheKey);
|
|
103
106
|
const providerKey = oauthProviderCacheKey(authProvider);
|
|
104
107
|
if (providerKey) parts.push(providerKey);
|
|
105
108
|
const fetchKey = fetchFnCacheKey(fetchFn);
|
|
106
109
|
if (fetchKey) parts.push(fetchKey);
|
|
107
|
-
const signalKey = signalCacheKey(signal);
|
|
108
|
-
if (signalKey) parts.push(signalKey);
|
|
109
|
-
if (requestTimeoutMs !== void 0) parts.push(`timeout:${requestTimeoutMs}`);
|
|
110
110
|
if (handleLegacy !== void 0) parts.push(`handle-legacy:${handleLegacy}`);
|
|
111
111
|
if (allowPrivateIp !== void 0) parts.push(`allow-private-ip:${allowPrivateIp}`);
|
|
112
112
|
const scopeKey = currentMCPConnectionScopeKey();
|
|
113
113
|
if (scopeKey) parts.push(scopeKey);
|
|
114
114
|
return parts.join("::");
|
|
115
115
|
}
|
|
116
|
-
function
|
|
116
|
+
function classificationKey(cacheKey) {
|
|
117
|
+
const marker = cacheKey.lastIndexOf("::scope:");
|
|
118
|
+
return marker >= 0 ? cacheKey.slice(0, marker) : cacheKey;
|
|
119
|
+
}
|
|
120
|
+
function isKnownLegacy(connectionKey) {
|
|
121
|
+
const cacheKey = classificationKey(connectionKey);
|
|
117
122
|
const classifiedAt = knownLegacyConnections.get(cacheKey);
|
|
118
123
|
if (classifiedAt === void 0) return false;
|
|
119
124
|
if (Date.now() - classifiedAt > LEGACY_CLASSIFICATION_TTL_MS) {
|
|
@@ -124,7 +129,9 @@ function isKnownLegacy(cacheKey) {
|
|
|
124
129
|
knownLegacyConnections.set(cacheKey, classifiedAt);
|
|
125
130
|
return true;
|
|
126
131
|
}
|
|
127
|
-
function markKnownLegacy(
|
|
132
|
+
function markKnownLegacy(connectionKey) {
|
|
133
|
+
const cacheKey = classificationKey(connectionKey);
|
|
134
|
+
modernDiscoveries.delete(connectionKey);
|
|
128
135
|
modernDiscoveries.delete(cacheKey);
|
|
129
136
|
knownLegacyConnections.delete(cacheKey);
|
|
130
137
|
knownLegacyConnections.set(cacheKey, Date.now());
|
|
@@ -146,6 +153,7 @@ function getCachedModernDiscovery(cacheKey) {
|
|
|
146
153
|
return { kind: "modern", discover: cached.discover };
|
|
147
154
|
}
|
|
148
155
|
function cacheModernDiscovery(cacheKey, discover) {
|
|
156
|
+
knownLegacyConnections.delete(classificationKey(cacheKey));
|
|
149
157
|
modernDiscoveries.delete(cacheKey);
|
|
150
158
|
modernDiscoveries.set(cacheKey, {
|
|
151
159
|
discover,
|
|
@@ -172,8 +180,12 @@ function withPerRequestTraceHeaders(fetchImpl) {
|
|
|
172
180
|
return (input, init) => {
|
|
173
181
|
const headers = new Headers(input instanceof Request ? input.headers : void 0);
|
|
174
182
|
new Headers(init?.headers).forEach((value, key) => headers.set(key, value));
|
|
175
|
-
|
|
176
|
-
return fetchImpl(input, { ...init, headers });
|
|
183
|
+
applyMCPCallHeaders(headers);
|
|
184
|
+
if (headers.has("traceparent")) return fetchImpl(input, { ...init, headers, ...linkedCallSignal(init) });
|
|
185
|
+
for (const [key, value] of Object.entries(injectTraceHeaders())) {
|
|
186
|
+
if (isCorrelationHeader(key) && !headers.has(key)) headers.set(key, value);
|
|
187
|
+
}
|
|
188
|
+
return fetchImpl(input, { ...init, headers, ...linkedCallSignal(init) });
|
|
177
189
|
};
|
|
178
190
|
}
|
|
179
191
|
function getCachedConnection(cacheKey) {
|
|
@@ -183,7 +195,7 @@ function getCachedConnection(cacheKey) {
|
|
|
183
195
|
if (legacyExpiry !== void 0 && legacyExpiry <= Date.now()) {
|
|
184
196
|
modernConnections.delete(cacheKey);
|
|
185
197
|
legacyConnectionExpiresAt.delete(cacheKey);
|
|
186
|
-
void closeModernClient(client).catch(() => {
|
|
198
|
+
void closeWhenIdle(client, () => closeModernClient(client)).catch(() => {
|
|
187
199
|
});
|
|
188
200
|
return void 0;
|
|
189
201
|
}
|
|
@@ -199,7 +211,7 @@ function evictLeastRecentlyUsed() {
|
|
|
199
211
|
const client = modernConnections.get(oldestKey);
|
|
200
212
|
modernConnections.delete(oldestKey);
|
|
201
213
|
legacyConnectionExpiresAt.delete(oldestKey);
|
|
202
|
-
if (client) void closeModernClient(client).catch(() => {
|
|
214
|
+
if (client) void closeWhenIdle(client, () => closeModernClient(client)).catch(() => {
|
|
203
215
|
});
|
|
204
216
|
}
|
|
205
217
|
function isNegotiationAuthFailure(error) {
|
|
@@ -214,13 +226,24 @@ function presentedStaticCredential(authHeaders, authProvider) {
|
|
|
214
226
|
}
|
|
215
227
|
async function createNegotiatedClient(cacheKey, options, authHeaders, useCachedDiscovery = true, skipProbe = false) {
|
|
216
228
|
const generation = connectionGeneration;
|
|
217
|
-
const requestTimeoutMs = resolveRequestTimeoutMs(options.requestTimeoutMs);
|
|
218
229
|
const clientRequestTimeoutMs = resolveClientRequestTimeoutMs(options.requestTimeoutMs);
|
|
219
230
|
const rawNetworkFetch = createAgentTransportFetch(options.agentUrl, {
|
|
220
231
|
trustedFetchFn: options.fetchFn,
|
|
221
232
|
allowPrivateIp: options.allowPrivateIp
|
|
222
233
|
});
|
|
223
|
-
const networkFetch = (input, init) =>
|
|
234
|
+
const networkFetch = (input, init) => (
|
|
235
|
+
// `init.signal` already carries the active call's cancellation (linked at the
|
|
236
|
+
// outermost transport wrapper), including on legacy-era sessions where the
|
|
237
|
+
// v2 client cancels with a notification and leaves the HTTP stream open.
|
|
238
|
+
withAbortSignal(
|
|
239
|
+
[init?.signal],
|
|
240
|
+
requestTimeoutFor(init),
|
|
241
|
+
(deadline) => rawNetworkFetch(input, {
|
|
242
|
+
...init,
|
|
243
|
+
signal: init?.signal && deadline ? AbortSignal.any([init.signal, deadline]) : deadline ?? init?.signal
|
|
244
|
+
})
|
|
245
|
+
)
|
|
246
|
+
);
|
|
224
247
|
const diagnosticFetch = wrapFetchWithTransportDiagnostics(wrapFetchWithSizeLimit(networkFetch));
|
|
225
248
|
const signedFetch = options.signingContext ? buildAgentSigningFetch({
|
|
226
249
|
upstream: wrapFetchWithSignedRequestRejection(diagnosticFetch, options.agentUrl),
|
|
@@ -229,7 +252,7 @@ async function createNegotiatedClient(cacheKey, options, authHeaders, useCachedD
|
|
|
229
252
|
adcpVersion: options.signingContext.adcpVersion
|
|
230
253
|
}) : diagnosticFetch;
|
|
231
254
|
const transport = new StreamableHTTPClientTransport(new URL(options.agentUrl), {
|
|
232
|
-
requestInit: { headers: authHeaders, redirect: "manual" },
|
|
255
|
+
requestInit: { headers: splitConnectionHeaders(authHeaders).identity, redirect: "manual" },
|
|
233
256
|
fetch: wrapFetchWithCapture(withPerRequestTraceHeaders(signedFetch)),
|
|
234
257
|
...options.authProvider && {
|
|
235
258
|
authProvider: options.authProvider
|
|
@@ -277,10 +300,19 @@ async function createNegotiatedClient(cacheKey, options, authHeaders, useCachedD
|
|
|
277
300
|
}
|
|
278
301
|
}
|
|
279
302
|
async function getOrCreateModernConnection(cacheKey, options, authHeaders) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
303
|
+
for (let attempt = 0; ; attempt++) {
|
|
304
|
+
const cached = getCachedConnection(cacheKey);
|
|
305
|
+
if (cached) return cached;
|
|
306
|
+
const pending = pendingModernConnections.get(cacheKey);
|
|
307
|
+
if (pending) {
|
|
308
|
+
const joined = await joinPendingConnection(pending, options, attempt);
|
|
309
|
+
if (joined === RETRY_CONNECT) continue;
|
|
310
|
+
return joined;
|
|
311
|
+
}
|
|
312
|
+
return createSharedModernConnection(cacheKey, options, authHeaders);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function createSharedModernConnection(cacheKey, options, authHeaders) {
|
|
284
316
|
const generation = connectionGeneration;
|
|
285
317
|
const promise = createNegotiatedClient(cacheKey, options, authHeaders).then(async (client) => {
|
|
286
318
|
if ((client.getProtocolEra() === "modern" || options.handleLegacy === true) && generation === connectionGeneration) {
|
|
@@ -311,6 +343,16 @@ async function getOrCreateModernConnection(cacheKey, options, authHeaders) {
|
|
|
311
343
|
registerMCPConnectionScopePending(promise);
|
|
312
344
|
return promise;
|
|
313
345
|
}
|
|
346
|
+
async function retireFailedConnection(cacheKey, client, error) {
|
|
347
|
+
if (isAbortOrTimeoutError(error)) return;
|
|
348
|
+
if (modernConnections.get(cacheKey) === client) {
|
|
349
|
+
modernConnections.delete(cacheKey);
|
|
350
|
+
legacyConnectionExpiresAt.delete(cacheKey);
|
|
351
|
+
}
|
|
352
|
+
modernDiscoveries.delete(cacheKey);
|
|
353
|
+
await closeWhenIdle(client, () => closeModernClient(client, true)).catch(() => {
|
|
354
|
+
});
|
|
355
|
+
}
|
|
314
356
|
async function callOnModernClient(client, toolName, args, signal, requestTimeoutMs) {
|
|
315
357
|
const resolvedRequestTimeoutMs = resolveClientRequestTimeoutMs(requestTimeoutMs);
|
|
316
358
|
return await client.callTool(
|
|
@@ -329,14 +371,11 @@ async function attemptModernCall(options, toolName, args) {
|
|
|
329
371
|
options.signingContext?.cacheKey,
|
|
330
372
|
options.authProvider,
|
|
331
373
|
options.fetchFn,
|
|
332
|
-
options.signal,
|
|
333
|
-
options.requestTimeoutMs,
|
|
334
374
|
options.handleLegacy,
|
|
335
375
|
options.allowPrivateIp
|
|
336
376
|
);
|
|
337
377
|
if (isKnownLegacy(cacheKey)) return { handled: false };
|
|
338
|
-
const
|
|
339
|
-
const oneShot = guardedConnection && currentMCPConnectionScopeKey() === void 0;
|
|
378
|
+
const oneShot = options.fetchFn !== void 0 && currentMCPConnectionScopeKey() === void 0;
|
|
340
379
|
let client;
|
|
341
380
|
let callSucceeded = false;
|
|
342
381
|
try {
|
|
@@ -390,16 +429,21 @@ async function attemptModernCall(options, toolName, args) {
|
|
|
390
429
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
391
430
|
});
|
|
392
431
|
try {
|
|
393
|
-
const response = await
|
|
432
|
+
const response = await trackConnectionUse(
|
|
433
|
+
client,
|
|
434
|
+
() => runCallPhase(
|
|
435
|
+
options.signal,
|
|
436
|
+
() => callOnModernClient(client, toolName, args, options.signal, options.requestTimeoutMs)
|
|
437
|
+
)
|
|
438
|
+
);
|
|
394
439
|
callSucceeded = true;
|
|
395
440
|
return { handled: true, response };
|
|
396
441
|
} catch (error) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
await
|
|
402
|
-
} catch {
|
|
442
|
+
if (oneShot) {
|
|
443
|
+
await closeModernClient(client, !isAbortOrTimeoutError(error)).catch(() => {
|
|
444
|
+
});
|
|
445
|
+
} else {
|
|
446
|
+
await retireFailedConnection(cacheKey, client, error);
|
|
403
447
|
}
|
|
404
448
|
throw error;
|
|
405
449
|
} finally {
|
|
@@ -407,28 +451,39 @@ async function attemptModernCall(options, toolName, args) {
|
|
|
407
451
|
});
|
|
408
452
|
}
|
|
409
453
|
}
|
|
454
|
+
function callContextFor(options) {
|
|
455
|
+
const { perRequest } = splitConnectionHeaders(options.customHeaders);
|
|
456
|
+
const requestTimeoutMs = resolveRequestTimeoutMs(options.requestTimeoutMs);
|
|
457
|
+
return {
|
|
458
|
+
...Object.keys(perRequest).length > 0 && { headers: perRequest },
|
|
459
|
+
...requestTimeoutMs !== void 0 && { requestTimeoutMs }
|
|
460
|
+
};
|
|
461
|
+
}
|
|
410
462
|
async function tryCallModernMCPTool(agentUrl, toolName, args, authToken, debugLogs = [], customHeaders, options = {}) {
|
|
411
463
|
return withSpan(
|
|
412
464
|
"adcp.mcp.negotiate",
|
|
413
465
|
{ "adcp.tool": toolName, "http.url": agentUrl },
|
|
414
466
|
() => signingContextStorage.run(
|
|
415
467
|
options.signingContext,
|
|
416
|
-
() =>
|
|
417
|
-
{
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
468
|
+
() => runWithMCPCallContext(
|
|
469
|
+
callContextFor({ customHeaders, requestTimeoutMs: options.requestTimeoutMs }),
|
|
470
|
+
() => attemptModernCall(
|
|
471
|
+
{
|
|
472
|
+
agentUrl,
|
|
473
|
+
authToken,
|
|
474
|
+
customHeaders,
|
|
475
|
+
debugLogs,
|
|
476
|
+
signingContext: options.signingContext,
|
|
477
|
+
authProvider: options.authProvider,
|
|
478
|
+
signal: options.signal,
|
|
479
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
480
|
+
fetchFn: options.fetchFn,
|
|
481
|
+
allowPrivateIp: options.allowPrivateIp,
|
|
482
|
+
handleLegacy: options.handleLegacy
|
|
483
|
+
},
|
|
484
|
+
toolName,
|
|
485
|
+
args
|
|
486
|
+
)
|
|
432
487
|
)
|
|
433
488
|
)
|
|
434
489
|
);
|
|
@@ -454,26 +509,34 @@ async function probeModernMCPConnection(agentUrl, authToken, customHeaders, opti
|
|
|
454
509
|
options.signingContext?.cacheKey,
|
|
455
510
|
options.authProvider,
|
|
456
511
|
options.fetchFn,
|
|
457
|
-
options.signal,
|
|
458
|
-
options.requestTimeoutMs,
|
|
459
512
|
options.handleLegacy,
|
|
460
513
|
options.allowPrivateIp
|
|
461
514
|
);
|
|
515
|
+
const scoped = currentMCPConnectionScopeKey() !== void 0;
|
|
462
516
|
let client;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
517
|
+
return runWithMCPCallContext(
|
|
518
|
+
callContextFor({ customHeaders, requestTimeoutMs: options.requestTimeoutMs }),
|
|
519
|
+
async () => {
|
|
520
|
+
try {
|
|
521
|
+
client = scoped ? await getOrCreateModernConnection(cacheKey, connectionOptions, authHeaders) : await createNegotiatedClient(cacheKey, connectionOptions, authHeaders, false);
|
|
522
|
+
const era = client.getProtocolEra();
|
|
523
|
+
if (scoped && era === "legacy" && connectionOptions.handleLegacy !== true) markKnownLegacy(cacheKey);
|
|
524
|
+
return { connected: true, era };
|
|
525
|
+
} catch (error) {
|
|
526
|
+
modernDiscoveries.delete(cacheKey);
|
|
527
|
+
if (is401Error(error) || isAbortOrTimeoutError(error)) throw error;
|
|
528
|
+
const status = httpStatusOf(error);
|
|
529
|
+
if (status === 404 || status === 405) return { connected: false };
|
|
530
|
+
if (isLegacyEraNegotiationFailure(error, status)) return { connected: false };
|
|
531
|
+
throw error;
|
|
532
|
+
} finally {
|
|
533
|
+
if (client && !(scoped && modernConnections.get(cacheKey) === client)) {
|
|
534
|
+
await closeModernClient(client).catch(() => {
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
);
|
|
477
540
|
}
|
|
478
541
|
async function tryListModernMCPTools(agentUrl, authToken, customHeaders, options = {}) {
|
|
479
542
|
const connectionOptions = {
|
|
@@ -486,7 +549,8 @@ async function tryListModernMCPTools(agentUrl, authToken, customHeaders, options
|
|
|
486
549
|
signal: options.signal,
|
|
487
550
|
requestTimeoutMs: options.requestTimeoutMs,
|
|
488
551
|
fetchFn: options.fetchFn,
|
|
489
|
-
allowPrivateIp: options.allowPrivateIp
|
|
552
|
+
allowPrivateIp: options.allowPrivateIp,
|
|
553
|
+
handleLegacy: options.handleLegacy
|
|
490
554
|
};
|
|
491
555
|
const authHeaders = buildAuthHeaders(authToken, customHeaders, options.authProvider);
|
|
492
556
|
const cacheKey = connectionCacheKey(
|
|
@@ -495,50 +559,75 @@ async function tryListModernMCPTools(agentUrl, authToken, customHeaders, options
|
|
|
495
559
|
options.signingContext?.cacheKey,
|
|
496
560
|
options.authProvider,
|
|
497
561
|
options.fetchFn,
|
|
498
|
-
options.signal,
|
|
499
|
-
options.requestTimeoutMs,
|
|
500
562
|
options.handleLegacy,
|
|
501
563
|
options.allowPrivateIp
|
|
502
564
|
);
|
|
565
|
+
const scoped = currentMCPConnectionScopeKey() !== void 0;
|
|
566
|
+
if (scoped && isKnownLegacy(cacheKey)) return { handled: false };
|
|
503
567
|
let client;
|
|
568
|
+
const sharedClients = /* @__PURE__ */ new WeakSet();
|
|
569
|
+
const acquire = async (create) => {
|
|
570
|
+
const acquired = await create();
|
|
571
|
+
if (scoped && modernConnections.get(cacheKey) === acquired) sharedClients.add(acquired);
|
|
572
|
+
return acquired;
|
|
573
|
+
};
|
|
504
574
|
const listTools = async (connectedClient) => {
|
|
505
|
-
if (connectedClient.getProtocolEra() !== "modern"
|
|
575
|
+
if (connectedClient.getProtocolEra() !== "modern" && connectionOptions.handleLegacy !== true) {
|
|
576
|
+
if (scoped) markKnownLegacy(cacheKey);
|
|
577
|
+
return { handled: false };
|
|
578
|
+
}
|
|
506
579
|
const resolvedRequestTimeoutMs = resolveClientRequestTimeoutMs(options.requestTimeoutMs);
|
|
507
|
-
const result = await
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
580
|
+
const result = await trackConnectionUse(
|
|
581
|
+
connectedClient,
|
|
582
|
+
() => runCallPhase(
|
|
583
|
+
options.signal,
|
|
584
|
+
() => connectedClient.listTools(
|
|
585
|
+
options.adcpVersion === void 0 ? void 0 : { _meta: { adcp_version: options.adcpVersion } },
|
|
586
|
+
{
|
|
587
|
+
...options.signal && { signal: options.signal },
|
|
588
|
+
...resolvedRequestTimeoutMs !== void 0 && { timeout: resolvedRequestTimeoutMs }
|
|
589
|
+
}
|
|
590
|
+
)
|
|
591
|
+
)
|
|
513
592
|
);
|
|
514
593
|
return { handled: true, tools: result.tools };
|
|
515
594
|
};
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
} catch (error) {
|
|
520
|
-
let failure = error;
|
|
521
|
-
modernDiscoveries.delete(cacheKey);
|
|
522
|
-
if (!is401Error(failure) && !isAbortOrTimeoutError(failure) && client && clientsUsingCachedDiscovery.has(client)) {
|
|
523
|
-
await closeModernClient(client, false).catch(() => {
|
|
524
|
-
});
|
|
595
|
+
return runWithMCPCallContext(
|
|
596
|
+
callContextFor({ customHeaders, requestTimeoutMs: options.requestTimeoutMs }),
|
|
597
|
+
async () => {
|
|
525
598
|
try {
|
|
526
|
-
client = await
|
|
599
|
+
client = await acquire(
|
|
600
|
+
() => scoped ? getOrCreateModernConnection(cacheKey, connectionOptions, authHeaders) : createNegotiatedClient(cacheKey, connectionOptions, authHeaders)
|
|
601
|
+
);
|
|
527
602
|
return await listTools(client);
|
|
528
|
-
} catch (
|
|
529
|
-
failure =
|
|
603
|
+
} catch (error) {
|
|
604
|
+
let failure = error;
|
|
530
605
|
modernDiscoveries.delete(cacheKey);
|
|
606
|
+
if (client && sharedClients.has(client)) {
|
|
607
|
+
await retireFailedConnection(cacheKey, client, failure);
|
|
608
|
+
}
|
|
609
|
+
if (!is401Error(failure) && !isAbortOrTimeoutError(failure) && client && clientsUsingCachedDiscovery.has(client)) {
|
|
610
|
+
if (!sharedClients.has(client)) await closeModernClient(client, false).catch(() => {
|
|
611
|
+
});
|
|
612
|
+
try {
|
|
613
|
+
client = await acquire(() => createNegotiatedClient(cacheKey, connectionOptions, authHeaders, false));
|
|
614
|
+
return await listTools(client);
|
|
615
|
+
} catch (retryError) {
|
|
616
|
+
failure = retryError;
|
|
617
|
+
modernDiscoveries.delete(cacheKey);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (is401Error(failure) || isAbortOrTimeoutError(failure)) throw failure;
|
|
621
|
+
const status = httpStatusOf(failure);
|
|
622
|
+
if (status === 404 || status === 405) return { handled: false };
|
|
623
|
+
if (isLegacyEraNegotiationFailure(failure, status)) return { handled: false };
|
|
624
|
+
throw failure;
|
|
625
|
+
} finally {
|
|
626
|
+
if (client && !sharedClients.has(client)) await closeModernClient(client).catch(() => {
|
|
627
|
+
});
|
|
531
628
|
}
|
|
532
629
|
}
|
|
533
|
-
|
|
534
|
-
const status = httpStatusOf(failure);
|
|
535
|
-
if (status === 404 || status === 405) return { handled: false };
|
|
536
|
-
if (isLegacyEraNegotiationFailure(failure, status)) return { handled: false };
|
|
537
|
-
throw failure;
|
|
538
|
-
} finally {
|
|
539
|
-
if (client) await closeModernClient(client).catch(() => {
|
|
540
|
-
});
|
|
541
|
-
}
|
|
630
|
+
);
|
|
542
631
|
}
|
|
543
632
|
async function closeModernMCPConnections() {
|
|
544
633
|
connectionGeneration++;
|
|
@@ -13,6 +13,21 @@ export declare function closeCurrentMCPConnectionScope(): Promise<boolean>;
|
|
|
13
13
|
* Isolate connection reuse to one runner/workflow and close it on every exit.
|
|
14
14
|
* Nested callers join the existing scope so a storyboard still gets exactly
|
|
15
15
|
* one reusable session rather than one session per helper layer.
|
|
16
|
+
*
|
|
17
|
+
* Every MCP call made inside `fn` — endpoint probe, capability discovery,
|
|
18
|
+
* `tools/list`, and tool calls — reuses one negotiated session per
|
|
19
|
+
* (endpoint, credential, tenant headers, signing identity, transport policy).
|
|
20
|
+
* Per-call correlation headers, `AbortSignal`s and timeouts do not choose the
|
|
21
|
+
* session. The scope terminates its sessions once, when `fn` settles; pass
|
|
22
|
+
* `{ isolate: true }` to force a fresh scope even when called from inside one.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* await withMCPConnectionScope(async () => {
|
|
27
|
+
* await client.syncAccounts(accounts);
|
|
28
|
+
* await client.getProducts(brief); // same MCP session, one `initialize`
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
16
31
|
*/
|
|
17
32
|
export declare function withMCPConnectionScope<T>(fn: () => Promise<T>, options?: {
|
|
18
33
|
isolate?: boolean;
|
|
@@ -13,6 +13,21 @@ export declare function closeCurrentMCPConnectionScope(): Promise<boolean>;
|
|
|
13
13
|
* Isolate connection reuse to one runner/workflow and close it on every exit.
|
|
14
14
|
* Nested callers join the existing scope so a storyboard still gets exactly
|
|
15
15
|
* one reusable session rather than one session per helper layer.
|
|
16
|
+
*
|
|
17
|
+
* Every MCP call made inside `fn` — endpoint probe, capability discovery,
|
|
18
|
+
* `tools/list`, and tool calls — reuses one negotiated session per
|
|
19
|
+
* (endpoint, credential, tenant headers, signing identity, transport policy).
|
|
20
|
+
* Per-call correlation headers, `AbortSignal`s and timeouts do not choose the
|
|
21
|
+
* session. The scope terminates its sessions once, when `fn` settles; pass
|
|
22
|
+
* `{ isolate: true }` to force a fresh scope even when called from inside one.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* await withMCPConnectionScope(async () => {
|
|
27
|
+
* await client.syncAccounts(accounts);
|
|
28
|
+
* await client.getProducts(brief); // same MCP session, one `initialize`
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
16
31
|
*/
|
|
17
32
|
export declare function withMCPConnectionScope<T>(fn: () => Promise<T>, options?: {
|
|
18
33
|
isolate?: boolean;
|
|
@@ -43,8 +43,10 @@ export interface MCPCallOptions {
|
|
|
43
43
|
/**
|
|
44
44
|
* Scoped fetch implementation used for MCP requests, OAuth discovery, and token exchange.
|
|
45
45
|
* Direct SDK calls retain isolated one-shot connections. Runner workflows opt into
|
|
46
|
-
* scoped reuse, where the exact fetch function,
|
|
47
|
-
*
|
|
46
|
+
* scoped reuse, where the exact fetch function, credential, signing identity,
|
|
47
|
+
* tenant/routing headers and transport policy participate in connection identity.
|
|
48
|
+
* The cancellation signal, timeout and correlation headers are per call: they
|
|
49
|
+
* bound that call's requests and never choose or split the session.
|
|
48
50
|
*/
|
|
49
51
|
fetchFn?: typeof fetch;
|
|
50
52
|
/** Explicitly allow private/loopback agent addresses for this connection. */
|
|
@@ -43,8 +43,10 @@ export interface MCPCallOptions {
|
|
|
43
43
|
/**
|
|
44
44
|
* Scoped fetch implementation used for MCP requests, OAuth discovery, and token exchange.
|
|
45
45
|
* Direct SDK calls retain isolated one-shot connections. Runner workflows opt into
|
|
46
|
-
* scoped reuse, where the exact fetch function,
|
|
47
|
-
*
|
|
46
|
+
* scoped reuse, where the exact fetch function, credential, signing identity,
|
|
47
|
+
* tenant/routing headers and transport policy participate in connection identity.
|
|
48
|
+
* The cancellation signal, timeout and correlation headers are per call: they
|
|
49
|
+
* bound that call's requests and never choose or split the session.
|
|
48
50
|
*/
|
|
49
51
|
fetchFn?: typeof fetch;
|
|
50
52
|
/** Explicitly allow private/loopback agent addresses for this connection. */
|