@agentcash/discovery 1.1.3 → 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
@@ -14146,9 +14146,23 @@ var AUDIT_CODES = {
14146
14146
  L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
14147
14147
  L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
14148
14148
  L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
14149
+ L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
14149
14150
  // ─── L4 guidance checks ──────────────────────────────────────────────────────
14150
14151
  L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
14151
- 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"
14152
14166
  };
14153
14167
 
14154
14168
  // src/audit/warnings/sources.ts
@@ -14727,6 +14741,157 @@ function validateWithCoinbaseSchema2(body) {
14727
14741
  });
14728
14742
  }
14729
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
+
14730
14895
  // src/audit/warnings/l3.ts
14731
14896
  function getWarningsFor402Body(body) {
14732
14897
  if (!isRecord(body)) {
@@ -14856,17 +15021,28 @@ function getWarningsForL3(l3) {
14856
15021
  hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
14857
15022
  });
14858
15023
  }
14859
- if (l3.authMode === "paid" && !l3.protocols?.length) {
15024
+ if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
14860
15025
  warnings.push({
14861
15026
  code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
14862
15027
  severity: "info",
14863
15028
  message: "Paid endpoint does not declare supported payment protocols.",
14864
- 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."
14865
15038
  });
14866
15039
  }
14867
15040
  if (l3.paymentRequiredBody !== void 0) {
14868
15041
  warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
14869
15042
  }
15043
+ if (l3.wwwAuthenticate !== void 0) {
15044
+ warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
15045
+ }
14870
15046
  return warnings;
14871
15047
  }
14872
15048
 
