@agentcash/discovery 1.1.2 → 1.2.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/cli.cjs CHANGED
@@ -13864,7 +13864,6 @@ var WellKnownDocSchema = external_exports.object({
13864
13864
  version: external_exports.number().optional(),
13865
13865
  resources: external_exports.array(external_exports.string()).default([]),
13866
13866
  mppResources: external_exports.array(external_exports.string()).optional(),
13867
- // isMmmEnabled
13868
13867
  description: external_exports.string().optional(),
13869
13868
  ownershipProofs: external_exports.array(external_exports.string()).optional(),
13870
13869
  instructions: external_exports.string().optional()
@@ -13974,9 +13973,6 @@ function fetchSafe(url2, init) {
13974
13973
  return import_neverthrow.ResultAsync.fromPromise(fetch(url2, init), toFetchError);
13975
13974
  }
13976
13975
 
13977
- // src/mmm-enabled.ts
13978
- var isMmmEnabled = () => "1.1.2".includes("-mmm");
13979
-
13980
13976
  // src/core/source/openapi/index.ts
13981
13977
  var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
13982
13978
  const routes = [];
@@ -13986,9 +13982,7 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
13986
13982
  if (!operation) continue;
13987
13983
  const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
13988
13984
  const p = operation["x-payment-info"];
13989
- const protocols = (p?.protocols ?? []).filter(
13990
- (proto) => proto !== "mpp" || isMmmEnabled()
13991
- );
13985
+ const protocols = (p?.protocols ?? []).filter((proto) => proto.length > 0);
13992
13986
  const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
13993
13987
  pricingMode: p.pricingMode,
13994
13988
  ...p.price ? { price: p.price } : {},
@@ -14102,6 +14096,7 @@ function checkL2ForOpenAPI(openApi) {
14102
14096
  return {
14103
14097
  ...openApi.info.title ? { title: openApi.info.title } : {},
14104
14098
  ...openApi.info.description ? { description: openApi.info.description } : {},
14099
+ ...openApi.info.version ? { version: openApi.info.version } : {},
14105
14100
  routes,
14106
14101
  source: "openapi"
14107
14102
  };
@@ -14151,9 +14146,23 @@ var AUDIT_CODES = {
14151
14146
  L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
14152
14147
  L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
14153
14148
  L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
14149
+ L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
14154
14150
  // ─── L4 guidance checks ──────────────────────────────────────────────────────
14155
14151
  L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
14156
- L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
14152
+ L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
14153
+ // ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
14154
+ MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
14155
+ MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
14156
+ MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
14157
+ MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
14158
+ MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
14159
+ MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
14160
+ MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
14161
+ MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
14162
+ MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
14163
+ MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
14164
+ MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
14165
+ MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
14157
14166
  };
14158
14167
 
14159
14168
  // src/audit/warnings/sources.ts
@@ -14732,6 +14741,157 @@ function validateWithCoinbaseSchema2(body) {
14732
14741
  });
14733
14742
  }
14734
14743
 
14744
+ // src/audit/warnings/mpp.ts
14745
+ function parseAuthParams(segment) {
14746
+ const params = {};
14747
+ const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
14748
+ let match;
14749
+ while ((match = re.exec(segment)) !== null) {
14750
+ params[match[1]] = match[2] ?? match[3] ?? "";
14751
+ }
14752
+ return params;
14753
+ }
14754
+ function getWarningsForMppHeader(wwwAuthenticate) {
14755
+ if (!wwwAuthenticate?.trim()) {
14756
+ return [
14757
+ {
14758
+ code: AUDIT_CODES.MPP_HEADER_MISSING,
14759
+ severity: "error",
14760
+ message: "WWW-Authenticate header is absent.",
14761
+ hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
14762
+ }
14763
+ ];
14764
+ }
14765
+ const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
14766
+ if (segments.length === 0) {
14767
+ return [
14768
+ {
14769
+ code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
14770
+ severity: "error",
14771
+ message: "WWW-Authenticate header contains no Payment challenges.",
14772
+ hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
14773
+ }
14774
+ ];
14775
+ }
14776
+ const warnings = [];
14777
+ for (let i = 0; i < segments.length; i++) {
14778
+ const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
14779
+ const params = parseAuthParams(stripped);
14780
+ const idx = `WWW-Authenticate[${i}]`;
14781
+ if (!params["id"]) {
14782
+ warnings.push({
14783
+ code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
14784
+ severity: "error",
14785
+ message: `Payment challenge ${i} is missing the id parameter.`,
14786
+ hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
14787
+ path: `${idx}.id`
14788
+ });
14789
+ }
14790
+ if (!params["method"]) {
14791
+ warnings.push({
14792
+ code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
14793
+ severity: "error",
14794
+ message: `Payment challenge ${i} is missing the method parameter.`,
14795
+ hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
14796
+ path: `${idx}.method`
14797
+ });
14798
+ }
14799
+ if (!params["intent"]) {
14800
+ warnings.push({
14801
+ code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
14802
+ severity: "error",
14803
+ message: `Payment challenge ${i} is missing the intent parameter.`,
14804
+ hint: 'Set intent="charge" on the Payment challenge.',
14805
+ path: `${idx}.intent`
14806
+ });
14807
+ }
14808
+ if (!params["realm"]) {
14809
+ warnings.push({
14810
+ code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
14811
+ severity: "error",
14812
+ message: `Payment challenge ${i} is missing the realm parameter.`,
14813
+ hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
14814
+ path: `${idx}.realm`
14815
+ });
14816
+ }
14817
+ if (!params["expires"]) {
14818
+ warnings.push({
14819
+ code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
14820
+ severity: "error",
14821
+ message: `Payment challenge ${i} is missing the expires parameter.`,
14822
+ hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
14823
+ path: `${idx}.expires`
14824
+ });
14825
+ }
14826
+ const requestStr = params["request"];
14827
+ if (!requestStr) {
14828
+ warnings.push({
14829
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
14830
+ severity: "error",
14831
+ message: `Payment challenge ${i} is missing the request field.`,
14832
+ hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
14833
+ path: `${idx}.request`
14834
+ });
14835
+ continue;
14836
+ }
14837
+ let request;
14838
+ try {
14839
+ const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
14840
+ const parsed = JSON.parse(decoded);
14841
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
14842
+ throw new Error("not an object");
14843
+ }
14844
+ request = parsed;
14845
+ } catch {
14846
+ warnings.push({
14847
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
14848
+ severity: "error",
14849
+ message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
14850
+ hint: "The request value must be a base64url-encoded JCS JSON object.",
14851
+ path: `${idx}.request`
14852
+ });
14853
+ continue;
14854
+ }
14855
+ if (!request["currency"]) {
14856
+ warnings.push({
14857
+ code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
14858
+ severity: "error",
14859
+ message: `Payment challenge ${i} is missing currency in the request object.`,
14860
+ hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
14861
+ path: `${idx}.request.currency`
14862
+ });
14863
+ }
14864
+ const amount = request["amount"];
14865
+ if (amount === void 0 || amount === null) {
14866
+ warnings.push({
14867
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
14868
+ severity: "error",
14869
+ message: `Payment challenge ${i} is missing amount in the request object.`,
14870
+ hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
14871
+ path: `${idx}.request.amount`
14872
+ });
14873
+ } else if (typeof amount !== "string" && typeof amount !== "number") {
14874
+ warnings.push({
14875
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
14876
+ severity: "error",
14877
+ message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
14878
+ hint: "Set amount to a raw token-unit string.",
14879
+ path: `${idx}.request.amount`
14880
+ });
14881
+ }
14882
+ if (!request["recipient"]) {
14883
+ warnings.push({
14884
+ code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
14885
+ severity: "error",
14886
+ message: `Payment challenge ${i} is missing recipient in the request object.`,
14887
+ hint: "Set recipient to the wallet address that should receive payment.",
14888
+ path: `${idx}.request.recipient`
14889
+ });
14890
+ }
14891
+ }
14892
+ return warnings;
14893
+ }
14894
+
14735
14895
  // src/audit/warnings/l3.ts
