@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 +6 -1
- package/dist/cli.cjs +65 -10
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +66 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +65 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +66 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
|
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
|