@haven_ai/mcp 0.1.37-alpha.0 → 0.2.1-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
@@ -36,12 +36,17 @@ Create a private JSON file from the values in the Haven agent handoff:
36
36
  "api_key": "sk_agent_...",
37
37
  "delegate_key": "0x...",
38
38
  "agent_id": "agent-id",
39
- "safe_address": "0xYourHavenWallet",
39
+ "account_address": "0xYourHavenWallet",
40
40
  "api_url": "https://havenbackend.example"
41
41
  }
42
42
  ```
43
43
 
44
44
  `delegate_key` is required. Without it the MCP server cannot sign locally.
45
+ `account_address` is the name the connector writes since #2908; a file that
46
+ still says `safe_address` (or `safeAddress`) is read the same way, permanently.
47
+ The same holds for the environment: `HAVEN_ACCOUNT_ADDRESS` is read first, and
48
+ the older `HAVEN_WALLET_ADDRESS` / `HAVEN_SAFE_ADDRESS` are accepted for one
49
+ release (#2914 removes them).
45
50
 
46
51
  The Haven connector may also write split credentials:
47
52
 
package/dist/cli.cjs CHANGED
@@ -50,11 +50,13 @@ async function loadCredentialsFromFile(path) {
50
50
  if (!delegateKey) {
51
51
  throw new Error("Haven MCP requires delegate_key so payments can be signed locally.");
52
52
  }
53
+ const accountAddress = readAccountAddressField(raw);
53
54
  return {
54
55
  apiKey,
55
56
  delegateKey,
56
57
  agentId: stringField(raw.agent_id ?? raw.agentId),
57
- safeAddress: stringField(raw.safe_address ?? raw.safeAddress),
58
+ accountAddress,
59
+ safeAddress: accountAddress,
58
60
  delegateAddress: stringField(raw.delegate_address ?? raw.delegateAddress),
59
61
  chainId: numberField(raw.chain_id ?? raw.chainId),
60
62
  network: stringField(raw.network),
@@ -84,11 +86,13 @@ async function loadCredentialsFromSplitFiles(identityPath, signerPath) {
84
86
  identity.agent_id ?? identity.agentId,
85
87
  signer.agent_id ?? signer.agentId
86
88
  ),
87
- safeAddress: matchingStringField(
88
- "safe_address",
89
- identity.safe_address ?? identity.safeAddress,
90
- signer.safe_address ?? signer.safeAddress,
91
- { caseInsensitive: true }
89
+ ...accountAddressTwins(
90
+ matchingStringField(
91
+ "account_address",
92
+ readAccountAddressField(identity),
93
+ readAccountAddressField(signer),
94
+ { caseInsensitive: true }
95
+ )
92
96
  ),
93
97
  delegateAddress: matchingStringField(
94
98
  "delegate_address",
@@ -143,12 +147,21 @@ function loadCredentialsFromEnv() {
143
147
  apiKey,
144
148
  delegateKey,
145
149
  agentId: stringField(process.env.HAVEN_AGENT_ID),
146
- safeAddress: stringField(process.env.HAVEN_SAFE_ADDRESS),
150
+ ...accountAddressTwins(readAccountAddressEnv(process.env)),
147
151
  chainId: numberField(process.env.HAVEN_CHAIN_ID),
148
152
  network: stringField(process.env.HAVEN_NETWORK),
149
153
  apiUrl: stringField(process.env.HAVEN_API_URL)
150
154
  };
151
155
  }
156
+ function readAccountAddressField(raw) {
157
+ return stringField(raw.account_address ?? raw.safe_address ?? raw.safeAddress);
158
+ }
159
+ function readAccountAddressEnv(env) {
160
+ return stringField(env.HAVEN_ACCOUNT_ADDRESS ?? env.HAVEN_WALLET_ADDRESS ?? env.HAVEN_SAFE_ADDRESS);
161
+ }
162
+ function accountAddressTwins(address) {
163
+ return { accountAddress: address, safeAddress: address };
164
+ }
152
165
  function stringField(value) {
153
166
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
154
167
  }
@@ -361,6 +374,8 @@ function createToolHandlers(haven) {
361
374
  const attempt = () => haven.fetch(merchantUrl, init, { idempotencyKey });
362
375
  let response = await attempt();
363
376
  if (!response.ok) {
377
+ const notReady = await merchantNotReadyErrorFor(response);
378
+ if (notReady) throw notReady;
364
379
  const discovered = await sdk.discoverMerchantMcpUrl(merchantUrl);
365
380
  if (!discovered || sdk.sameUrl(discovered, merchantUrl)) {
366
381
  throw discoveryMissError(response, merchantUrl, discovered);
@@ -369,6 +384,8 @@ function createToolHandlers(haven) {
369
384
  merchantUrl = discovered;
370
385
  const retryResponse = await attempt();
371
386
  if (!retryResponse.ok) {
387
+ const notReadyAtDiscovered = await merchantNotReadyErrorFor(retryResponse);
388
+ if (notReadyAtDiscovered) throw notReadyAtDiscovered;
372
389
  throw discoveryMissError(retryResponse, merchantUrl, discovered, inputUrl);
373
390
  }
374
391
  response = retryResponse;
@@ -602,12 +619,50 @@ function parseMaybeJson(text) {
602
619
  return text;
603
620
  }
604
621
  }
622
+ var MerchantNotReadyError = class extends Error {
623
+ code = sdk.AgentPaymentFailureCode.MerchantNotReady;
624
+ statusCode = 503;
625
+ nextAction = sdk.AgentPaymentNextAction.StopAndTellUser;
626
+ // Genuinely retryable — unlike a rejection, nothing about THIS call was
627
+ // wrong; the merchant's own wallet needs to recover first.
628
+ retryWithNewQuote = true;
629
+ constructor(message) {
630
+ super(message);
631
+ this.name = "MerchantNotReadyError";
632
+ }
633
+ };
634
+ async function merchantNotReadyErrorFor(response) {
635
+ if (response.status !== 503) return null;
636
+ let body;
637
+ try {
638
+ body = await response.clone().json();
639
+ } catch {
640
+ return null;
641
+ }
642
+ if (!body || typeof body !== "object" || body.error !== "merchant_not_ready") {
643
+ return null;
644
+ }
645
+ const { reason_code, settlements_remaining, retry_after_s } = body;
646
+ return new MerchantNotReadyError(
647
+ "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.")
648
+ );
649
+ }
605
650
  function discoveryMissError(response, merchantUrl, discovered, discoveredFromUrl) {
606
651
  const base = discoveredFromUrl ? `Merchant call to ${merchantUrl} failed with HTTP ${response.status} (at the DISCOVERED endpoint ${merchantUrl}, resolved from ${discoveredFromUrl} via the merchant discovery document).` : `Merchant call to ${merchantUrl} failed with HTTP ${response.status}.`;
607
652
  const guidance = discoveredFromUrl ? "" : discovered ? ` Same-origin discovery resolved the same URL (${discovered}), which still did not answer successfully.` : ` No same-origin discovery document was found at /.well-known/haven-demo-merchant or /. If ${merchantUrl} is a base merchant URL, pass the exact MCP endpoint instead (often <origin>/mcp).`;
608
653
  return new sdk.HavenApiError(`${base}${guidance}`, response.status || 400);
609
654
  }
610
655
  function normalizeError(err) {
656
+ if (err instanceof MerchantNotReadyError) {
657
+ return {
658
+ success: false,
659
+ code: err.code,
660
+ message: err.message,
661
+ statusCode: err.statusCode,
662
+ nextAction: err.nextAction,
663
+ retry_with_new_quote: err.retryWithNewQuote
664
+ };
665
+ }
611
666
  if (err instanceof sdk.HavenPaymentStateError) {
612
667
  return {
613
668
  success: false,
@@ -801,7 +856,7 @@ async function consentInputFromClient(haven, seed, toolNames) {
801
856
  const summary = await haven.getAllowances();
802
857
  const list = isAllowanceSummary(summary) ? summary.allowances : Array.isArray(summary) ? summary : [];
803
858
  if (isAllowanceSummary(summary)) {
804
- safeAddress = summary.safeAddress ?? safeAddress;
859
+ safeAddress = summary.accountAddress ?? summary.safeAddress ?? safeAddress;
805
860
  delegateAddress = summary.delegateAddress;
806
861
  chainId = typeof summary.chainId === "number" ? summary.chainId : chainId;
807
862
  }
@@ -851,7 +906,7 @@ async function resolveHavenClient(options = {}) {
851
906
  return { client, credentials };
852
907
  }
853
908
  var MCP_NAME = "@haven_ai/mcp";
854
- var MCP_VERSION = "0.1.37-alpha.0";
909
+ var MCP_VERSION = "0.2.1-alpha.0";
855
910
  var MCP_INSTRUCTIONS = [
856
911
  "Haven local MCP server: signs in-process with the delegate key it holds on",
857
912
  "this machine \u2014 the key never leaves this process. Call haven_get_agent",
@@ -924,7 +979,7 @@ async function runConsentGate(haven, credentials, options) {
924
979
  apiKey: credentials.apiKey,
925
980
  apiUrl: credentials.apiUrl,
926
981
  agentId: credentials.agentId,
927
- safeAddress: credentials.safeAddress,
982
+ safeAddress: credentials.accountAddress ?? credentials.safeAddress,
928
983
  delegateAddress: credentials.delegateAddress,
929
984
  chainId: credentials.chainId,
930
985
  allowanceSummary: credentials.allowanceSummary