14736
14896
  function getWarningsFor402Body(body) {
14737
14897
  if (!isRecord(body)) {
@@ -14861,17 +15021,28 @@ function getWarningsForL3(l3) {
14861
15021
  hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
14862
15022
  });
14863
15023
  }
14864
- if (l3.authMode === "paid" && !l3.protocols?.length) {
15024
+ if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
14865
15025
  warnings.push({
14866
15026
  code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
14867
15027
  severity: "info",
14868
15028
  message: "Paid endpoint does not declare supported payment protocols.",
14869
- hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
15029
+ hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
15030
+ });
15031
+ }
15032
+ if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
15033
+ warnings.push({
15034
+ code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
15035
+ severity: "warn",
15036
+ message: "Paid endpoint did not return payment options in the 402 response.",
15037
+ hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
14870
15038
  });
14871
15039
  }
14872
15040
  if (l3.paymentRequiredBody !== void 0) {
14873
15041
  warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
14874
15042
  }
15043
+ if (l3.wwwAuthenticate !== void 0) {
15044
+ warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
15045
+ }
14875
15046
  return warnings;
14876
15047
  }
14877
15048
 
@@ -14974,7 +15145,7 @@ function detectProtocols(response) {
14974
15145
  }
14975
15146
  const authHeader = response.headers.get("www-authenticate")?.toLowerCase() ?? "";
