@astrasyncai/verification-gateway 2.4.14 → 2.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/index.d.mts CHANGED
@@ -4,7 +4,7 @@ export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as Acces
4
4
  export { e as express } from './express-ienhAXps.mjs';
5
5
  export { n as nextjs } from './nextjs-DSpisQst.mjs';
6
6
  export { aR as extractMcpCredentials, bg as setMcpMeta, b1 as transport } from './index-CEg_WG6y.mjs';
7
- export { McpMiddlewareOptions, createMcpMiddleware } from './adapters/mcp.mjs';
7
+ export { McpMiddlewareOptions, ToolGateConfig, createMcpMiddleware } from './adapters/mcp.mjs';
8
8
  export { AgentProtocol, AgentRecord, AstraSync, AstraSyncConfig, AstraSyncError, AuthenticationError, BuildGuidanceParams, FrameworkConfig, GuidanceEnvelope, HealthResponse, KYDRequiredError, ModelConfig, PDLSSConfig, PDLSSDuration, PDLSSLimits, PDLSSPurpose, PDLSSScope, PDLSSSelfInstantiation, PendingRegistrationResponse, PollRegistrationResult, RegisterOptions, RegisterResult, RegistrationDeniedError, RegistrationExpiredError, RegistrationResponse, RegistrationTimeoutError, VerifyResponse, WaitForApprovalOptions, buildGuidance } from './registration/index.mjs';
9
9
  export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-B5e2IDWU.mjs';
10
10
  import 'express';
@@ -50,6 +50,36 @@ declare function quickVerify(config: GatewayConfig, credentials: AgentCredential
50
50
  reason?: string;
51
51
  }>;
52
52
 
