@haven_ai/mcp 0.8.1-alpha.0 → 0.10.0-alpha.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/README.md CHANGED
@@ -117,6 +117,9 @@ Environment variable form:
117
117
  - `haven_discover_tools`
118
118
  - `haven_submit_catalog_entry`
119
119
  - `haven_list_receipts`
120
+ - `haven_verify_receipt`
121
+ - `haven_get_receipt` (#3723 — the signed bundle: `{ receipt }` from one of this agent's own settled payments, which `haven_verify_receipt` reads unchanged)
122
+ - `haven_get_task_budget` (#3518)
120
123
  - `haven_open_task_budget` (#3329)
121
124
  - `haven_close_task_budget` (#3329)
122
125
  - `haven_submit` (#3329 — relays a local signer signature by `task_budget_id`, or (#3506) by `sub_budget_id` for a sub-budget this agent delegates (listed in `haven_get_agent`'s `pendingSubBudgetSignatures[]`); a `payment_id` is refused on this runtime, which signs and submits payments inline)
package/dist/cli.cjs CHANGED
@@ -338,7 +338,21 @@ var toolSchemas = {
338
338
  compact: v3.z.boolean().optional().describe("true drops challengePayload, selectedPayment and protocolReceiptPayload from each row; omit for the full row.")
339
339
  },
340
340
  haven_verify_receipt: {
341
- receipt: v3.z.unknown()
341
+ receipt: v3.z.unknown(),
342
+ // #3723: the spread form — the endpoint's `{ receipt, verification }`
343
+ // spread as top-level arguments — verifies: `verification` is declared
344
+ // ACCEPTED-AND-IGNORED (Haven's own self-check, never read). The stdio
345
+ // z.object would strip it silently anyway; declaring it keeps the schema
346
+ // identical to the hosted runtime's, where the key must be declared to
347
+ // pass its strict refusal.
348
+ verification: v3.z.unknown().optional().describe("accepted and ignored \u2014 verification reads the receipt itself")
349
+ },
350
+ // #3723: the signed bundle's read — `{ receipt }` only, settled payments
351
+ // only. The endpoint's server-side `verification` is deliberately NOT
352
+ // returned: it is computed on Haven's server, not offline, and an agent
353
+ // that trusted it would skip haven_verify_receipt.
354
+ haven_get_receipt: {
355
+ payment_id: v3.z.string().min(1)
342
356
  },
343
357
  // #3329: a budget for one task that ends by itself — a short-lived child of
344
358
  // the agent's own budget, capped and time-boxed independently of the period
@@ -423,6 +437,8 @@ var toolDescriptions = {
423
437
  haven_submit_catalog_entry: sdk.composeDescription(sdk.toolDescriptions.submitCatalogEntry),
424
438
  haven_list_receipts: sdk.composeDescription(sdk.toolDescriptions.listReceipts),
425
439
  haven_verify_receipt: sdk.composeDescription(sdk.toolDescriptions.verifyReceipt),
440
+ // #3723: the signed bundle's read, described from the shared fragment.
441
+ haven_get_receipt: sdk.composeDescription(sdk.toolDescriptions.getReceipt),
426
442
  haven_open_task_budget: OPEN_TASK_BUDGET_DESCRIPTION,
427
443
  haven_close_task_budget: CLOSE_TASK_BUDGET_DESCRIPTION,
428
444
  haven_get_task_budget: GET_TASK_BUDGET_DESCRIPTION,
@@ -711,6 +727,10 @@ function createToolHandlers(haven) {
711
727
  price_atomic: entry.priceAtomic,
712
728
  asset: entry.asset,
713
729
  network: entry.network,
730
+ // #3839: same hint the hosted surface emits — a catalog hint, the
731
+ // live quote stays authoritative; "unknown" when nothing is recorded.
732
+ asset_transfer_methods: entry.assetTransferMethods ?? null,
733
+ funding_leg_expected: sdk.catalogFundingLegExpected(entry.assetTransferMethods),
714
734
  status: entry.status,
715
735
  verified_at: entry.verifiedAt,
716
736
  source: entry.source,
@@ -769,6 +789,17 @@ function createToolHandlers(haven) {
769
789
  const args = objectInput("haven_verify_receipt", input);
770
790
  return runTool(async () => sdk.verifyPaymentReceipt(args.receipt));
771
791
  },
792
+ // #3723: the signed bundle's home on the local surface. Parsed INSIDE
793
+ // runTool, so a missing payment_id is a structured INVALID_INPUT failure
794
+ // rather than a thrown ZodError (the hosted runtime's parseStrict already
795
+ // answers that way). Returns `{ receipt }` only — the endpoint's
796
+ // server-side `verification` is not offline evidence, and the agent
797
+ // verifies with haven_verify_receipt, which reads the bundle unchanged.
798
+ haven_get_receipt: async (input) => runTool(async () => {
799
+ const args = objectInput("haven_get_receipt", input);
800
+ const { receipt } = await haven.getReceipt(args.payment_id);
801
+ return { receipt };
802
+ }),
772
803
  haven_open_task_budget: async (input) => {
773
804
  const args = objectInput("haven_open_task_budget", input);
774
805
  return runTool(async () => {
@@ -965,17 +996,26 @@ function parseMaybeJson(text) {
965
996
  return text;
966
997
  }
967
998
  }
999
+ var MERCHANT_OUT_OF_GAS_REASON = "settlement_wallet_out_of_gas";
1000
+ function merchantOutOfGasMessage(settlementsRemaining, failFloor) {
1001
+ const remaining = typeof settlementsRemaining === "number" ? settlementsRemaining : null;
1002
+ const floor = typeof failFloor === "number" ? failFloor : null;
1003
+ const figures = remaining !== null && floor !== null ? ` It has gas for ${remaining} more settlement${remaining === 1 ? "" : "s"} and refuses new payments below ${floor} (settlements_remaining: ${remaining}, fail_floor: ${floor}).` : remaining !== null ? ` settlements_remaining: ${remaining}.` : "";
1004
+ return `The merchant refused this call: its settlement wallet is out of gas (reason_code: ${MERCHANT_OUT_OF_GAS_REASON}).` + figures + " No payment was created. The merchant's operator must top up its settlement wallet; until then every retry is refused again. Tell the user, and re-quote once the merchant has been topped up.";
1005
+ }
968
1006
  var MerchantNotReadyError = class extends Error {
1007
+ constructor(message, nextToolOmittedReason = "the merchant needs to recover first; re-quote after the retry_after_s in the message") {
1008
+ super(message);
1009
+ this.nextToolOmittedReason = nextToolOmittedReason;
1010
+ this.name = "MerchantNotReadyError";
1011
+ }
1012
+ nextToolOmittedReason;
969
1013
  code = sdk.AgentPaymentFailureCode.MerchantNotReady;
970
1014
  statusCode = 503;
971
1015
  nextAction = sdk.AgentPaymentNextAction.StopAndTellUser;
972
1016
  // Genuinely retryable — unlike a rejection, nothing about THIS call was
973
1017
  // wrong; the merchant's own wallet needs to recover first.
974
1018
  retryWithNewQuote = true;
975
- constructor(message) {
976
- super(message);
977
- this.name = "MerchantNotReadyError";
978
- }
979
1019
  };
980
1020
  async function merchantNotReadyErrorFor(response) {
981
1021
  if (response.status !== 503) return null;
@@ -988,7 +1028,14 @@ async function merchantNotReadyErrorFor(response) {
988
1028
  if (!body || typeof body !== "object" || body.error !== "merchant_not_ready") {
989
1029
  return null;
990
1030
  }
991
- const { reason_code, settlements_remaining, retry_after_s } = body;
1031
+ const { reason_code, settlements_remaining, fail_floor, retry_after_s } = body;
1032
+ if (reason_code === MERCHANT_OUT_OF_GAS_REASON) {
1033
+ return new MerchantNotReadyError(
1034
+ merchantOutOfGasMessage(settlements_remaining, fail_floor),
1035
+ // Same reason the hosted runtime returns (support/mcp-context.ts).
1036
+ "the merchant's operator must top up its settlement wallet first; re-quote after that"
1037
+ );
1038
+ }
992
1039
  return new MerchantNotReadyError(
993
1040
  "The merchant refused this call: it cannot settle a payment right now" + (typeof reason_code === "string" ? ` (reason_code: ${reason_code})` : "") + (typeof settlements_remaining === "number" ? `, settlements_remaining: ${settlements_remaining}` : "") + ". No payment was created." + (typeof retry_after_s === "number" ? ` Retry after approximately ${retry_after_s}s.` : " This is often transient; retry later.")
994
1041
  );
@@ -1009,8 +1056,10 @@ function normalizeError(err) {
1009
1056
  next_action: err.nextAction,
1010
1057
  retry_with_new_quote: err.retryWithNewQuote,
1011
1058
  // #3103: the local runtime's one decision site — no tool can act until
1012
- // the merchant recovers; the message carries retry_after_s.
1013
- next_tool_omitted_reason: "the merchant needs to recover first; re-quote after the retry_after_s in the message"
1059
+ // the merchant recovers. The reason rides the error (#3834): the
1060
+ // generic one points at the message's retry_after_s, the out-of-gas
1061
+ // one at the operator top-up.
1062
+ next_tool_omitted_reason: err.nextToolOmittedReason
1014
1063
  };
1015
1064
  }
1016
1065
  if (err instanceof sdk.HavenPaymentStateError) {
@@ -1052,6 +1101,14 @@ function normalizeError(err) {
1052
1101
  body: err.body
1053
1102
  };
1054
1103
  }
1104
+ if (err instanceof v3.z.ZodError) {
1105
+ return {
1106
+ success: false,
1107
+ code: "INVALID_INPUT",
1108
+ message: err.errors.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`).join("; "),
1109
+ statusCode: 400
1110
+ };
1111
+ }
1055
1112
  if (err instanceof sdk.HavenError) {
1056
1113
  return {
1057
1114
  success: false,
@@ -1280,7 +1337,7 @@ async function resolveHavenClient(options = {}) {
1280
1337
  return { client, credentials };
1281
1338
  }
1282
1339
  var MCP_NAME = "@haven_ai/mcp";
1283
- var MCP_VERSION = "0.8.1-alpha.0";
1340
+ var MCP_VERSION = "0.10.0-alpha.0";
1284
1341
  var MCP_INSTRUCTIONS = [
1285
1342
  "Haven local MCP server: signs in-process with the delegate key it holds on",
1286
1343
  "this machine \u2014 the key never leaves this process. Call haven_get_agent",