14976
15147
  if (authHeader.includes("x402")) protocols.add("x402");
14977
- if (isMmmEnabled() && authHeader.includes("mpp")) protocols.add("mpp");
15148
+ if (authHeader.includes("mpp")) protocols.add("mpp");
14978
15149
  return [...protocols];
14979
15150
  }
14980
15151
  function buildProbeUrl(url2, method, inputBody) {
@@ -15044,7 +15215,7 @@ function getProbe(url2, headers, signal, inputBody) {
15044
15215
 
15045
15216
  // src/core/protocols/mpp/index.ts
15046
15217
  var TEMPO_DEFAULT_CHAIN_ID = 4217;
15047
- function parseAuthParams(segment) {
15218
+ function parseAuthParams2(segment) {
15048
15219
  const params = {};
15049
15220
  const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
15050
15221
  let match;
@@ -15054,11 +15225,11 @@ function parseAuthParams(segment) {
15054
15225
  return params;
15055
15226
  }
15056
15227
  function extractPaymentOptions4(wwwAuthenticate) {
15057
- if (!isMmmEnabled() || !wwwAuthenticate) return [];
15228
+ if (!wwwAuthenticate) return [];
15058
15229
  const options = [];
15059
15230
  for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
15060
15231
  const stripped = segment.replace(/^Payment\s+/i, "").trim();
15061
- const params = parseAuthParams(stripped);
15232
+ const params = parseAuthParams2(stripped);
15062
15233
  const paymentMethod = params["method"];
15063
15234
  const intent = params["intent"];
15064
15235
  const realm = params["realm"];
@@ -15083,12 +15254,10 @@ function extractPaymentOptions4(wwwAuthenticate) {
15083
15254
  if (!asset || !amount) continue;
15084
15255
  options.push({
15085
15256
  protocol: "mpp",
15086
- // isMmmEnabled
15087
15257
  paymentMethod,
15088
15258
  intent,
15089
15259
  realm,
15090
15260
  network: `tempo:${String(chainId)}`,
15091
- // isMmmEnabled
15092
15261
  asset,
15093
15262
  amount,
15094
15263
  ...decimals != null ? { decimals } : {},
@@ -15216,7 +15385,7 @@ function parseOperationProtocols(operation) {
15216
15385
  const paymentInfo = operation["x-payment-info"];
15217
15386
  if (!isRecord(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
15218
15387
  const protocols = paymentInfo.protocols.filter(
15219
- (protocol) => typeof protocol === "string" && protocol.length > 0 && (protocol !== "mpp" || isMmmEnabled())
15388
+ (protocol) => typeof protocol === "string" && protocol.length > 0
15220
15389
  );
15221
15390
  return protocols.length > 0 ? protocols : void 0;
15222
15391
  }
@@ -15247,8 +15416,7 @@ function getL3ForProbe(probe, path, method) {
15247
15416
  const outputSchema = probeResult.paymentRequiredBody ? parseOutputSchema(probeResult.paymentRequiredBody) : void 0;
15248
15417
  const paymentOptions = [
15249
15418
  ...probeResult.paymentRequiredBody ? extractPaymentOptions3(probeResult.paymentRequiredBody) : [],
15250
- ...isMmmEnabled() ? extractPaymentOptions4(probeResult.wwwAuthenticate) : []
15251
- // isMmmEnabled
15419
+ ...extractPaymentOptions4(probeResult.wwwAuthenticate)
15252
15420
  ];
15253
15421
  return {
15254
15422
  source: "probe",
@@ -15257,7 +15425,8 @@ function getL3ForProbe(probe, path, method) {
15257
15425
  ...inputSchema ? { inputSchema } : {},
15258
15426
  ...outputSchema ? { outputSchema } : {},
15259
15427
  ...paymentOptions.length ? { paymentOptions } : {},
15260
- ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
15428
+ ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
15429
+ ...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
15261
15430
  };
15262
15431
  }
15263
15432
  async function attachProbePayload(url2, advisories) {
@@ -15288,7 +15457,7 @@ async function checkEndpointSchema(options) {
15288
15457
  const endpoint = new URL(ensureProtocol(options.url));
15289
15458
  const origin = normalizeOrigin(endpoint.origin);
15290
15459
  const path = normalizePath(endpoint.pathname || "/");
15291
- if (options.sampleInputBody !== void 0) {
15460
+ if (options.probe || options.sampleInputBody !== void 0) {
15292
15461
  const probeResult2 = await getProbe(
15293
15462
  endpoint.href,
15294
15463
  options.headers,
package/dist/cli.js CHANGED
@@ -13834,7 +13834,6 @@ var WellKnownDocSchema = external_exports.object({
13834
13834
  version: external_exports.number().optional(),
13835
13835
  resources: external_exports.array(external_exports.string()).default([]),
13836
13836
  mppResources: external_exports.array(external_exports.string()).optional(),
13837
- // isMmmEnabled
13838
13837
  description: external_exports.string().optional(),
13839
13838
  ownershipProofs: external_exports.array(external_exports.string()).optional(),
13840
13839
  instructions: external_exports.string().optional()
@@ -13944,9 +13943,6 @@ function fetchSafe(url2, init) {
13944
13943
  return ResultAsync.fromPromise(fetch(url2, init), toFetchError);
13945
13944
  }
13946
13945
 
13947
- // src/mmm-enabled.ts
13948
- var isMmmEnabled = () => "1.1.2".includes("-mmm");
13949
-
13950
13946
  // src/core/source/openapi/index.ts
13951
13947
  var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
13952
13948
  const routes = [];
@@ -13956,9 +13952,7 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
13956
13952
  if (!operation) continue;
13957
13953
  const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
13958
13954
  const p = operation["x-payment-info"];
13959
- const protocols = (p?.protocols ?? []).filter(
13960
- (proto) => proto !== "mpp" || isMmmEnabled()
13961
- );
13955
+ const protocols = (p?.protocols ?? []).filter((proto) => proto.length > 0);
13962
13956
  const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
13963
13957
  pricingMode: p.pricingMode,
13964
13958
  ...p.price ? { price: p.price } : {},
@@ -14072,6 +14066,7 @@ function checkL2ForOpenAPI(openApi) {
14072
14066
  return {
14073
14067
  ...openApi.info.title ? { title: openApi.info.title } : {},
14074
14068
  ...openApi.info.description ? { description: openApi.info.description } : {},
14069
+ ...openApi.info.version ? { version: openApi.info.version } : {},
14075
14070
  routes,
14076
14071
  source: "openapi"
14077
14072
  };
@@ -14121,9 +14116,23 @@ var AUDIT_CODES = {
14121
14116
  L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
14122
14117
  L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
14123
14118
  L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
14119
+ L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
14124
14120
  // ─── L4 guidance checks ──────────────────────────────────────────────────────
14125
14121
  L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
14126
- L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
14122
+ L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
14123
+ // ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
14124
+ MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
14125
+ MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
14126
+ MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
14127
+ MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
14128
+ MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
14129
+ MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
14130
+ MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
14131
+ MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
14132
+ MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
14133
+ MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
14134
+ MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
14135
+ MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
14127
14136
  };
14128
14137
 
14129
14138
  // src/audit/warnings/sources.ts
@@ -14702,6 +14711,157 @@ function validateWithCoinbaseSchema2(body) {
14702
14711
  });
14703
14712
  }
14704
14713
 
14714
+ // src/audit/warnings/mpp.ts
14715
+ function parseAuthParams(segment) {
14716
+ const params = {};
14717
+ const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
14718
+ let match;
14719
+ while ((match = re.exec(segment)) !== null) {
14720
+ params[match[1]] = match[2] ?? match[3] ?? "";
14721
+ }
14722
+ return params;
14723
+ }
14724
+ function getWarningsForMppHeader(wwwAuthenticate) {
14725
+ if (!wwwAuthenticate?.trim()) {
14726
+ return [
14727
+ {
14728
+ code: AUDIT_CODES.MPP_HEADER_MISSING,
14729
+ severity: "error",
14730
+ message: "WWW-Authenticate header is absent.",
14731
+ hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
14732
+ }
14733
+ ];
14734
+ }
14735
+ const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
14736
+ if (segments.length === 0) {
14737
+ return [
14738
+ {
14739
+ code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
14740
+ severity: "error",
14741
+ message: "WWW-Authenticate header contains no Payment challenges.",
14742
+ hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
14743
+ }
14744
+ ];
14745
+ }
14746
+ const warnings = [];
14747
+ for (let i = 0; i < segments.length; i++) {
14748
+ const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
14749
+ const params = parseAuthParams(stripped);
14750
+ const idx = `WWW-Authenticate[${i}]`;
14751
+ if (!params["id"]) {
14752
+ warnings.push({
14753
+ code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
14754
+ severity: "error",
14755
+ message: `Payment challenge ${i} is missing the id parameter.`,
14756
+ hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
14757
+ path: `${idx}.id`
14758
+ });
14759
+ }
14760
+ if (!params["method"]) {
14761
+ warnings.push({
14762
+ code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
14763
+ severity: "error",
14764
+ message: `Payment challenge ${i} is missing the method parameter.`,
14765
+ hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
14766
+ path: `${idx}.method`
14767
+ });
14768
+ }
14769
+ if (!params["intent"]) {
14770
+ warnings.push({
14771
+ code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
14772
+ severity: "error",
14773
+ message: `Payment challenge ${i} is missing the intent parameter.`,
14774
+ hint: 'Set intent="charge" on the Payment challenge.',
14775
+ path: `${idx}.intent`
14776
+ });
14777
+ }
14778
+ if (!params["realm"]) {
14779
+ warnings.push({
14780
+ code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
14781
+ severity: "error",
14782
+ message: `Payment challenge ${i} is missing the realm parameter.`,
14783
+ hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
14784
+ path: `${idx}.realm`
14785
+ });
14786
+ }
14787
+ if (!params["expires"]) {
14788
+ warnings.push({
14789
+ code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
14790
+ severity: "error",
14791
+ message: `Payment challenge ${i} is missing the expires parameter.`,
14792
+ hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
14793
+ path: `${idx}.expires`
14794
+ });
14795
+ }
14796
+ const requestStr = params["request"];
14797
+ if (!requestStr) {
14798
+ warnings.push({
14799
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
14800
+ severity: "error",
14801
+ message: `Payment challenge ${i} is missing the request field.`,
14802
+ hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
14803
+ path: `${idx}.request`
14804
+ });
14805
+ continue;
14806
+ }
14807
+ let request;
14808
+ try {
14809
+ const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
14810
+ const parsed = JSON.parse(decoded);
14811
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
14812
+ throw new Error("not an object");
14813
+ }
14814
+ request = parsed;
14815
+ } catch {
14816
+ warnings.push({
14817
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
14818
+ severity: "error",
14819
+ message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
14820
+ hint: "The request value must be a base64url-encoded JCS JSON object.",
14821
+ path: `${idx}.request`
14822
+ });
14823
+ continue;
14824
+ }
14825
+ if (!request["currency"]) {
14826
+ warnings.push({
14827
+ code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
14828
+ severity: "error",
14829
+ message: `Payment challenge ${i} is missing currency in the request object.`,
14830
+ hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
14831
+ path: `${idx}.request.currency`
14832
+ });
14833
+ }
14834
+ const amount = request["amount"];
14835
+ if (amount === void 0 || amount === null) {
14836
+ warnings.push({
14837
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
14838
+ severity: "error",
14839
+ message: `Payment challenge ${i} is missing amount in the request object.`,
14840
+ hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
14841
+ path: `${idx}.request.amount`
14842
+ });
14843
+ } else if (typeof amount !== "string" && typeof amount !== "number") {
14844
+ warnings.push({
14845
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
14846
+ severity: "error",
14847
+ message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
14848
+ hint: "Set amount to a raw token-unit string.",
14849
+ path: `${idx}.request.amount`
14850
+ });
14851
+ }
14852
+ if (!request["recipient"]) {
14853
+ warnings.push({
14854
+ code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
14855
+ severity: "error",
14856
+ message: `Payment challenge ${i} is missing recipient in the request object.`,
14857
+ hint: "Set recipient to the wallet address that should receive payment.",
14858
+ path: `${idx}.request.recipient`
14859
+ });
14860
+ }
14861
+ }
14862
+ return warnings;
14863
+ }
14864
+
14705
14865
  // src/audit/warnings/l3.ts
14706
14866
  function getWarningsFor402Body(body) {
14707
14867
  if (!isRecord(body)) {
@@ -14831,17 +14991,28 @@ function getWarningsForL3(l3) {
14831
14991
  hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
14832
14992
  });
14833
14993
  }
14834
- if (l3.authMode === "paid" && !l3.protocols?.length) {
14994
+ if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
14835
14995
  warnings.push({
14836
14996
  code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
14837
14997
  severity: "info",
14838
14998
  message: "Paid endpoint does not declare supported payment protocols.",
14839
- hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
14999
+ hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
15000
+ });
15001
+ }
15002
+ if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
15003
+ warnings.push({
15004
+ code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
15005
+ severity: "warn",
15006
+ message: "Paid endpoint did not return payment options in the 402 response.",
15007
+ hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
14840
15008
  });
14841
15009
  }
14842
15010
  if (l3.paymentRequiredBody !== void 0) {
14843
15011
  warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
14844
15012
  }
15013
+ if (l3.wwwAuthenticate !== void 0) {
15014
+ warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
15015
+ }
14845
15016
  return warnings;
14846
15017
  }
14847
15018
 
@@ -14944,7 +15115,7 @@ function detectProtocols(response) {
14944
15115
  }
14945
15116
  const authHeader = response.headers.get("www-authenticate")?.toLowerCase() ?? "";
14946
15117
  if (authHeader.includes("x402")) protocols.add("x402");
14947
- if (isMmmEnabled() && authHeader.includes("mpp")) protocols.add("mpp");
15118
+ if (authHeader.includes("mpp")) protocols.add("mpp");
14948
15119
  return [...protocols];
14949
15120
  }
14950
15121
  function buildProbeUrl(url2, method, inputBody) {
@@ -15014,7 +15185,7 @@ function getProbe(url2, headers, signal, inputBody) {
15014
15185
 
15015
15186
  // src/core/protocols/mpp/index.ts
15016
15187
  var TEMPO_DEFAULT_CHAIN_ID = 4217;
15017
- function parseAuthParams(segment) {
15188
+ function parseAuthParams2(segment) {
15018
15189
  const params = {};
15019
15190
  const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
15020
15191
  let match;
@@ -15024,11 +15195,11 @@ function parseAuthParams(segment) {
15024
15195
  return params;
15025
15196
  }
15026
15197
  function extractPaymentOptions4(wwwAuthenticate) {
15027
- if (!isMmmEnabled() || !wwwAuthenticate) return [];
15198
+ if (!wwwAuthenticate) return [];
15028
15199
  const options = [];
15029
15200
  for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
15030
15201
  const stripped = segment.replace(/^Payment\s+/i, "").trim();
15031
- const params = parseAuthParams(stripped);
15202
+ const params = parseAuthParams2(stripped);
15032
15203
  const paymentMethod = params["method"];
15033
15204
  const intent = params["intent"];
15034
15205
  const realm = params["realm"];
@@ -15053,12 +15224,10 @@ function extractPaymentOptions4(wwwAuthenticate) {
15053
15224
  if (!asset || !amount) continue;
15054
15225
  options.push({
15055
15226
  protocol: "mpp",
15056
- // isMmmEnabled
15057
15227
  paymentMethod,
15058
15228
  intent,
15059
15229
  realm,
15060
15230
  network: `tempo:${String(chainId)}`,
15061
- // isMmmEnabled
15062
15231
  asset,
15063
15232
  amount,
15064
15233
  ...decimals != null ? { decimals } : {},
@@ -15186,7 +15355,7 @@ function parseOperationProtocols(operation) {
15186
15355
  const paymentInfo = operation["x-payment-info"];
15187
15356
  if (!isRecord(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
15188
15357
  const protocols = paymentInfo.protocols.filter(
15189
- (protocol) => typeof protocol === "string" && protocol.length > 0 && (protocol !== "mpp" || isMmmEnabled())
15358
+ (protocol) => typeof protocol === "string" && protocol.length > 0
15190
15359
  );
15191
15360
  return protocols.length > 0 ? protocols : void 0;
15192
15361
  }
@@ -15217,8 +15386,7 @@ function getL3ForProbe(probe, path, method) {
15217
15386
  const outputSchema = probeResult.paymentRequiredBody ? parseOutputSchema(probeResult.paymentRequiredBody) : void 0;
15218
15387
  const paymentOptions = [
15219
15388
  ...probeResult.paymentRequiredBody ? extractPaymentOptions3(probeResult.paymentRequiredBody) : [],
15220
- ...isMmmEnabled() ? extractPaymentOptions4(probeResult.wwwAuthenticate) : []
15221
- // isMmmEnabled
15389
+ ...extractPaymentOptions4(probeResult.wwwAuthenticate)
15222
15390
  ];
15223
15391
  return {
15224
15392
  source: "probe",
@@ -15227,7 +15395,8 @@ function getL3ForProbe(probe, path, method) {
15227
15395
  ...inputSchema ? { inputSchema } : {},
15228
15396
  ...outputSchema ? { outputSchema } : {},
15229
15397
  ...paymentOptions.length ? { paymentOptions } : {},
15230
- ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
15398
+ ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
15399
+ ...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
15231
15400
  };
15232
15401
  }
15233
15402
  async function attachProbePayload(url2, advisories) {
@@ -15258,7 +15427,7 @@ async function checkEndpointSchema(options) {
15258
15427
  const endpoint = new URL(ensureProtocol(options.url));
15259
15428
  const origin = normalizeOrigin(endpoint.origin);
15260
15429
  const path = normalizePath(endpoint.pathname || "/");
15261
- if (options.sampleInputBody !== void 0) {
15430
+ if (options.probe || options.sampleInputBody !== void 0) {
15262
15431
  const probeResult2 = await getProbe(
15263
15432
  endpoint.href,
15264
15433
  options.headers,