53
+ /**
54
+ * SDK-side discovery of canonical platform URLs via `/.well-known/agentic-commerce`.
55
+ *
56
+ * Fire-and-forget pre-fetch at middleware creation; first verify() awaits
57
+ * the in-flight promise if it hasn't resolved. 60-minute TTL with
58
+ * stale-while-revalidate background refresh.
59
+ */
60
+ interface WellKnownAgenticCommerce {
61
+ registrationUrl: string;
62
+ documentationUrl: string;
63
+ verifyAccessUrl: string;
64
+ }
65
+ /**
66
+ * Start a background fetch. Returns the promise for callers that need
67
+ * to await it (first verify() call).
68
+ */
69
+ declare function prefetchWellKnown(apiBaseUrl: string): Promise<WellKnownAgenticCommerce>;
70
+ /**
71
+ * Get cached well-known URLs. If stale, triggers a background refresh
72
+ * and returns stale data (stale-while-revalidate). If no cache exists,
73
+ * awaits the in-flight fetch or starts a new one.
74
+ */
75
+ declare function getWellKnownUrls(apiBaseUrl: string): Promise<WellKnownAgenticCommerce>;
76
+ /**
77
+ * Synchronous cache read — returns cached URLs or undefined.
78
+ * Never triggers a fetch. Used by verify() to avoid extra HTTP calls
79
+ * in the hot path; adapters are responsible for prefetching.
80
+ */
81
+ declare function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCommerce | undefined;
82
+
53
83
  /**
54
84
  * AstraSync Universal Verification Gateway
55
85
  *
@@ -77,4 +107,4 @@ declare function quickVerify(config: GatewayConfig, credentials: AgentCredential
77
107
 
78
108
  declare const VERSION = "2.0.0";
79
109
 
80
- export { AccessLevel, AgentCredentials, GatewayConfig, VERSION, VerificationRequest, VerificationResult, clearCache, extractCredentials, hasCredentials, quickVerify, verify };
110
+ export { AccessLevel, AgentCredentials, GatewayConfig, VERSION, VerificationRequest, VerificationResult, type WellKnownAgenticCommerce, clearCache, extractCredentials, getCachedWellKnownUrls, getWellKnownUrls, hasCredentials, prefetchWellKnown, quickVerify, verify };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as Acces
4
4
  export { e as express } from './express-CrfwoNAR.js';
5
5
  export { n as nextjs } from './nextjs-66R1KW8e.js';
6
6
  export { aR as extractMcpCredentials, bg as setMcpMeta, b1 as transport } from './index-CCdZxvAr.js';
7
- export { McpMiddlewareOptions, createMcpMiddleware } from './adapters/mcp.js';
7
+ export { McpMiddlewareOptions, ToolGateConfig, createMcpMiddleware } from './adapters/mcp.js';
8
8
  export { AgentProtocol, AgentRecord, AstraSync, AstraSyncConfig, AstraSyncError, AuthenticationError, BuildGuidanceParams, FrameworkConfig, GuidanceEnvelope, HealthResponse, KYDRequiredError, ModelConfig, PDLSSConfig, PDLSSDuration, PDLSSLimits, PDLSSPurpose, PDLSSScope, PDLSSSelfInstantiation, PendingRegistrationResponse, PollRegistrationResult, RegisterOptions, RegisterResult, RegistrationDeniedError, RegistrationExpiredError, RegistrationResponse, RegistrationTimeoutError, VerifyResponse, WaitForApprovalOptions, buildGuidance } from './registration/index.js';
9
9
  export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-DC5f8eoQ.js';
10
10
  import 'express';
@@ -50,6 +50,36 @@ declare function quickVerify(config: GatewayConfig, credentials: AgentCredential
50
50
  reason?: string;
51
51
  }>;
52
52
 
53
+ /**
54
+ * SDK-side discovery of canonical platform URLs via `/.well-known/agentic-commerce`.
55
+ *
56
+ * Fire-and-forget pre-fetch at middleware creation; first verify() awaits
57
+ * the in-flight promise if it hasn't resolved. 60-minute TTL with
58
+ * stale-while-revalidate background refresh.
59
+ */
60
+ interface WellKnownAgenticCommerce {
61
+ registrationUrl: string;
62
+ documentationUrl: string;
63
+ verifyAccessUrl: string;
64
+ }
65
+ /**
66
+ * Start a background fetch. Returns the promise for callers that need
67
+ * to await it (first verify() call).
68
+ */
69
+ declare function prefetchWellKnown(apiBaseUrl: string): Promise<WellKnownAgenticCommerce>;
70
+ /**
71
+ * Get cached well-known URLs. If stale, triggers a background refresh
72
+ * and returns stale data (stale-while-revalidate). If no cache exists,
73
+ * awaits the in-flight fetch or starts a new one.
74
+ */
75
+ declare function getWellKnownUrls(apiBaseUrl: string): Promise<WellKnownAgenticCommerce>;
76
+ /**
77
+ * Synchronous cache read — returns cached URLs or undefined.
78
+ * Never triggers a fetch. Used by verify() to avoid extra HTTP calls
79
+ * in the hot path; adapters are responsible for prefetching.
80
+ */
81
+ declare function getCachedWellKnownUrls(apiBaseUrl: string): WellKnownAgenticCommerce | undefined;
82
+
53
83
  /**
54
84
  * AstraSync Universal Verification Gateway
55
85
  *
@@ -77,4 +107,4 @@ declare function quickVerify(config: GatewayConfig, credentials: AgentCredential
77
107
 
78
108
  declare const VERSION = "2.0.0";
79
109
 
80
- export { AccessLevel, AgentCredentials, GatewayConfig, VERSION, VerificationRequest, VerificationResult, clearCache, extractCredentials, hasCredentials, quickVerify, verify };
110
+ export { AccessLevel, AgentCredentials, GatewayConfig, VERSION, VerificationRequest, VerificationResult, type WellKnownAgenticCommerce, clearCache, extractCredentials, getCachedWellKnownUrls, getWellKnownUrls, hasCredentials, prefetchWellKnown, quickVerify, verify };
package/dist/index.js CHANGED
@@ -53,11 +53,14 @@ __export(src_exports, {
53
53
  extractCredentials: () => extractCredentials,
54
54
  extractMcpCredentials: () => extractMcpCredentials,
55
55
  getAccessLevelForScore: () => getAccessLevelForScore,
56
+ getCachedWellKnownUrls: () => getCachedWellKnownUrls,
56
57
  getCapabilities: () => getCapabilities,
57
58
  getTrustLevel: () => getTrustLevel,
59
+ getWellKnownUrls: () => getWellKnownUrls,
58
60
  hasCredentials: () => hasCredentials,
59
61
  hasMinimumAccess: () => hasMinimumAccess,
60
62
  nextjs: () => nextjs_exports,
63
+ prefetchWellKnown: () => prefetchWellKnown,
61
64
  quickVerify: () => quickVerify,
62
65
  recordDecision: () => recordDecision2,
63
66
  sdk: () => sdk_exports,
@@ -191,6 +194,65 @@ function getCapabilities(accessLevel) {
191
194
  // src/version.ts
192
195
  var SDK_VERSION = "2.4.13";
193
196
 
197
+ // src/well-known.ts
198
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
199
+ var cache = /* @__PURE__ */ new Map();
200
+ var inflight = /* @__PURE__ */ new Map();
201
+ function wellKnownUrl(apiBaseUrl) {
202
+ const base = apiBaseUrl.replace(/\/api\/?$/, "");
203
+ return `${base}/.well-known/agentic-commerce`;
204
+ }
205
+ async function fetchWellKnown(apiBaseUrl) {
206
+ const url = wellKnownUrl(apiBaseUrl);
207
+ const response = await fetch(url, {
208
+ method: "GET",
209
+ headers: { Accept: "application/json" },
210
+ signal: AbortSignal.timeout(5e3)
211
+ });
212
+ if (!response.ok) {
213
+ throw new Error(
214
+ `AstraSync platform must expose /.well-known/agentic-commerce; got ${response.status} from ${url}. SDK cannot initialise without it.`
215
+ );
216
+ }
217
+ const data = await response.json();
218
+ if (!data.registrationUrl || !data.documentationUrl || !data.verifyAccessUrl) {
219
+ throw new Error(
220
+ `/.well-known/agentic-commerce response missing required fields (registrationUrl, documentationUrl, verifyAccessUrl).`
221
+ );
222
+ }
223
+ return data;
224
+ }
225
+ function prefetchWellKnown(apiBaseUrl) {
226
+ const existing = inflight.get(apiBaseUrl);
227
+ if (existing) return existing;
228
+ const promise = fetchWellKnown(apiBaseUrl).then((data) => {
229
+ cache.set(apiBaseUrl, { data, fetchedAt: Date.now() });
230
+ inflight.delete(apiBaseUrl);
231
+ return data;
232
+ }).catch((err) => {
233
+ inflight.delete(apiBaseUrl);
234
+ throw err;
235
+ });
236
+ inflight.set(apiBaseUrl, promise);
237
+ return promise;
238
+ }
239
+ async function getWellKnownUrls(apiBaseUrl) {
240
+ const entry = cache.get(apiBaseUrl);
241
+ if (entry) {
242
+ if (Date.now() - entry.fetchedAt > CACHE_TTL_MS) {
243
+ prefetchWellKnown(apiBaseUrl).catch(() => {
244
+ });
245
+ }
246
+ return entry.data;
247
+ }
248
+ const pending = inflight.get(apiBaseUrl);
249
+ if (pending) return pending;
250
+ return prefetchWellKnown(apiBaseUrl);
251
+ }
252
+ function getCachedWellKnownUrls(apiBaseUrl) {
253
+ return cache.get(apiBaseUrl)?.data;
254
+ }
255
+
194
256
  // src/verify.ts
