@agentcash/discovery 1.1.3 → 1.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/dist/index.d.ts CHANGED
@@ -89,12 +89,18 @@ interface OpenApiRoute {
89
89
  interface WellKnownSource {
90
90
  raw: Record<string, unknown>;
91
91
  routes: WellKnownRoute[];
92
+ title?: string;
93
+ description?: string;
92
94
  instructions?: string;
93
95
  fetchedUrl: string;
96
+ /** Which well-known document(s) this source was built from. */
97
+ protocol: 'x402' | 'mpp' | 'x402+mpp';
94
98
  }
95
99
  interface WellKnownRoute {
96
100
  path: string;
97
101
  method: HttpMethod;
102
+ /** Raw price hint from the well-known document (e.g. MPP `payment.amount`). */
103
+ price?: string;
98
104
  }
99
105
  interface ProbeResult {
100
106
  path: string;
@@ -111,7 +117,7 @@ interface L2Result {
111
117
  description?: string;
112
118
  version?: string;
113
119
  routes: L2Route[];
114
- source: 'openapi' | 'well-known/x402' | null;
120
+ source: 'openapi' | 'well-known/x402' | 'well-known/mpp' | 'well-known/x402+mpp' | null;
115
121
  }
116
122
  interface L2Route {
117
123
  path: string;
@@ -137,10 +143,15 @@ interface L3Result {
137
143
  * and returned a 402. Used by getWarningsForL3 to run full payment-required validation.
138
144
  */
139
145
  paymentRequiredBody?: unknown;
146
+ /**
147
+ * Raw WWW-Authenticate header value from the 402 response. Present when the endpoint
148
+ * was probed and returned a 402 with an MPP challenge. Used by getWarningsForMppHeader.
149
+ */
150
+ wwwAuthenticate?: string;
140
151
  }
141
152
  interface L4Result {
142
153
  guidance: string;
143
- source: 'openapi' | 'well-known/x402';
154
+ source: 'openapi' | 'well-known/x402' | 'well-known/mpp' | 'well-known/x402+mpp';
144
155
  }
145
156
 
146
157
  declare enum GuidanceMode {
@@ -236,8 +247,21 @@ interface FetchError {
236
247
 
237
248
  declare function getOpenAPI(origin: string, headers?: Record<string, string>, signal?: AbortSignal, specificationOverrideUrl?: string): ResultAsync<OpenApiSource | null, FetchError>;
238
249
 
250
+ /**
251
+ * Fetches both `/.well-known/x402` and `/.well-known/mpp` in parallel and merges results.
252
+ *
253
+ * In practice these are mutually exclusive, but if both exist their routes are combined
254
+ * (deduplicated by method+path). x402 wins on instruction/fetchedUrl conflicts.
255
+ *
256
+ * Individual leg failures are treated as "not found" for that leg so valid data from
257
+ * the other is never suppressed. Returns Err(FetchError) only when both legs hard-fail.
258
+ */
239
259
  declare function getWellKnown(origin: string, headers?: Record<string, string>, signal?: AbortSignal): ResultAsync<WellKnownSource | null, FetchError>;
240
260
 
261
+ declare function getX402WellKnown(origin: string, headers?: Record<string, string>, signal?: AbortSignal): ResultAsync<WellKnownSource | null, FetchError>;
262
+
263
+ declare function getMppWellKnown(origin: string, headers?: Record<string, string>, signal?: AbortSignal): ResultAsync<WellKnownSource | null, FetchError>;
264
+
241
265
  declare function getProbe(url: string, headers?: Record<string, string>, signal?: AbortSignal, inputBody?: Record<string, unknown>): ResultAsync<ProbeResult[], FetchError>;
242
266
 
243
267
  declare function checkL2ForOpenAPI(openApi: OpenApiSource): L2Result;
@@ -361,8 +385,21 @@ declare const AUDIT_CODES: {
361
385
  readonly L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING";
362
386
  readonly L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING";
363
387
  readonly L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID";
388
+ readonly L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID";
364
389
  readonly L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING";
365
390
  readonly L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG";
391
+ readonly MPP_HEADER_MISSING: "MPP_HEADER_MISSING";
392
+ readonly MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES";
393
+ readonly MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING";
394
+ readonly MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING";
395
+ readonly MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING";
396
+ readonly MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING";
397
+ readonly MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING";
398
+ readonly MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING";
399
+ readonly MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID";
400
+ readonly MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING";
401
+ readonly MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING";
402
+ readonly MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING";
366
403
  };
367
404
  type AuditCode = (typeof AUDIT_CODES)[keyof typeof AUDIT_CODES];
368
405
 
@@ -392,4 +429,18 @@ declare function getWarningsForL3(l3: L3Result | null): AuditWarning[];
392
429
 
393
430
  declare function getWarningsForL4(l4: L4Result | null): AuditWarning[];
394
431
 
395
- export { AUDIT_CODES, type AuditCode, type AuditSeverity, type AuditWarning, type AuthMode, type CheckEndpointNotFound, type CheckEndpointOptions, type CheckEndpointResult, type CheckEndpointSuccess, type DiscoverOriginSchemaNotFound, type DiscoverOriginSchemaOptions, type DiscoverOriginSchemaResult, type DiscoverOriginSchemaSuccess, type EndpointMethodAdvisory, GuidanceMode, type HttpMethod, type L2Result, type L2Route, type L3Result, type L4Result, type MetadataPreview, type MppPaymentOption, type NormalizedAccept, type NormalizedPaymentRequired, type OpenApiRoute, type OpenApiSource, type PaymentOption, type PricingMode, type ProbeResult, type TrustTier, VALIDATION_CODES, type ValidatePaymentRequiredDetailedResult, type ValidatePaymentRequiredOptions, type ValidationIssue, type ValidationSeverity, type ValidationStage, type ValidationSummary, type WellKnownRoute, type WellKnownSource, type X402PaymentOption, type X402V1PaymentOption, type X402V2PaymentOption, attachProbePayload, checkEndpointSchema, checkL2ForOpenAPI, checkL2ForWellknown, checkL4ForOpenAPI, checkL4ForWellknown, discoverOriginSchema, evaluateMetadataCompleteness, getL3, getL3ForOpenAPI, getL3ForProbe, getOpenAPI, getProbe, getWarningsFor402Body, getWarningsForL2, getWarningsForL3, getWarningsForL4, getWarningsForOpenAPI, getWarningsForWellKnown, getWellKnown, validatePaymentRequiredDetailed };
432
+ /**
433
+ * Validates a raw WWW-Authenticate header value from an MPP 402 response and
434
+ * returns issues as AuditWarnings.
435
+ *
436
+ * Checks for:
437
+ * - Header presence
438
+ * - At least one Payment challenge
439
+ * - Required challenge parameters: id, method, intent, realm, expires, request
440
+ * - Valid base64url-encoded JSON in the request field
441
+ * - Required request fields: currency (asset), amount
442
+ * - Recommended request field: recipient (payTo)
443
+ */
444
+ declare function getWarningsForMppHeader(wwwAuthenticate: string | null | undefined): AuditWarning[];
445
+
446
+ export { AUDIT_CODES, type AuditCode, type AuditSeverity, type AuditWarning, type AuthMode, type CheckEndpointNotFound, type CheckEndpointOptions, type CheckEndpointResult, type CheckEndpointSuccess, type DiscoverOriginSchemaNotFound, type DiscoverOriginSchemaOptions, type DiscoverOriginSchemaResult, type DiscoverOriginSchemaSuccess, type EndpointMethodAdvisory, GuidanceMode, type HttpMethod, type L2Result, type L2Route, type L3Result, type L4Result, type MetadataPreview, type MppPaymentOption, type NormalizedAccept, type NormalizedPaymentRequired, type OpenApiRoute, type OpenApiSource, type PaymentOption, type PricingMode, type ProbeResult, type TrustTier, VALIDATION_CODES, type ValidatePaymentRequiredDetailedResult, type ValidatePaymentRequiredOptions, type ValidationIssue, type ValidationSeverity, type ValidationStage, type ValidationSummary, type WellKnownRoute, type WellKnownSource, type X402PaymentOption, type X402V1PaymentOption, type X402V2PaymentOption, attachProbePayload, checkEndpointSchema, checkL2ForOpenAPI, checkL2ForWellknown, checkL4ForOpenAPI, checkL4ForWellknown, discoverOriginSchema, evaluateMetadataCompleteness, getL3, getL3ForOpenAPI, getL3ForProbe, getMppWellKnown, getOpenAPI, getProbe, getWarningsFor402Body, getWarningsForL2, getWarningsForL3, getWarningsForL4, getWarningsForMppHeader, getWarningsForOpenAPI, getWarningsForWellKnown, getWellKnown, getX402WellKnown, validatePaymentRequiredDetailed };
package/dist/index.js CHANGED
@@ -13842,7 +13842,8 @@ var WellKnownParsedSchema = external_exports.object({
13842
13842
  routes: external_exports.array(
13843
13843
  external_exports.object({
13844
13844
  path: external_exports.string(),
13845
- method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"])
13845
+ method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]),
13846
+ price: external_exports.string().optional()
13846
13847
  })
13847
13848
  ),
13848
13849
  instructions: external_exports.string().optional()
@@ -13935,9 +13936,9 @@ function toAbsoluteUrl(origin, value) {
13935
13936
 
13936
13937
  // src/core/source/fetch.ts
13937
13938
  import { ResultAsync } from "neverthrow";
13938
- function toFetchError(err) {
13939
- const cause = err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError") ? "timeout" : "network";
13940
- return { cause, message: String(err) };
13939
+ function toFetchError(err2) {
13940
+ const cause = err2 instanceof DOMException && (err2.name === "TimeoutError" || err2.name === "AbortError") ? "timeout" : "network";
13941
+ return { cause, message: String(err2) };
13941
13942
  }
13942
13943
  function fetchSafe(url2, init) {
13943
13944
  return ResultAsync.fromPromise(fetch(url2, init), toFetchError);
@@ -14003,6 +14004,9 @@ function getOpenAPI(origin, headers, signal, specificationOverrideUrl) {
14003
14004
  }
14004
14005
 
14005
14006
  // src/core/source/wellknown/index.ts
14007
+ import { ok, err, ResultAsync as ResultAsync5 } from "neverthrow";
14008
+
14009
+ // src/core/source/wellknown/x402.ts
14006
14010
  import { okAsync as okAsync2, ResultAsync as ResultAsync3 } from "neverthrow";
14007
14011
  function toWellKnownParsed(origin, doc) {
14008
14012
  const routes = doc.resources.flatMap((entry) => {
@@ -14029,12 +14033,17 @@ async function parseBody2(response, origin, url2) {
14029
14033
  if (!doc.success) return null;
14030
14034
  const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed(origin, doc.data));
14031
14035
  if (!parsed.success) return null;
14032
- return { raw: payload, ...parsed.data, fetchedUrl: url2 };
14036
+ return {
14037
+ raw: payload,
14038
+ ...parsed.data,
14039
+ protocol: "x402",
14040
+ fetchedUrl: url2
14041
+ };
14033
14042
  } catch {
14034
14043
  return null;
14035
14044
  }
14036
14045
  }
14037
- function getWellKnown(origin, headers, signal) {
14046
+ function getX402WellKnown(origin, headers, signal) {
14038
14047
  const url2 = `${origin}/.well-known/x402`;
14039
14048
  return fetchSafe(url2, {
14040
14049
  method: "GET",
@@ -14046,6 +14055,127 @@ function getWellKnown(origin, headers, signal) {
14046
14055
  });
14047
14056
  }
14048
14057
 
14058
+ // src/core/source/wellknown/mpp.ts
14059
+ import { okAsync as okAsync3, ResultAsync as ResultAsync4 } from "neverthrow";
14060
+ var MppEndpointSchema = external_exports.object({
14061
+ method: external_exports.string(),
14062
+ path: external_exports.string(),
14063
+ description: external_exports.string().optional(),
14064
+ payment: external_exports.object({
14065
+ intent: external_exports.string().optional(),
14066
+ method: external_exports.string().optional(),
14067
+ amount: external_exports.string().optional(),
14068
+ currency: external_exports.string().optional()
14069
+ }).optional()
14070
+ });
14071
+ var MppWellKnownDocSchema = external_exports.object({
14072
+ version: external_exports.number().optional(),
14073
+ name: external_exports.string().optional(),
14074
+ description: external_exports.string().optional(),
14075
+ categories: external_exports.array(external_exports.string()).optional(),
14076
+ methods: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
14077
+ endpoints: external_exports.array(MppEndpointSchema).default([]),
14078
+ docs: external_exports.object({
14079
+ homepage: external_exports.string().optional(),
14080
+ apiReference: external_exports.string().optional()
14081
+ }).optional()
14082
+ });
14083
+ var MPP_DECIMALS = 6;
14084
+ function formatMppAmount(raw) {
14085
+ if (!raw) return void 0;
14086
+ const n = Number(raw);
14087
+ if (!Number.isFinite(n)) return void 0;
14088
+ return `$${(n / 10 ** MPP_DECIMALS).toFixed(MPP_DECIMALS)}`;
14089
+ }
14090
+ function toWellKnownParsed2(doc) {
14091
+ const routes = doc.endpoints.flatMap((entry) => {
14092
+ const method = parseMethod(entry.method);
14093
+ if (!method) return [];
14094
+ const path = normalizePath(entry.path);
14095
+ if (!path) return [];
14096
+ const price = formatMppAmount(entry.payment?.amount);
14097
+ return [{ path, method, ...price ? { price } : {} }];
14098
+ });
14099
+ return {
14100
+ routes,
14101
+ ...doc.description ? { instructions: doc.description } : {}
14102
+ };
14103
+ }
14104
+ async function parseBody3(response, url2) {
14105
+ try {
14106
+ const payload = await response.json();
14107
+ const doc = MppWellKnownDocSchema.safeParse(payload);
14108
+ if (!doc.success) return null;
14109
+ const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed2(doc.data));
14110
+ if (!parsed.success) return null;
14111
+ return {
14112
+ raw: payload,
14113
+ ...parsed.data,
14114
+ ...doc.data.name ? { title: doc.data.name } : {},
14115
+ ...doc.data.description ? { description: doc.data.description } : {},
14116
+ protocol: "mpp",
14117
+ fetchedUrl: url2
14118
+ };
14119
+ } catch {
14120
+ return null;
14121
+ }
14122
+ }
14123
+ function getMppWellKnown(origin, headers, signal) {
14124
+ const url2 = `${origin}/.well-known/mpp`;
14125
+ return fetchSafe(url2, {
14126
+ method: "GET",
14127
+ headers: { Accept: "application/json", ...headers },
14128
+ signal
14129
+ }).andThen((response) => {
14130
+ if (!response.ok) return okAsync3(null);
14131
+ return ResultAsync4.fromSafePromise(parseBody3(response, url2));
14132
+ });
14133
+ }
14134
+
14135
+ // src/core/source/wellknown/index.ts
14136
+ function mergeError(a, b) {
14137
+ return {
14138
+ cause: a.cause === "network" || b.cause === "network" ? "network" : "timeout",
14139
+ message: `x402: ${a.message} | mpp: ${b.message}`
14140
+ };
14141
+ }
14142
+ function merge2(x402, mpp) {
14143
+ const seen = /* @__PURE__ */ new Set();
14144
+ const routes = [...x402.routes, ...mpp.routes].filter((r) => {
14145
+ const key = `${r.method} ${r.path}`;
14146
+ if (seen.has(key)) return false;
14147
+ seen.add(key);
14148
+ return true;
14149
+ });
14150
+ return {
14151
+ raw: { ...mpp.raw, ...x402.raw },
14152
+ routes,
14153
+ protocol: "x402+mpp",
14154
+ // Prefer x402 instructions; fall back to mpp
14155
+ ...x402.instructions || mpp.instructions ? { instructions: x402.instructions ?? mpp.instructions } : {},
14156
+ fetchedUrl: x402.fetchedUrl
14157
+ };
14158
+ }
14159
+ function getWellKnown(origin, headers, signal) {
14160
+ return new ResultAsync5(
14161
+ Promise.all([
14162
+ getX402WellKnown(origin, headers, signal),
14163
+ getMppWellKnown(origin, headers, signal)
14164
+ ]).then(([x402Result, mppResult]) => {
14165
+ const x402 = x402Result.isOk() ? x402Result.value : null;
14166
+ const mpp = mppResult.isOk() ? mppResult.value : null;
14167
+ if (x402 && mpp) return ok(merge2(x402, mpp));
14168
+ if (x402) return ok(x402);
14169
+ if (mpp) return ok(mpp);
14170
+ if (x402Result.isErr() && mppResult.isErr())
14171
+ return err(mergeError(x402Result.error, mppResult.error));
14172
+ if (x402Result.isErr()) return err(x402Result.error);
14173
+ if (mppResult.isErr()) return err(mppResult.error);
14174
+ return ok(null);
14175
+ })
14176
+ );
14177
+ }
14178
+
14049
14179
  // src/core/layers/l2.ts
14050
14180
  function formatPrice(pricing) {
14051
14181
  if (pricing.pricingMode === "fixed") return `$${pricing.price}`;
@@ -14071,15 +14201,27 @@ function checkL2ForOpenAPI(openApi) {
14071
14201
  source: "openapi"
14072
14202
  };
14073
14203
  }
14204
+ var WELL_KNOWN_PROTOCOLS = {
14205
+ x402: ["x402"],
14206
+ mpp: ["mpp"],
14207
+ "x402+mpp": ["x402", "mpp"]
14208
+ };
14074
14209
  function checkL2ForWellknown(wellKnown) {
14210
+ const protocols = WELL_KNOWN_PROTOCOLS[wellKnown.protocol];
14075
14211
  const routes = wellKnown.routes.map((route) => ({
14076
14212
  path: route.path,
14077
14213
  method: route.method,
14078
14214
  summary: `${route.method} ${route.path}`,
14079
14215
  authMode: "paid",
14080
- protocols: ["x402"]
14216
+ protocols,
14217
+ ...route.price ? { price: route.price } : {}
14081
14218
  }));
14082
- return { routes, source: "well-known/x402" };
14219
+ return {
14220
+ ...wellKnown.title ? { title: wellKnown.title } : {},
14221
+ ...wellKnown.description ? { description: wellKnown.description } : {},
14222
+ routes,
14223
+ source: `well-known/${wellKnown.protocol}`
14224
+ };
14083
14225
  }
14084
14226
 
14085
14227
  // src/core/layers/l4.ts
@@ -14091,7 +14233,7 @@ function checkL4ForOpenAPI(openApi) {
14091
14233
  }
14092
14234
  function checkL4ForWellknown(wellKnown) {
14093
14235
  if (wellKnown.instructions) {
14094
- return { guidance: wellKnown.instructions, source: "well-known/x402" };
14236
+ return { guidance: wellKnown.instructions, source: `well-known/${wellKnown.protocol}` };
14095
14237
  }
14096
14238
  return null;
14097
14239
  }
@@ -14164,14 +14306,20 @@ async function discoverOriginSchema(options) {
14164
14306
  const base = {
14165
14307
  found: true,
14166
14308
  origin,
14167
- source: "well-known/x402",
14309
+ source: l2.source,
14310
+ ...l2.title ? {
14311
+ info: {
14312
+ title: l2.title,
14313
+ ...l2.description ? { description: l2.description } : {}
14314
+ }
14315
+ } : {},
14168
14316
  endpoints: l2.routes
14169
14317
  };
14170
14318
  return withGuidance(base, l4, guidanceMode);
14171
14319
  }
14172
14320
 
14173
14321
  // src/core/source/probe/index.ts
14174
- import { ResultAsync as ResultAsync4 } from "neverthrow";
14322
+ import { ResultAsync as ResultAsync6 } from "neverthrow";
14175
14323
 
14176
14324
  // src/core/protocols/x402/v1/schema.ts
14177
14325
  function extractSchemas(accepts) {
@@ -14621,8 +14769,8 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
14621
14769
  ...hasBody ? { body: JSON.stringify(inputBody) } : {},
14622
14770
  signal
14623
14771
  }).andThen((response) => {
14624
- if (!isUsableStatus(response.status)) return ResultAsync4.fromSafePromise(Promise.resolve(null));
14625
- return ResultAsync4.fromSafePromise(
14772
+ if (!isUsableStatus(response.status)) return ResultAsync6.fromSafePromise(Promise.resolve(null));
14773
+ return ResultAsync6.fromSafePromise(
14626
14774
  (async () => {
14627
14775
  let authHint = response.status === 402 ? "paid" : "unprotected";
14628
14776
  let paymentRequiredBody;
@@ -14653,7 +14801,7 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
14653
14801
  }
14654
14802
  function getProbe(url2, headers, signal, inputBody) {
14655
14803
  const path = normalizePath(new URL(url2).pathname || "/");
14656
- return ResultAsync4.fromSafePromise(
14804
+ return ResultAsync6.fromSafePromise(
14657
14805
  Promise.all(
14658
14806
  [...HTTP_METHODS].map(
14659
14807
  (method) => probeMethod(url2, method, path, headers, signal, inputBody).match(
@@ -14666,6 +14814,20 @@ function getProbe(url2, headers, signal, inputBody) {
14666
14814
  }
14667
14815
 
14668
14816
  // src/core/protocols/mpp/index.ts
14817
+ import { Result } from "neverthrow";
14818
+ function parseBase64Json(encoded) {
14819
+ return Result.fromThrowable(
14820
+ () => {
14821
+ const decoded = typeof Buffer !== "undefined" ? Buffer.from(encoded, "base64").toString("utf8") : atob(encoded);
14822
+ const parsed = JSON.parse(decoded);
14823
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
14824
+ throw new Error("not an object");
14825
+ }
14826
+ return parsed;
14827
+ },
14828
+ (e) => e
14829
+ )();
14830
+ }
14669
14831
  var TEMPO_DEFAULT_CHAIN_ID = 4217;
14670
14832
  function parseAuthParams(segment) {
14671
14833
  const params = {};
@@ -14688,14 +14850,9 @@ function extractPaymentOptions4(wwwAuthenticate) {
14688
14850
  const description = params["description"];
14689
14851
  const requestStr = params["request"];
14690
14852
  if (!paymentMethod || !intent || !realm || !requestStr) continue;
14691
- let request;
14692
- try {
14693
- const parsed = JSON.parse(requestStr);
14694
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
14695
- request = parsed;
14696
- } catch {
14697
- continue;
14698
- }
14853
+ const requestResult = parseBase64Json(requestStr);
14854
+ if (requestResult.isErr()) continue;
14855
+ const request = requestResult.value;
14699
14856
  const asset = typeof request["currency"] === "string" ? request["currency"] : void 0;
14700
14857
  const amountRaw = request["amount"];
14701
14858
  const amount = typeof amountRaw === "string" ? amountRaw : typeof amountRaw === "number" ? String(amountRaw) : void 0;
@@ -14877,7 +15034,8 @@ function getL3ForProbe(probe, path, method) {
14877
15034
  ...inputSchema ? { inputSchema } : {},
14878
15035
  ...outputSchema ? { outputSchema } : {},
14879
15036
  ...paymentOptions.length ? { paymentOptions } : {},
14880
- ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
15037
+ ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
15038
+ ...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
14881
15039
  };
14882
15040
  }
14883
15041
  async function attachProbePayload(url2, advisories) {
@@ -15058,9 +15216,23 @@ var AUDIT_CODES = {
15058
15216
  L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
15059
15217
  L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
15060
15218
  L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
15219
+ L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
15061
15220
  // ─── L4 guidance checks ──────────────────────────────────────────────────────
15062
15221
  L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
15063
- L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
15222
+ L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
15223
+ // ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
15224
+ MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
15225
+ MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
15226
+ MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
15227
+ MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
15228
+ MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
15229
+ MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
15230
+ MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
15231
+ MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
15232
+ MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
15233
+ MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
15234
+ MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
15235
+ MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
15064
15236
  };
15065
15237
 
15066
15238
  // src/core/protocols/x402/v1/coinbase-schema.ts
@@ -15149,6 +15321,157 @@ function validateWithCoinbaseSchema2(body) {
15149
15321
  });
15150
15322
  }
15151
15323
 
15324
+ // src/audit/warnings/mpp.ts
15325
+ function parseAuthParams2(segment) {
15326
+ const params = {};
15327
+ const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
15328
+ let match;
15329
+ while ((match = re.exec(segment)) !== null) {
15330
+ params[match[1]] = match[2] ?? match[3] ?? "";
15331
+ }
15332
+ return params;
15333
+ }
15334
+ function getWarningsForMppHeader(wwwAuthenticate) {
15335
+ if (!wwwAuthenticate?.trim()) {
15336
+ return [
15337
+ {
15338
+ code: AUDIT_CODES.MPP_HEADER_MISSING,
15339
+ severity: "error",
15340
+ message: "WWW-Authenticate header is absent.",
15341
+ hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
15342
+ }
15343
+ ];
15344
+ }
15345
+ const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
15346
+ if (segments.length === 0) {
15347
+ return [
15348
+ {
15349
+ code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
15350
+ severity: "error",
15351
+ message: "WWW-Authenticate header contains no Payment challenges.",
15352
+ hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
15353
+ }
15354
+ ];
15355
+ }
15356
+ const warnings = [];
15357
+ for (let i = 0; i < segments.length; i++) {
15358
+ const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
15359
+ const params = parseAuthParams2(stripped);
15360
+ const idx = `WWW-Authenticate[${i}]`;
15361
+ if (!params["id"]) {
15362
+ warnings.push({
15363
+ code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
15364
+ severity: "error",
15365
+ message: `Payment challenge ${i} is missing the id parameter.`,
15366
+ hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
15367
+ path: `${idx}.id`
15368
+ });
15369
+ }
15370
+ if (!params["method"]) {
15371
+ warnings.push({
15372
+ code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
15373
+ severity: "error",
15374
+ message: `Payment challenge ${i} is missing the method parameter.`,
15375
+ hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
15376
+ path: `${idx}.method`
15377
+ });
15378
+ }
15379
+ if (!params["intent"]) {
15380
+ warnings.push({
15381
+ code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
15382
+ severity: "error",
15383
+ message: `Payment challenge ${i} is missing the intent parameter.`,
15384
+ hint: 'Set intent="charge" on the Payment challenge.',
15385
+ path: `${idx}.intent`
15386
+ });
15387
+ }
15388
+ if (!params["realm"]) {
15389
+ warnings.push({
15390
+ code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
15391
+ severity: "error",
15392
+ message: `Payment challenge ${i} is missing the realm parameter.`,
15393
+ hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
15394
+ path: `${idx}.realm`
15395
+ });
15396
+ }
15397
+ if (!params["expires"]) {
15398
+ warnings.push({
15399
+ code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
15400
+ severity: "error",
15401
+ message: `Payment challenge ${i} is missing the expires parameter.`,
15402
+ hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
15403
+ path: `${idx}.expires`
15404
+ });
15405
+ }
15406
+ const requestStr = params["request"];
15407
+ if (!requestStr) {
15408
+ warnings.push({
15409
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
15410
+ severity: "error",
15411
+ message: `Payment challenge ${i} is missing the request field.`,
15412
+ hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
15413
+ path: `${idx}.request`
15414
+ });
15415
+ continue;
15416
+ }
15417
+ let request;
15418
+ try {
15419
+ const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
15420
+ const parsed = JSON.parse(decoded);
15421
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
15422
+ throw new Error("not an object");
15423
+ }
15424
+ request = parsed;
15425
+ } catch {
15426
+ warnings.push({
15427
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
15428
+ severity: "error",
15429
+ message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
15430
+ hint: "The request value must be a base64url-encoded JCS JSON object.",
15431
+ path: `${idx}.request`
15432
+ });
15433
+ continue;
15434
+ }
15435
+ if (!request["currency"]) {
15436
+ warnings.push({
15437
+ code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
15438
+ severity: "error",
15439
+ message: `Payment challenge ${i} is missing currency in the request object.`,
15440
+ hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
15441
+ path: `${idx}.request.currency`
15442
+ });
15443
+ }
15444
+ const amount = request["amount"];
15445
+ if (amount === void 0 || amount === null) {
15446
+ warnings.push({
15447
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
15448
+ severity: "error",
15449
+ message: `Payment challenge ${i} is missing amount in the request object.`,
15450
+ hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
15451
+ path: `${idx}.request.amount`
15452
+ });
15453
+ } else if (typeof amount !== "string" && typeof amount !== "number") {
15454
+ warnings.push({
15455
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
15456
+ severity: "error",
15457
+ message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
15458
+ hint: "Set amount to a raw token-unit string.",
15459
+ path: `${idx}.request.amount`
15460
+ });
15461
+ }
15462
+ if (!request["recipient"]) {
15463
+ warnings.push({
15464
+ code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
15465
+ severity: "error",
15466
+ message: `Payment challenge ${i} is missing recipient in the request object.`,
15467
+ hint: "Set recipient to the wallet address that should receive payment.",
15468
+ path: `${idx}.request.recipient`
15469
+ });
15470
+ }
15471
+ }
15472
+ return warnings;
15473
+ }
15474
+
15152
15475
  // src/audit/warnings/l3.ts
15153
15476
  function getWarningsFor402Body(body) {
15154
15477
  if (!isRecord(body)) {
@@ -15278,17 +15601,28 @@ function getWarningsForL3(l3) {
15278
15601
  hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
15279
15602
  });
15280
15603
  }
15281
- if (l3.authMode === "paid" && !l3.protocols?.length) {
15604
+ if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
15282
15605
  warnings.push({
15283
15606
  code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
15284
15607
  severity: "info",
15285
15608
  message: "Paid endpoint does not declare supported payment protocols.",
15286
- hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
15609
+ hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
15610
+ });
15611
+ }
15612
+ if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
15613
+ warnings.push({
15614
+ code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
15615
+ severity: "warn",
15616
+ message: "Paid endpoint did not return payment options in the 402 response.",
15617
+ hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
15287
15618
  });
15288
15619
  }
15289
15620
  if (l3.paymentRequiredBody !== void 0) {
15290
15621
  warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
15291
15622
  }
15623
+ if (l3.wwwAuthenticate !== void 0) {
15624
+ warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
15625
+ }
15292
15626
  return warnings;
15293
15627
  }
15294
15628
 
@@ -15506,14 +15840,17 @@ export {
15506
15840
  getL3,
15507
15841
  getL3ForOpenAPI,
15508
15842
  getL3ForProbe,
15843
+ getMppWellKnown,
15509
15844
  getOpenAPI,
15510
15845
  getProbe,
15511
15846
  getWarningsFor402Body,
15512
15847
  getWarningsForL2,
15513
15848
  getWarningsForL3,
15514
15849
  getWarningsForL4,
15850
+ getWarningsForMppHeader,
15515
15851
  getWarningsForOpenAPI,
15516
15852
  getWarningsForWellKnown,
15517
15853
  getWellKnown,
15854
+ getX402WellKnown,
15518
15855
  validatePaymentRequiredDetailed
15519
15856
  };
package/dist/schemas.cjs CHANGED
@@ -13864,7 +13864,8 @@ var WellKnownParsedSchema = external_exports.object({
13864
13864
  routes: external_exports.array(
13865
13865
  external_exports.object({
13866
13866
  path: external_exports.string(),
13867
- method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"])
13867
+ method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]),
13868
+ price: external_exports.string().optional()
13868
13869
  })
13869
13870
  ),
13870
13871
  instructions: external_exports.string().optional()
@@ -517,6 +517,7 @@ declare const WellKnownParsedSchema: z.ZodObject<{
517
517
  OPTIONS: "OPTIONS";
518
518
  TRACE: "TRACE";
519
519
  }>;
520
+ price: z.ZodOptional<z.ZodString>;
520
521
  }, z.core.$strip>>;
521
522
  instructions: z.ZodOptional<z.ZodString>;
522
523
  }, z.core.$strip>;
package/dist/schemas.d.ts CHANGED
@@ -517,6 +517,7 @@ declare const WellKnownParsedSchema: z.ZodObject<{
517
517
  OPTIONS: "OPTIONS";
518
518
  TRACE: "TRACE";
519
519
  }>;
520
+ price: z.ZodOptional<z.ZodString>;
520
521
  }, z.core.$strip>>;
521
522
  instructions: z.ZodOptional<z.ZodString>;
522
523
  }, z.core.$strip>;