@@ -15039,7 +15215,7 @@ function getProbe(url2, headers, signal, inputBody) {
15039
15215
 
15040
15216
  // src/core/protocols/mpp/index.ts
15041
15217
  var TEMPO_DEFAULT_CHAIN_ID = 4217;
15042
- function parseAuthParams(segment) {
15218
+ function parseAuthParams2(segment) {
15043
15219
  const params = {};
15044
15220
  const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
15045
15221
  let match;
@@ -15053,7 +15229,7 @@ function extractPaymentOptions4(wwwAuthenticate) {
15053
15229
  const options = [];
15054
15230
  for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
15055
15231
  const stripped = segment.replace(/^Payment\s+/i, "").trim();
15056
- const params = parseAuthParams(stripped);
15232
+ const params = parseAuthParams2(stripped);
15057
15233
  const paymentMethod = params["method"];
15058
15234
  const intent = params["intent"];
15059
15235
  const realm = params["realm"];
@@ -15249,7 +15425,8 @@ function getL3ForProbe(probe, path, method) {
15249
15425
  ...inputSchema ? { inputSchema } : {},
15250
15426
  ...outputSchema ? { outputSchema } : {},
15251
15427
  ...paymentOptions.length ? { paymentOptions } : {},
15252
- ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
15428
+ ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
15429
+ ...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
15253
15430
  };
15254
15431
  }
15255
15432
  async function attachProbePayload(url2, advisories) {
package/dist/cli.js CHANGED
@@ -14116,9 +14116,23 @@ var AUDIT_CODES = {
14116
14116
  L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
14117
14117
  L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
14118
14118
  L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
14119
+ L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
14119
14120
  // ─── L4 guidance checks ──────────────────────────────────────────────────────
14120
14121
  L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
14121
- 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"
14122
14136
  };
14123
14137
 
14124
14138
  // src/audit/warnings/sources.ts
@@ -14697,6 +14711,157 @@ function validateWithCoinbaseSchema2(body) {
14697
14711
  });
14698
14712
  }
14699
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
+
14700
14865
  // src/audit/warnings/l3.ts
14701
14866
  function getWarningsFor402Body(body) {
14702
14867
  if (!isRecord(body)) {
@@ -14826,17 +14991,28 @@ function getWarningsForL3(l3) {
14826
14991
  hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
14827
14992
  });
14828
14993
  }
14829
- if (l3.authMode === "paid" && !l3.protocols?.length) {
14994
+ if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
14830
14995
  warnings.push({
14831
14996
  code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
14832
14997
  severity: "info",
14833
14998
  message: "Paid endpoint does not declare supported payment protocols.",
14834
- 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."
14835
15008
  });
14836
15009
  }
14837
15010
  if (l3.paymentRequiredBody !== void 0) {
14838
15011
  warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
14839
15012
  }
15013
+ if (l3.wwwAuthenticate !== void 0) {
15014
+ warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
15015
+ }
14840
15016
  return warnings;
14841
15017
  }
14842
15018
 
@@ -15009,7 +15185,7 @@ function getProbe(url2, headers, signal, inputBody) {
15009
15185
 
15010
15186
  // src/core/protocols/mpp/index.ts
15011
15187
  var TEMPO_DEFAULT_CHAIN_ID = 4217;
15012
- function parseAuthParams(segment) {
15188
+ function parseAuthParams2(segment) {
15013
15189
  const params = {};
15014
15190
  const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
15015
15191
  let match;
@@ -15023,7 +15199,7 @@ function extractPaymentOptions4(wwwAuthenticate) {
15023
15199
  const options = [];
15024
15200
  for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
15025
15201
  const stripped = segment.replace(/^Payment\s+/i, "").trim();
15026
- const params = parseAuthParams(stripped);
15202
+ const params = parseAuthParams2(stripped);
15027
15203
  const paymentMethod = params["method"];
15028
15204
  const intent = params["intent"];
15029
15205
  const realm = params["realm"];
@@ -15219,7 +15395,8 @@ function getL3ForProbe(probe, path, method) {
15219
15395
  ...inputSchema ? { inputSchema } : {},
15220
15396
  ...outputSchema ? { outputSchema } : {},
15221
15397
  ...paymentOptions.length ? { paymentOptions } : {},
15222
- ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
15398
+ ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
15399
+ ...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
15223
15400
  };
15224
15401
  }
15225
15402
  async function attachProbePayload(url2, advisories) {
package/dist/index.cjs CHANGED
@@ -50,6 +50,7 @@ __export(index_exports, {
50
50
  getWarningsForL2: () => getWarningsForL2,
51
51
  getWarningsForL3: () => getWarningsForL3,
52
52
  getWarningsForL4: () => getWarningsForL4,
53
+ getWarningsForMppHeader: () => getWarningsForMppHeader,
53
54
  getWarningsForOpenAPI: () => getWarningsForOpenAPI,
54
55
  getWarningsForWellKnown: () => getWarningsForWellKnown,
55
56
  getWellKnown: () => getWellKnown,
@@ -14930,7 +14931,8 @@ function getL3ForProbe(probe, path, method) {
14930
14931
  ...inputSchema ? { inputSchema } : {},
14931
14932
  ...outputSchema ? { outputSchema } : {},
14932
14933
  ...paymentOptions.length ? { paymentOptions } : {},
14933
- ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
14934
+ ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
14935
+ ...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
14934
14936
  };
14935
14937
  }
14936
14938
  async function attachProbePayload(url2, advisories) {
@@ -15111,9 +15113,23 @@ var AUDIT_CODES = {
15111
15113
  L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
15112
15114
  L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
15113
15115
  L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
15116
+ L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
15114
15117
  // ─── L4 guidance checks ──────────────────────────────────────────────────────
15115
15118
  L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
15116
- L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
15119
+ L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
15120
+ // ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
15121
+ MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
15122
+ MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
15123
+ MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
15124
+ MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
15125
+ MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
15126
+ MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
15127
+ MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
15128
+ MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
15129
+ MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
15130
+ MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
15131
+ MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
15132
+ MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
15117
15133
  };
15118
15134
 
15119
15135
  // src/core/protocols/x402/v1/coinbase-schema.ts
@@ -15202,6 +15218,157 @@ function validateWithCoinbaseSchema2(body) {
15202
15218
  });
15203
15219
  }
15204
15220
 
15221
+ // src/audit/warnings/mpp.ts
15222
+ function parseAuthParams2(segment) {
15223
+ const params = {};
15224
+ const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
15225
+ let match;
15226
+ while ((match = re.exec(segment)) !== null) {
15227
+ params[match[1]] = match[2] ?? match[3] ?? "";
15228
+ }
15229
+ return params;
15230
+ }
15231
+ function getWarningsForMppHeader(wwwAuthenticate) {
15232
+ if (!wwwAuthenticate?.trim()) {
15233
+ return [
15234
+ {
15235
+ code: AUDIT_CODES.MPP_HEADER_MISSING,
15236
+ severity: "error",
15237
+ message: "WWW-Authenticate header is absent.",
15238
+ hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
15239
+ }
15240
+ ];
15241
+ }
15242
+ const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
15243
+ if (segments.length === 0) {
15244
+ return [
15245
+ {
15246
+ code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
15247
+ severity: "error",
15248
+ message: "WWW-Authenticate header contains no Payment challenges.",
15249
+ hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
15250
+ }
15251
+ ];
15252
+ }
15253
+ const warnings = [];
15254
+ for (let i = 0; i < segments.length; i++) {
15255
+ const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
15256
+ const params = parseAuthParams2(stripped);
15257
+ const idx = `WWW-Authenticate[${i}]`;
15258
+ if (!params["id"]) {
15259
+ warnings.push({
15260
+ code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
15261
+ severity: "error",
15262
+ message: `Payment challenge ${i} is missing the id parameter.`,
15263
+ hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
15264
+ path: `${idx}.id`
15265
+ });
15266
+ }
15267
+ if (!params["method"]) {
15268
+ warnings.push({
15269
+ code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
15270
+ severity: "error",
15271
+ message: `Payment challenge ${i} is missing the method parameter.`,
15272
+ hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
15273
+ path: `${idx}.method`
15274
+ });
15275
+ }
15276
+ if (!params["intent"]) {
15277
+ warnings.push({
15278
+ code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
15279
+ severity: "error",
15280
+ message: `Payment challenge ${i} is missing the intent parameter.`,
15281
+ hint: 'Set intent="charge" on the Payment challenge.',
15282
+ path: `${idx}.intent`
15283
+ });
15284
+ }
15285
+ if (!params["realm"]) {
15286
+ warnings.push({
15287
+ code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
15288
+ severity: "error",
15289
+ message: `Payment challenge ${i} is missing the realm parameter.`,
15290
+ hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
15291
+ path: `${idx}.realm`
15292
+ });
15293
+ }
15294
+ if (!params["expires"]) {
15295
+ warnings.push({
15296
+ code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
15297
+ severity: "error",
15298
+ message: `Payment challenge ${i} is missing the expires parameter.`,
15299
+ hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
15300
+ path: `${idx}.expires`
15301
+ });
15302
+ }
15303
+ const requestStr = params["request"];
15304
+ if (!requestStr) {
15305
+ warnings.push({
15306
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
15307
+ severity: "error",
15308
+ message: `Payment challenge ${i} is missing the request field.`,
15309
+ hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
15310
+ path: `${idx}.request`
15311
+ });
15312
+ continue;
15313
+ }
15314
+ let request;
15315
+ try {
15316
+ const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
15317
+ const parsed = JSON.parse(decoded);
15318
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
15319
+ throw new Error("not an object");
15320
+ }
15321
+ request = parsed;
15322
+ } catch {
15323
+ warnings.push({
15324
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
15325
+ severity: "error",
15326
+ message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
15327
+ hint: "The request value must be a base64url-encoded JCS JSON object.",
15328
+ path: `${idx}.request`
15329
+ });
15330
+ continue;
15331
+ }
15332
+ if (!request["currency"]) {
15333
+ warnings.push({
15334
+ code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
15335
+ severity: "error",
15336
+ message: `Payment challenge ${i} is missing currency in the request object.`,
15337
+ hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
15338
+ path: `${idx}.request.currency`
15339
+ });
15340
+ }
15341
+ const amount = request["amount"];
15342
+ if (amount === void 0 || amount === null) {
15343
+ warnings.push({
15344
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
15345
+ severity: "error",
15346
+ message: `Payment challenge ${i} is missing amount in the request object.`,
15347
+ hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
15348
+ path: `${idx}.request.amount`
15349
+ });
15350
+ } else if (typeof amount !== "string" && typeof amount !== "number") {
15351
+ warnings.push({
15352
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
15353
+ severity: "error",
15354
+ message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
15355
+ hint: "Set amount to a raw token-unit string.",
15356
+ path: `${idx}.request.amount`
15357
+ });
15358
+ }
15359
+ if (!request["recipient"]) {
15360
+ warnings.push({
15361
+ code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
15362
+ severity: "error",
15363
+ message: `Payment challenge ${i} is missing recipient in the request object.`,
15364
+ hint: "Set recipient to the wallet address that should receive payment.",
15365
+ path: `${idx}.request.recipient`
15366
+ });
15367
+ }
15368
+ }
15369
+ return warnings;
15370
+ }
15371
+
15205
15372
  // src/audit/warnings/l3.ts
15206
15373
  function getWarningsFor402Body(body) {
15207
15374
  if (!isRecord(body)) {
@@ -15331,17 +15498,28 @@ function getWarningsForL3(l3) {
15331
15498
  hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
15332
15499
  });
15333
15500
  }
15334
- if (l3.authMode === "paid" && !l3.protocols?.length) {
15501
+ if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
15335
15502
  warnings.push({
15336
15503
  code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
15337
15504
  severity: "info",
15338
15505
  message: "Paid endpoint does not declare supported payment protocols.",
15339
- hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
15506
+ hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
15507
+ });
15508
+ }
15509
+ if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
15510
+ warnings.push({
15511
+ code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
15512
+ severity: "warn",
15513
+ message: "Paid endpoint did not return payment options in the 402 response.",
15514
+ hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
15340
15515
  });
15341
15516
  }
15342
15517
  if (l3.paymentRequiredBody !== void 0) {
15343
15518
  warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
15344
15519
  }
15520
+ if (l3.wwwAuthenticate !== void 0) {
15521
+ warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
15522
+ }
15345
15523
  return warnings;
15346
15524
  }
15347
15525
 
@@ -15566,6 +15744,7 @@ function getWarningsForL4(l4) {
15566
15744
  getWarningsForL2,
15567
15745
  getWarningsForL3,
15568
15746
  getWarningsForL4,
15747
+ getWarningsForMppHeader,
15569
15748
  getWarningsForOpenAPI,
15570
15749
  getWarningsForWellKnown,
15571
15750
  getWellKnown,
package/dist/index.d.cts CHANGED
@@ -137,6 +137,11 @@ interface L3Result {
137
137
  * and returned a 402. Used by getWarningsForL3 to run full payment-required validation.
138
138
  */
139
139
  paymentRequiredBody?: unknown;
140
+ /**
141
+ * Raw WWW-Authenticate header value from the 402 response. Present when the endpoint
142
+ * was probed and returned a 402 with an MPP challenge. Used by getWarningsForMppHeader.
143
+ */
144
+ wwwAuthenticate?: string;
140
145
  }
141
146
  interface L4Result {
142
147
  guidance: string;
@@ -361,8 +366,21 @@ declare const AUDIT_CODES: {
361
366
  readonly L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING";
362
367
  readonly L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING";
363
368
  readonly L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID";
369
+ readonly L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID";
364
370
  readonly L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING";
365
371
  readonly L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG";
372
+ readonly MPP_HEADER_MISSING: "MPP_HEADER_MISSING";
373
+ readonly MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES";
374
+ readonly MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING";
375
+ readonly MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING";
376
+ readonly MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING";
377
+ readonly MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING";
378
+ readonly MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING";
379
+ readonly MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING";
380
+ readonly MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID";
381
+ readonly MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING";
382
+ readonly MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING";
383
+ readonly MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING";
366
384
  };
367
385
  type AuditCode = (typeof AUDIT_CODES)[keyof typeof AUDIT_CODES];
368
386
 
@@ -392,4 +410,18 @@ declare function getWarningsForL3(l3: L3Result | null): AuditWarning[];
392
410
 
393
411
  declare function getWarningsForL4(l4: L4Result | null): AuditWarning[];
394
412
 
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 };
413
+ /**
414
+ * Validates a raw WWW-Authenticate header value from an MPP 402 response and
415
+ * returns issues as AuditWarnings.
416
+ *
417
+ * Checks for:
418
+ * - Header presence
419
+ * - At least one Payment challenge
420
+ * - Required challenge parameters: id, method, intent, realm, expires, request
421
+ * - Valid base64url-encoded JSON in the request field
422
+ * - Required request fields: currency (asset), amount
423
+ * - Recommended request field: recipient (payTo)
424
+ */
425
+ declare function getWarningsForMppHeader(wwwAuthenticate: string | null | undefined): AuditWarning[];
426
+
427
+ 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, getWarningsForMppHeader, getWarningsForOpenAPI, getWarningsForWellKnown, getWellKnown, validatePaymentRequiredDetailed };
package/dist/index.d.ts CHANGED
@@ -137,6 +137,11 @@ interface L3Result {
137
137
  * and returned a 402. Used by getWarningsForL3 to run full payment-required validation.
138
138
  */
139
139
  paymentRequiredBody?: unknown;
140
+ /**
141
+ * Raw WWW-Authenticate header value from the 402 response. Present when the endpoint
142
+ * was probed and returned a 402 with an MPP challenge. Used by getWarningsForMppHeader.
143
+ */
144
+ wwwAuthenticate?: string;
140
145
  }
141
146
  interface L4Result {
142
147
  guidance: string;
@@ -361,8 +366,21 @@ declare const AUDIT_CODES: {
361
366
  readonly L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING";
362
367
  readonly L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING";
363
368
  readonly L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID";
369
+ readonly L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID";
364
370
  readonly L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING";
365
371
  readonly L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG";
372
+ readonly MPP_HEADER_MISSING: "MPP_HEADER_MISSING";
373
+ readonly MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES";
374
+ readonly MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING";
375
+ readonly MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING";
376
+ readonly MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING";
377
+ readonly MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING";
378
+ readonly MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING";
379
+ readonly MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING";
380
+ readonly MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID";
381
+ readonly MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING";
382
+ readonly MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING";
383
+ readonly MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING";
366
384
  };
367
385
  type AuditCode = (typeof AUDIT_CODES)[keyof typeof AUDIT_CODES];
368
386
 
@@ -392,4 +410,18 @@ declare function getWarningsForL3(l3: L3Result | null): AuditWarning[];
392
410
 
393
411
  declare function getWarningsForL4(l4: L4Result | null): AuditWarning[];
394
412
 
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 };
413
+ /**
414
+ * Validates a raw WWW-Authenticate header value from an MPP 402 response and
415
+ * returns issues as AuditWarnings.
416
+ *
417
+ * Checks for:
418
+ * - Header presence
419
+ * - At least one Payment challenge
420
+ * - Required challenge parameters: id, method, intent, realm, expires, request
421
+ * - Valid base64url-encoded JSON in the request field
422
+ * - Required request fields: currency (asset), amount
423
+ * - Recommended request field: recipient (payTo)
424
+ */
425
+ declare function getWarningsForMppHeader(wwwAuthenticate: string | null | undefined): AuditWarning[];
426
+
427
+ 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, getWarningsForMppHeader, getWarningsForOpenAPI, getWarningsForWellKnown, getWellKnown, validatePaymentRequiredDetailed };
package/dist/index.js CHANGED
@@ -14877,7 +14877,8 @@ function getL3ForProbe(probe, path, method) {
14877
14877
  ...inputSchema ? { inputSchema } : {},
14878
14878
  ...outputSchema ? { outputSchema } : {},
14879
14879
  ...paymentOptions.length ? { paymentOptions } : {},
14880
- ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
14880
+ ...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
14881
+ ...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
14881
14882
  };
14882
14883
  }