195
257
  var DEFAULT_CONFIG = {
196
258
  apiBaseUrl: "https://astrasync.ai/api",
@@ -332,21 +394,22 @@ function extractCredentials(headers, query) {
332
394
  function hasCredentials(credentials) {
333
395
  return !!(credentials.astraId || credentials.apiKey || credentials.jwt);
334
396
  }
335
- function createGuidanceResponse(config, reason, options = {}) {
397
+ function createGuidanceResponse(_config, reason, options = {}) {
336
398
  const source = options.source ?? "no_credentials";
337
399
  const isApiError = source === "api_error";
400
+ const urls = options.urls;
338
401
  const guidance = isApiError ? {
339
402
  message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
340
- registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/agents/register`,
341
- documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
403
+ registrationUrl: urls?.registrationUrl ?? "",
404
+ documentationUrl: urls?.documentationUrl ?? "",
342
405
  steps: [
343
406
  "Retry the request with exponential backoff",
344
407
  "If failures persist, share the correlationId with support"
345
408
  ]
346
409
  } : {
347
410
  message: "This service verifies AI agents before granting access. Please register your agent with AstraSync.",
348
- registrationUrl: `${config.apiBaseUrl.replace("/api", "")}/agents/register`,
349
- documentationUrl: `${config.apiBaseUrl.replace("/api", "")}/docs/agent-access`,
411
+ registrationUrl: urls?.registrationUrl ?? "",
412
+ documentationUrl: urls?.documentationUrl ?? "",
350
413
  steps: [
351
414
  "Register for an AstraSync account",
352
415
  "Create and register your agent",
@@ -388,7 +451,7 @@ async function callVerifyAccessAPI(config, request) {
388
451
  const { credentials, ...requestData } = request;
389
452
  const body = {
390
453
  ...credentials.astraId && { agentId: credentials.astraId },
391
- purpose: requestData.purpose || "general"
454
+ ...requestData.purpose && { purpose: requestData.purpose }
392
455
  };
393
456
  if (requestData.action) body.action = requestData.action;
394
457
  if (requestData.resourceType) body.resourceType = requestData.resourceType;
@@ -468,6 +531,7 @@ async function callVerifyAccessAPI(config, request) {
468
531
  }
469
532
  async function verify(config, request) {
470
533
  const mergedConfig = { ...DEFAULT_CONFIG, ...config };
534
+ const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
471
535
  if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
472
536
  if (mergedConfig.strictInit) {
473
537
  await performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, true);
@@ -504,7 +568,8 @@ async function verify(config, request) {
504
568
  if (!apiResponse.success) {
505
569
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
506
570
  source: "api_error",
507
- correlationId: apiResponse.correlationId
571
+ correlationId: apiResponse.correlationId,
572
+ urls
508
573
  });
509
574
  }
510
575
  if (!apiResponse.access?.allowed) {
@@ -527,8 +592,8 @@ async function verify(config, request) {
527
592
  requiresApproval: apiResponse.access?.requiresApproval,
528
593
  guidance: {
529
594
  message: apiResponse.access?.reason || "Access denied by PDLSS policy",
530
- registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
531
- documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
595
+ registrationUrl: urls?.registrationUrl ?? "",
596
+ documentationUrl: urls?.documentationUrl ?? ""
532
597
  },
533
598
  verifiedAt: /* @__PURE__ */ new Date(),
534
599
  // Extract sessionId so decisions can be recorded for denials too
@@ -599,12 +664,12 @@ async function verify(config, request) {
599
664
  ];
600
665
  result.guidance = result.runtimeChallenge ? {
601
666
  message: `Verification failed: ${result.runtimeChallenge.reason || "runtime challenge failed"}`,
602
- registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
603
- documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/runtime-challenge`
667
+ registrationUrl: urls?.registrationUrl ?? "",
668
+ documentationUrl: urls?.documentationUrl ?? ""
604
669
  } : {
605
670
  message: result.recommendationReasons?.[0] || "Access denied by AstraSync recommendation",
606
- registrationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/agents/register`,
607
- documentationUrl: `${mergedConfig.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
671
+ registrationUrl: urls?.registrationUrl ?? "",
672
+ documentationUrl: urls?.documentationUrl ?? ""
608
673
  };
609
674
  } else if (result.recommendation === "step_up_required") {
610
675
  result.requiresStepUp = true;
@@ -887,6 +952,9 @@ function dedupeFailures(result) {
887
952
  return true;
888
953
  });
889
954
  }
955
+ if (result.denialReasons && result.denialReasons.length > 1) {
956
+ result.denialReasons = [...new Set(result.denialReasons)];
957
+ }
890
958
  }
891
959
  function defaultOnDenied(result, _req, res) {
892
960
  const statusCode = !result.identityVerified ? 401 : 403;
@@ -918,6 +986,10 @@ function createMiddleware(options) {
918
986
  caseInsensitiveRouteMatch = false,
919
987
  ...config
920
988
  } = options;
989
+ if (config.apiBaseUrl) {
990
+ prefetchWellKnown(config.apiBaseUrl).catch(() => {
991
+ });
992
+ }
921
993
  let cachedRoutes = [];
922
994
  let lastFetchAt = 0;
923
995
  let refreshing = null;
@@ -980,6 +1052,7 @@ function createMiddleware(options) {
980
1052
  }
981
1053
  return next();
982
1054
  }
1055
+ const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
983
1056
  const credentials = customExtractCredentials ? customExtractCredentials(req) : defaultExtractCredentials(req);
984
1057
  const shouldEnforce = routeConfig.minAccessLevel !== "none";
985
1058
  if (routeConfig.minAccessLevel === "none" && (!config.evaluateAlwaysIfCredentialed || !credentials.astraId)) {
@@ -1001,8 +1074,8 @@ function createMiddleware(options) {
1001
1074
  denialReasons: preCheckFailures.map((f) => f.message),
1002
1075
  guidance: {
1003
1076
  message: "Request exceeds counterparty-defined PDLSS limits.",
1004
- registrationUrl: `${config.apiBaseUrl?.replace("/api", "")}/agents/register`,
1005
- documentationUrl: `${config.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
1077
+ registrationUrl: wellKnownUrls?.registrationUrl ?? "",
1078
+ documentationUrl: wellKnownUrls?.documentationUrl ?? ""
1006
1079
  },
1007
1080
  verifiedAt: /* @__PURE__ */ new Date()
1008
1081
  };
@@ -1082,6 +1155,13 @@ function createMiddleware(options) {
1082
1155
  };
1083
1156
  result.failures = [...result.failures ?? [], insufficientFailure];
1084
1157
  result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
1158
+ if (!result.guidance && wellKnownUrls) {
1159
+ result.guidance = {
1160
+ message: insufficientFailure.message,
1161
+ registrationUrl: wellKnownUrls.registrationUrl,
1162
+ documentationUrl: wellKnownUrls.documentationUrl
1163
+ };
1164
+ }
1085
1165
  if (shouldRecordDecisions && sessionId) {
1086
1166
  recordDecision(config, sessionId, "denied", insufficientFailure.message).catch(() => {
1087
1167
  });
@@ -1099,6 +1179,13 @@ function createMiddleware(options) {
1099
1179
  };
1100
1180
  result.failures = [...result.failures ?? [], trustFailure];
1101
1181
  result.denialReasons = [trustFailure.message];
1182
+ if (!result.guidance && wellKnownUrls) {
1183
+ result.guidance = {
1184
+ message: trustFailure.message,
1185
+ registrationUrl: wellKnownUrls.registrationUrl,
1186
+ documentationUrl: wellKnownUrls.documentationUrl
1187
+ };
1188
+ }
1102
1189
  if (shouldRecordDecisions && sessionId) {
1103
1190
  recordDecision(config, sessionId, "denied", trustFailure.message).catch(() => {
1104
1191
  });
@@ -1139,6 +1226,14 @@ function createMiddleware(options) {
1139
1226
  verifiedAt: /* @__PURE__ */ new Date(),
1140
1227
  correlationId
1141
1228
  };
1229
+ const catchUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
1230
+ if (catchUrls) {
1231
+ result.guidance = {
1232
+ message: `Middleware threw ${errorClass} \u2014 failing closed`,
1233
+ registrationUrl: catchUrls.registrationUrl,
1234
+ documentationUrl: catchUrls.documentationUrl
1235
+ };
1236
+ }
1142
1237
  dedupeFailures(result);
1143
1238
  return onDenied(result, req, res);
1144
1239
  }
@@ -4143,7 +4238,7 @@ async function exportJwkFromKeyLike(keyLike) {
4143
4238
 
4144
4239
  // src/transport/registry/mastercard.ts
4145
4240
  function createMastercardRegistry(options = {}) {
4146
- const cache = /* @__PURE__ */ new Map();
4241
+ const cache2 = /* @__PURE__ */ new Map();
4147
4242
  const ttlSec = options.cacheTtlSec ?? 3600;
4148
4243
  const fetchFn = options.fetch ?? globalThis.fetch;
4149
4244
  let warned = false;
@@ -4160,7 +4255,7 @@ function createMastercardRegistry(options = {}) {
4160
4255
  }
4161
4256
  return null;
4162
4257
  }
4163
- const cached = cache.get(kid);
4258
+ const cached = cache2.get(kid);
4164
4259
  if (cached && cached.expiresAt > Date.now()) return cached.jwk;
4165
4260
  try {
4166
4261
  const res = await fetchFn(options.registryUrl);
@@ -4169,7 +4264,7 @@ function createMastercardRegistry(options = {}) {
4169
4264
  const keys = body.keys ?? [];
4170
4265
  for (const k of keys) {
4171
4266
  if (k.kid === kid) {
4172
- cache.set(kid, { jwk: k, expiresAt: Date.now() + ttlSec * 1e3 });
4267
+ cache2.set(kid, { jwk: k, expiresAt: Date.now() + ttlSec * 1e3 });
4173
4268
  return k;
4174
4269
  }
4175
4270
  }
@@ -4184,7 +4279,7 @@ function createMastercardRegistry(options = {}) {
4184
4279
  // src/transport/registry/web-bot-auth.ts
4185
4280
  var DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
4186
4281
  function createWebBotAuthRegistry(options = {}) {
4187
- const cache = /* @__PURE__ */ new Map();
4282
+ const cache2 = /* @__PURE__ */ new Map();
4188
4283
  const ttlSec = options.cacheTtlSec ?? 3600;
4189
4284
  const fetchFn = options.fetch ?? globalThis.fetch;
4190
4285
  return {
@@ -4193,7 +4288,7 @@ function createWebBotAuthRegistry(options = {}) {
4193
4288
  if (!kid) return null;
4194
4289
  const directoryUrl = resolveDirectoryUrl(options.directoryUrl, context?.origin);
4195
4290
  if (!directoryUrl) return null;
4196
- const cached = cache.get(directoryUrl);
4291
+ const cached = cache2.get(directoryUrl);
4197
4292
  const now = Date.now();
4198
4293
  if (cached && cached.expiresAt > now) {
4199
4294
  return findKeyByKid(cached.keys, kid);
@@ -4203,7 +4298,7 @@ function createWebBotAuthRegistry(options = {}) {
4203
4298
  if (!res.ok) return null;
4204
4299
  const body = await res.json();
4205
4300
  const keys = body.keys ?? [];
4206
- cache.set(directoryUrl, { keys, expiresAt: now + ttlSec * 1e3 });
4301
+ cache2.set(directoryUrl, { keys, expiresAt: now + ttlSec * 1e3 });
4207
4302
  return findKeyByKid(keys, kid);
4208
4303
  } catch {
4209
4304
  return null;
@@ -4342,19 +4437,22 @@ function extractFromMcpBody(astrasyncMeta, args, key) {
4342
4437
  }
4343
4438
  return { value: void 0, source: void 0 };
4344
4439
  }
4345
- function mcpToPdlss(parsed, headerPurpose, headerAction) {
4346
- const resource = parsed.toolName ? `mcp:tool/${parsed.toolName}` : `mcp:method/${parsed.method}`;
4440
+ function mcpToPdlss(parsed, requestPath, headerPurpose, headerAction, toolGate) {
4441
+ const resource = toolGate?.resource ?? requestPath;
4347
4442
  let purpose;
4348
4443
  let purposeSource;
4349
- if (headerPurpose) {
4444
+ if (toolGate?.purpose !== void 0) {
4445
+ purpose = toolGate.purpose;
4446
+ purposeSource = "tool_gate";
4447
+ } else if (headerPurpose) {
4350
4448
  purpose = headerPurpose;
4351
4449
  purposeSource = "header";
4352
4450
  } else if (parsed.purposeFromBody && parsed.purposeSourceFromBody) {
4353
4451
  purpose = parsed.purposeFromBody;
4354
4452
  purposeSource = parsed.purposeSourceFromBody;
4355
4453
  } else {
4356
- purpose = "mcp_invoke";
4357
- purposeSource = "default_mcp_invoke";
4454
+ purpose = void 0;
4455
+ purposeSource = void 0;
4358
4456
  }
4359
4457
  let action;
4360
4458
  let actionSource;
@@ -4379,6 +4477,9 @@ function mcpRiskTier(parsed) {
4379
4477
  }
4380
4478
 
4381
4479
  // src/adapters/mcp.ts
4480
+ function normalizeToolGate(gate) {
4481
+ return typeof gate === "string" ? { minAccessLevel: gate } : gate;
4482
+ }
4382
4483
  function readSingleHeader(value) {
4383
4484
  if (typeof value === "string") return value;
4384
4485
  if (Array.isArray(value)) return value[0];
@@ -4394,6 +4495,9 @@ function dedupeFailures2(result) {
4394
4495
  return true;
4395
4496
  });
4396
4497
  }
4498
+ if (result.denialReasons && result.denialReasons.length > 1) {
4499
+ result.denialReasons = [...new Set(result.denialReasons)];
4500
+ }
4397
4501
  }
4398
4502
  function defaultMcpDenied(result, req, res) {
4399
4503
  const id = req.body?.id ?? null;
@@ -4419,11 +4523,17 @@ function defaultMcpDenied(result, req, res) {
4419
4523
  });
4420
4524
  }
4421
4525
  function resolveMinAccessLevel(parsed, opts) {
4422
- if (parsed.toolName && opts.toolGates && opts.toolGates[parsed.toolName] !== void 0) {
4423
- return { level: opts.toolGates[parsed.toolName], source: "toolGate" };
4526
+ if (!parsed.toolName) {
4527
+ if (opts.methodGates && opts.methodGates[parsed.method] !== void 0) {
4528
+ return { level: opts.methodGates[parsed.method], source: "methodGate" };
4529
+ }
4530
+ return { level: "none", source: "discovery_default" };
4424
4531
  }
4425
- if (opts.methodGates && opts.methodGates[parsed.method] !== void 0) {
4426
- return { level: opts.methodGates[parsed.method], source: "methodGate" };
4532
+ if (opts.toolGates && opts.toolGates[parsed.toolName] !== void 0) {
4533
+ return {
4534
+ level: normalizeToolGate(opts.toolGates[parsed.toolName]).minAccessLevel,
4535
+ source: "toolGate"
4536
+ };
4427
4537
  }
4428
4538
  return { level: mcpRiskTier(parsed), source: "tier" };
4429
4539
  }
@@ -4441,6 +4551,10 @@ function createMcpMiddleware(options) {
4441
4551
  failOnError = "open",
4442
4552
  ...config
4443
4553
  } = options;
4554
+ if (config.apiBaseUrl) {
4555
+ prefetchWellKnown(config.apiBaseUrl).catch(() => {
4556
+ });
4557
+ }
4444
4558
  return async (req, res, next) => {
4445
4559
  try {
4446
4560
  if (skip) return next();
@@ -4453,6 +4567,7 @@ function createMcpMiddleware(options) {
4453
4567
  return next();
4454
4568
  }
4455
4569
  req.mcpRequest = parsed;
4570
+ const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
4456
4571
  const headerRaw = req.headers["x-astra-id"] ?? req.headers["x-astra-agentid"];
4457
4572
  const headerAstraId = typeof headerRaw === "string" ? headerRaw : Array.isArray(headerRaw) ? headerRaw[0] : void 0;
4458
4573
  const bodyAstraId = parsed.agentIdFromBody;
@@ -4506,9 +4621,17 @@ function createMcpMiddleware(options) {
4506
4621
  }
4507
4622
  return next();
4508
4623
  }
4624
+ const rawGate = parsed.toolName && toolGates?.[parsed.toolName];
4625
+ const gate = rawGate ? normalizeToolGate(rawGate) : void 0;
4509
4626
  const headerPurpose = readSingleHeader(req.headers["x-astra-purpose"]);
4510
4627
  const headerAction = readSingleHeader(req.headers["x-astra-action"]);
4511
- const pdlss = mcpToPdlss(parsed, headerPurpose, headerAction);
4628
+ const pdlss = mcpToPdlss(
4629
+ parsed,
4630
+ req.path,
4631
+ headerPurpose,
4632
+ headerAction,
4633
+ gate ? { purpose: gate.purpose, resource: gate.resource } : void 0
4634
+ );
4512
4635
  if (config.debug) {
4513
4636
  console.debug("[mcp-middleware] pdlss resolved", {
4514
4637
  purpose_source: pdlss.purposeSource,
@@ -4569,6 +4692,13 @@ function createMcpMiddleware(options) {
4569
4692
  };
4570
4693
  result.failures = [...result.failures ?? [], insufficientFailure];
4571
4694
  result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
4695
+ if (!result.guidance && wellKnownUrls) {
4696
+ result.guidance = {
4697
+ message: insufficientFailure.message,
4698
+ registrationUrl: wellKnownUrls.registrationUrl,
4699
+ documentationUrl: wellKnownUrls.documentationUrl
4700
+ };
4701
+ }
4572
4702
  if (shouldRecordDecisions) {
4573
4703
  const overrideKind = gateSource === "toolGate" ? "toolGate" : gateSource === "methodGate" ? "methodGate" : "other";
4574
4704
  const override = {
@@ -4637,6 +4767,14 @@ function createMcpMiddleware(options) {
4637
4767
  verifiedAt: /* @__PURE__ */ new Date(),
4638
4768
  correlationId
4639
4769
  };
4770
+ const catchUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
4771
+ if (catchUrls) {
4772
+ result.guidance = {
4773
+ message: `Middleware threw ${errorClass} \u2014 failing closed`,
4774
+ registrationUrl: catchUrls.registrationUrl,
4775
+ documentationUrl: catchUrls.documentationUrl
4776
+ };
4777
+ }
4640
4778
  dedupeFailures2(result);
4641
4779
  return onDenied(result, req, res);
4642
4780
  }
@@ -5347,11 +5485,14 @@ var VERSION = "2.0.0";
5347
5485
  extractCredentials,
5348
5486
  extractMcpCredentials,
5349
5487
  getAccessLevelForScore,
5488
+ getCachedWellKnownUrls,
5350
5489
  getCapabilities,
5351
5490
  getTrustLevel,
5491
+ getWellKnownUrls,
5352
5492
  hasCredentials,
5353
5493
  hasMinimumAccess,
5354
5494
  nextjs,
5495
+ prefetchWellKnown,
5355
5496
  quickVerify,
5356
5497
  recordDecision,
5357
5498
  sdk,