@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/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { composeDescription, toolDescriptions as toolDescriptions$1, HavenPaymentStateError, HavenSigningError, HavenApiError, AgentPaymentNextAction, HavenError, HavenClient, isSupportedNodeVersion, unsupportedNodeVersionMessage, verifyPaymentReceipt, discoverMerchantMcpUrl, sameUrl } from '@haven_ai/sdk';
|
|
3
|
+
import { composeDescription, toolDescriptions as toolDescriptions$1, HavenPaymentStateError, HavenSigningError, HavenApiError, AgentPaymentNextAction, HavenError, HavenClient, isSupportedNodeVersion, unsupportedNodeVersionMessage, verifyPaymentReceipt, discoverMerchantMcpUrl, sameUrl, AgentPaymentFailureCode } from '@haven_ai/sdk';
|
|
4
4
|
import { readFile, stat, mkdir, writeFile } from 'fs/promises';
|
|
5
5
|
import { z } from 'zod/v3';
|
|
6
6
|
import { createHash } from 'crypto';
|
|
@@ -48,11 +48,13 @@ async function loadCredentialsFromFile(path) {
|
|
|
48
48
|
if (!delegateKey) {
|
|
49
49
|
throw new Error("Haven MCP requires delegate_key so payments can be signed locally.");
|
|
50
50
|
}
|
|
51
|
+
const accountAddress = readAccountAddressField(raw);
|
|
51
52
|
return {
|
|
52
53
|
apiKey,
|
|
53
54
|
delegateKey,
|
|
54
55
|
agentId: stringField(raw.agent_id ?? raw.agentId),
|
|
55
|
-
|
|
56
|
+
accountAddress,
|
|
57
|
+
safeAddress: accountAddress,
|
|
56
58
|
delegateAddress: stringField(raw.delegate_address ?? raw.delegateAddress),
|
|
57
59
|
chainId: numberField(raw.chain_id ?? raw.chainId),
|
|
58
60
|
network: stringField(raw.network),
|
|
@@ -82,11 +84,13 @@ async function loadCredentialsFromSplitFiles(identityPath, signerPath) {
|
|
|
82
84
|
identity.agent_id ?? identity.agentId,
|
|
83
85
|
signer.agent_id ?? signer.agentId
|
|
84
86
|
),
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
...accountAddressTwins(
|
|
88
|
+
matchingStringField(
|
|
89
|
+
"account_address",
|
|
90
|
+
readAccountAddressField(identity),
|
|
91
|
+
readAccountAddressField(signer),
|
|
92
|
+
{ caseInsensitive: true }
|
|
93
|
+
)
|
|
90
94
|
),
|
|
91
95
|
delegateAddress: matchingStringField(
|
|
92
96
|
"delegate_address",
|
|
@@ -141,12 +145,21 @@ function loadCredentialsFromEnv() {
|
|
|
141
145
|
apiKey,
|
|
142
146
|
delegateKey,
|
|
143
147
|
agentId: stringField(process.env.HAVEN_AGENT_ID),
|
|
144
|
-
|
|
148
|
+
...accountAddressTwins(readAccountAddressEnv(process.env)),
|
|
145
149
|
chainId: numberField(process.env.HAVEN_CHAIN_ID),
|
|
146
150
|
network: stringField(process.env.HAVEN_NETWORK),
|
|
147
151
|
apiUrl: stringField(process.env.HAVEN_API_URL)
|
|
148
152
|
};
|
|
149
153
|
}
|
|
154
|
+
function readAccountAddressField(raw) {
|
|
155
|
+
return stringField(raw.account_address ?? raw.safe_address ?? raw.safeAddress);
|
|
156
|
+
}
|
|
157
|
+
function readAccountAddressEnv(env) {
|
|
158
|
+
return stringField(env.HAVEN_ACCOUNT_ADDRESS ?? env.HAVEN_WALLET_ADDRESS ?? env.HAVEN_SAFE_ADDRESS);
|
|
159
|
+
}
|
|
160
|
+
function accountAddressTwins(address) {
|
|
161
|
+
return { accountAddress: address, safeAddress: address };
|
|
162
|
+
}
|
|
150
163
|
function stringField(value) {
|
|
151
164
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
152
165
|
}
|
|
@@ -359,6 +372,8 @@ function createToolHandlers(haven) {
|
|
|
359
372
|
const attempt = () => haven.fetch(merchantUrl, init, { idempotencyKey });
|
|
360
373
|
let response = await attempt();
|
|
361
374
|
if (!response.ok) {
|
|
375
|
+
const notReady = await merchantNotReadyErrorFor(response);
|
|
376
|
+
if (notReady) throw notReady;
|
|
362
377
|
const discovered = await discoverMerchantMcpUrl(merchantUrl);
|
|
363
378
|
if (!discovered || sameUrl(discovered, merchantUrl)) {
|
|
364
379
|
throw discoveryMissError(response, merchantUrl, discovered);
|
|
@@ -367,6 +382,8 @@ function createToolHandlers(haven) {
|
|
|
367
382
|
merchantUrl = discovered;
|
|
368
383
|
const retryResponse = await attempt();
|
|
369
384
|
if (!retryResponse.ok) {
|
|
385
|
+
const notReadyAtDiscovered = await merchantNotReadyErrorFor(retryResponse);
|
|
386
|
+
if (notReadyAtDiscovered) throw notReadyAtDiscovered;
|
|
370
387
|
throw discoveryMissError(retryResponse, merchantUrl, discovered, inputUrl);
|
|
371
388
|
}
|
|
372
389
|
response = retryResponse;
|
|
@@ -600,12 +617,50 @@ function parseMaybeJson(text) {
|
|
|
600
617
|
return text;
|
|
601
618
|
}
|
|
602
619
|
}
|
|
620
|
+
var MerchantNotReadyError = class extends Error {
|
|
621
|
+
code = AgentPaymentFailureCode.MerchantNotReady;
|
|
622
|
+
statusCode = 503;
|
|
623
|
+
nextAction = AgentPaymentNextAction.StopAndTellUser;
|
|
624
|
+
// Genuinely retryable — unlike a rejection, nothing about THIS call was
|
|
625
|
+
// wrong; the merchant's own wallet needs to recover first.
|
|
626
|
+
retryWithNewQuote = true;
|
|
627
|
+
constructor(message) {
|
|
628
|
+
super(message);
|
|
629
|
+
this.name = "MerchantNotReadyError";
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
async function merchantNotReadyErrorFor(response) {
|
|
633
|
+
if (response.status !== 503) return null;
|
|
634
|
+
let body;
|
|
635
|
+
try {
|
|
636
|
+
body = await response.clone().json();
|
|
637
|
+
} catch {
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
if (!body || typeof body !== "object" || body.error !== "merchant_not_ready") {
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
const { reason_code, settlements_remaining, retry_after_s } = body;
|
|
644
|
+
return new MerchantNotReadyError(
|
|
645
|
+
"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.")
|
|
646
|
+
);
|
|
647
|
+
}
|
|
603
648
|
function discoveryMissError(response, merchantUrl, discovered, discoveredFromUrl) {
|
|
604
649
|
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}.`;
|
|
605
650
|
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).`;
|
|
606
651
|
return new HavenApiError(`${base}${guidance}`, response.status || 400);
|
|
607
652
|
}
|
|
608
653
|
function normalizeError(err) {
|
|
654
|
+
if (err instanceof MerchantNotReadyError) {
|
|
655
|
+
return {
|
|
656
|
+
success: false,
|
|
657
|
+
code: err.code,
|
|
658
|
+
message: err.message,
|
|
659
|
+
statusCode: err.statusCode,
|
|
660
|
+
nextAction: err.nextAction,
|
|
661
|
+
retry_with_new_quote: err.retryWithNewQuote
|
|
662
|
+
};
|
|
663
|
+
}
|
|
609
664
|
if (err instanceof HavenPaymentStateError) {
|
|
610
665
|
return {
|
|
611
666
|
success: false,
|
|
@@ -799,7 +854,7 @@ async function consentInputFromClient(haven, seed, toolNames) {
|
|
|
799
854
|
const summary = await haven.getAllowances();
|
|
800
855
|
const list = isAllowanceSummary(summary) ? summary.allowances : Array.isArray(summary) ? summary : [];
|
|
801
856
|
if (isAllowanceSummary(summary)) {
|
|
802
|
-
safeAddress = summary.safeAddress ?? safeAddress;
|
|
857
|
+
safeAddress = summary.accountAddress ?? summary.safeAddress ?? safeAddress;
|
|
803
858
|
delegateAddress = summary.delegateAddress;
|
|
804
859
|
chainId = typeof summary.chainId === "number" ? summary.chainId : chainId;
|
|
805
860
|
}
|
|
@@ -857,7 +912,7 @@ async function createHavenMcpServer(options = {}) {
|
|
|
857
912
|
return buildMcpServer(haven);
|
|
858
913
|
}
|
|
859
914
|
var MCP_NAME = "@haven_ai/mcp";
|
|
860
|
-
var MCP_VERSION = "0.1
|
|
915
|
+
var MCP_VERSION = "0.2.1-alpha.0";
|
|
861
916
|
var MCP_INSTRUCTIONS = [
|
|
862
917
|
"Haven local MCP server: signs in-process with the delegate key it holds on",
|
|
863
918
|
"this machine \u2014 the key never leaves this process. Call haven_get_agent",
|
|
@@ -930,7 +985,7 @@ async function runConsentGate(haven, credentials, options) {
|
|
|
930
985
|
apiKey: credentials.apiKey,
|
|
931
986
|
apiUrl: credentials.apiUrl,
|
|
932
987
|
agentId: credentials.agentId,
|
|
933
|
-
safeAddress: credentials.safeAddress,
|
|
988
|
+
safeAddress: credentials.accountAddress ?? credentials.safeAddress,
|
|
934
989
|
delegateAddress: credentials.delegateAddress,
|
|
935
990
|
chainId: credentials.chainId,
|
|
936
991
|
allowanceSummary: credentials.allowanceSummary
|