14883
14884
  async function attachProbePayload(url2, advisories) {
@@ -15058,9 +15059,23 @@ var AUDIT_CODES = {
15058
15059
  L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
15059
15060
  L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
15060
15061
  L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
15062
+ L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
15061
15063
  // ─── L4 guidance checks ──────────────────────────────────────────────────────
15062
15064
  L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
15063
- L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
15065
+ L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
15066
+ // ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
15067
+ MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
15068
+ MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
15069
+ MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
15070
+ MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
15071
+ MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
15072
+ MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
15073
+ MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
15074
+ MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
15075
+ MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
15076
+ MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
15077
+ MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
15078
+ MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
15064
15079
  };
15065
15080
 
15066
15081
  // src/core/protocols/x402/v1/coinbase-schema.ts
@@ -15149,6 +15164,157 @@ function validateWithCoinbaseSchema2(body) {
15149
15164
  });
15150
15165
  }
15151
15166
 
15167
+ // src/audit/warnings/mpp.ts
15168
+ function parseAuthParams2(segment) {
15169
+ const params = {};
15170
+ const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
15171
+ let match;
15172
+ while ((match = re.exec(segment)) !== null) {
15173
+ params[match[1]] = match[2] ?? match[3] ?? "";
15174
+ }
15175
+ return params;
15176
+ }
15177
+ function getWarningsForMppHeader(wwwAuthenticate) {
15178
+ if (!wwwAuthenticate?.trim()) {
15179
+ return [
15180
+ {
15181
+ code: AUDIT_CODES.MPP_HEADER_MISSING,
15182
+ severity: "error",
15183
+ message: "WWW-Authenticate header is absent.",
15184
+ hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
15185
+ }
15186
+ ];
15187
+ }
15188
+ const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
15189
+ if (segments.length === 0) {
15190
+ return [
15191
+ {
15192
+ code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
15193
+ severity: "error",
15194
+ message: "WWW-Authenticate header contains no Payment challenges.",
15195
+ hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
15196
+ }
15197
+ ];
15198
+ }
15199
+ const warnings = [];
15200
+ for (let i = 0; i < segments.length; i++) {
15201
+ const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
15202
+ const params = parseAuthParams2(stripped);
15203
+ const idx = `WWW-Authenticate[${i}]`;
15204
+ if (!params["id"]) {
15205
+ warnings.push({
15206
+ code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
15207
+ severity: "error",
15208
+ message: `Payment challenge ${i} is missing the id parameter.`,
15209
+ hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
15210
+ path: `${idx}.id`
15211
+ });
15212
+ }
15213
+ if (!params["method"]) {
15214
+ warnings.push({
15215
+ code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
15216
+ severity: "error",
15217
+ message: `Payment challenge ${i} is missing the method parameter.`,
15218
+ hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
15219
+ path: `${idx}.method`
15220
+ });
15221
+ }
15222
+ if (!params["intent"]) {
15223
+ warnings.push({
15224
+ code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
15225
+ severity: "error",
15226
+ message: `Payment challenge ${i} is missing the intent parameter.`,
15227
+ hint: 'Set intent="charge" on the Payment challenge.',
15228
+ path: `${idx}.intent`
15229
+ });
15230
+ }
15231
+ if (!params["realm"]) {
15232
+ warnings.push({
15233
+ code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
15234
+ severity: "error",
15235
+ message: `Payment challenge ${i} is missing the realm parameter.`,
15236
+ hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
15237
+ path: `${idx}.realm`
15238
+ });
15239
+ }
15240
+ if (!params["expires"]) {
15241
+ warnings.push({
15242
+ code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
15243
+ severity: "error",
15244
+ message: `Payment challenge ${i} is missing the expires parameter.`,
15245
+ hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
15246
+ path: `${idx}.expires`
15247
+ });
15248
+ }
15249
+ const requestStr = params["request"];
15250
+ if (!requestStr) {
15251
+ warnings.push({
15252
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
15253
+ severity: "error",
15254
+ message: `Payment challenge ${i} is missing the request field.`,
15255
+ hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
15256
+ path: `${idx}.request`
15257
+ });
15258
+ continue;
15259
+ }
15260
+ let request;
15261
+ try {
15262
+ const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
15263
+ const parsed = JSON.parse(decoded);
15264
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
15265
+ throw new Error("not an object");
15266
+ }
15267
+ request = parsed;
15268
+ } catch {
15269
+ warnings.push({
15270
+ code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
15271
+ severity: "error",
15272
+ message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
15273
+ hint: "The request value must be a base64url-encoded JCS JSON object.",
15274
+ path: `${idx}.request`
15275
+ });
15276
+ continue;
15277
+ }
15278
+ if (!request["currency"]) {
15279
+ warnings.push({
15280
+ code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
15281
+ severity: "error",
15282
+ message: `Payment challenge ${i} is missing currency in the request object.`,
15283
+ hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
15284
+ path: `${idx}.request.currency`
15285
+ });
15286
+ }
15287
+ const amount = request["amount"];
15288
+ if (amount === void 0 || amount === null) {
15289
+ warnings.push({
15290
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
15291
+ severity: "error",
15292
+ message: `Payment challenge ${i} is missing amount in the request object.`,
15293
+ hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
15294
+ path: `${idx}.request.amount`
15295
+ });
15296
+ } else if (typeof amount !== "string" && typeof amount !== "number") {
15297
+ warnings.push({
15298
+ code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
15299
+ severity: "error",
15300
+ message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
15301
+ hint: "Set amount to a raw token-unit string.",
15302
+ path: `${idx}.request.amount`
15303
+ });
15304
+ }
15305
+ if (!request["recipient"]) {
15306
+ warnings.push({
15307
+ code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
15308
+ severity: "error",
15309
+ message: `Payment challenge ${i} is missing recipient in the request object.`,
15310
+ hint: "Set recipient to the wallet address that should receive payment.",
15311
+ path: `${idx}.request.recipient`
15312
+ });
15313
+ }
15314
+ }
15315
+ return warnings;
15316
+ }
15317
+
15152
15318
  // src/audit/warnings/l3.ts
15153
15319
  function getWarningsFor402Body(body) {
15154
15320
  if (!isRecord(body)) {
@@ -15278,17 +15444,28 @@ function getWarningsForL3(l3) {
15278
15444
  hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
15279
15445
  });
15280
15446
  }
15281
- if (l3.authMode === "paid" && !l3.protocols?.length) {
15447
+ if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
15282
15448
  warnings.push({
15283
15449
  code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
15284
15450
  severity: "info",
15285
15451
  message: "Paid endpoint does not declare supported payment protocols.",
15286
- hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
15452
+ hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
15453
+ });
15454
+ }
15455
+ if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
15456
+ warnings.push({
15457
+ code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
15458
+ severity: "warn",
15459
+ message: "Paid endpoint did not return payment options in the 402 response.",
15460
+ hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
15287
15461
  });
15288
15462
  }
15289
15463
  if (l3.paymentRequiredBody !== void 0) {
15290
15464
  warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
15291
15465
  }
15466
+ if (l3.wwwAuthenticate !== void 0) {
15467
+ warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
15468
+ }
15292
15469
  return warnings;
15293
15470
  }
15294
15471
 
@@ -15512,6 +15689,7 @@ export {
15512
15689
  getWarningsForL2,
15513
15690
  getWarningsForL3,
15514
15691
  getWarningsForL4,
15692
+ getWarningsForMppHeader,
15515
15693
  getWarningsForOpenAPI,
15516
15694
  getWarningsForWellKnown,
15517
15695
  getWellKnown,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/discovery",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "Canonical OpenAPI-first discovery runtime for the agentcash ecosystem",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",