@haven_ai/mcp 0.6.0-alpha.0 → 0.7.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 +8 -13
- package/dist/cli.cjs +78 -42
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +79 -43
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +81 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -9
- package/dist/index.d.ts +77 -9
- package/dist/index.js +80 -44
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -121,21 +121,16 @@ Environment variable form:
|
|
|
121
121
|
- `haven_close_task_budget` (#3329)
|
|
122
122
|
- `haven_submit` (#3329 — relays a local signer signature by `task_budget_id`; a `payment_id` is refused on this runtime, which signs and submits payments inline)
|
|
123
123
|
|
|
124
|
-
### `idempotencyKey` is
|
|
124
|
+
### `idempotencyKey` is removed — send `idempotency_key`
|
|
125
125
|
|
|
126
126
|
The tools that take an idempotency key (`haven_send`, `haven_pay_mcp_tool`,
|
|
127
|
-
`haven_quote_x402`, `haven_pay_x402_quote`, `haven_pay_x402`)
|
|
128
|
-
**`idempotency_key`**, the spelling the hosted Haven MCP surface
|
|
129
|
-
Haven wire contract use. `idempotencyKey` still works and returns a deprecation
|
|
130
|
-
warning; it will be removed in a future release.
|
|
127
|
+
`haven_quote_x402`, `haven_pay_x402_quote`, `haven_pay_x402`) take
|
|
128
|
+
**`idempotency_key`**, the spelling the hosted Haven MCP surface uses.
|
|
131
129
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
The warning arrives in an optional `warnings` array on the success result. It is
|
|
138
|
-
additive: a caller that ignores it sees the response it always saw.
|
|
130
|
+
The legacy `idempotencyKey` spelling is **refused**, not silently accepted or
|
|
131
|
+
dropped: any call that sets it — alone or alongside `idempotency_key` — fails
|
|
132
|
+
with `IDEMPOTENCY_KEY_RENAMED` before anything is contacted or spent. Send
|
|
133
|
+
`idempotency_key` instead.
|
|
139
134
|
|
|
140
135
|
## First-launch consent
|
|
141
136
|
|
|
@@ -159,7 +154,7 @@ Tools this server will expose to your agent runtime:
|
|
|
159
154
|
|
|
160
155
|
On-chain budget (the real spend gate — enforced by the agent's
|
|
161
156
|
signed delegation, not by Haven):
|
|
162
|
-
• up to
|
|
157
|
+
• up to 25 USDC per 1440 min
|
|
163
158
|
|
|
164
159
|
Anything above the on-chain budget is declined before any money
|
|
165
160
|
moves — it is not queued, and no one is asked to review it. If the
|
package/dist/cli.cjs
CHANGED
|
@@ -238,6 +238,17 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
+
var TASK_BUDGET_MIN_SIGNER_VERSION = "0.6.0-alpha.0";
|
|
242
|
+
var SIGNER_CAPABILITY_KEY = "haven/signer-compatibility";
|
|
243
|
+
function taskSignerCompatibilityNotice() {
|
|
244
|
+
return {
|
|
245
|
+
task_sign_context_version: sdk.TASK_SIGN_CONTEXT_VERSION,
|
|
246
|
+
min_signer_version: TASK_BUDGET_MIN_SIGNER_VERSION,
|
|
247
|
+
signer_capability: SIGNER_CAPABILITY_KEY,
|
|
248
|
+
check: `Call next_tool with next_arguments EXACTLY as given \u2014 the signer fetches the exact bytes by task_budget_id. If haven_sign answers SIGNING_ERROR with a message starting "Pass payment_id (preferred for delegation-rail x402", the signer predates task budgets and signed nothing: first close this budget with haven_close_task_budget { task_budget_id } to release it, then update the connector by running \`${sdk.connectorUpgradeCommand()}\` and the repair line it prints, then open the budget again.`,
|
|
249
|
+
fallback: `haven_sign answered SIGNING_ERROR with a message starting "Pass payment_id (preferred for delegation-rail x402" \u2014 the signer predates task budgets and signed nothing: call haven_close_task_budget { task_budget_id } to release the pending budget, then update the connector by running \`${sdk.connectorUpgradeCommand()}\` and the repair line it prints, then open the budget again. There is no relay fallback for this signing context.`
|
|
250
|
+
};
|
|
251
|
+
}
|
|
241
252
|
var headersSchema = v3.z.record(v3.z.string(), v3.z.string()).optional();
|
|
242
253
|
var toolSchemas = {
|
|
243
254
|
haven_send: {
|
|
@@ -245,18 +256,24 @@ var toolSchemas = {
|
|
|
245
256
|
recipient: v3.z.string().min(1),
|
|
246
257
|
amount: v3.z.string().min(1),
|
|
247
258
|
idempotency_key: v3.z.string().optional(),
|
|
248
|
-
/**
|
|
249
|
-
idempotencyKey: v3.z.string().optional(),
|
|
259
|
+
/** REMOVED (#3411): declared only so the resolver can refuse it by name, never accepted. */
|
|
260
|
+
idempotencyKey: v3.z.string().optional().describe("REMOVED (#3411): refused \u2014 send idempotency_key"),
|
|
250
261
|
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
251
|
-
task_budget_id: v3.z.string().min(1).optional()
|
|
262
|
+
task_budget_id: v3.z.string().min(1).optional(),
|
|
263
|
+
/**
|
|
264
|
+
* #3330: spend against an open sub-budget this agent HOLDS (it is the
|
|
265
|
+
* sub-agent B) instead of the agent's own budget. Mutually exclusive
|
|
266
|
+
* with task_budget_id — the backend refuses a body naming both.
|
|
267
|
+
*/
|
|
268
|
+
sub_budget_id: v3.z.string().min(1).optional()
|
|
252
269
|
},
|
|
253
270
|
haven_pay_mcp_tool: {
|
|
254
271
|
merchant_url: v3.z.string().url(),
|
|
255
272
|
tool_name: v3.z.string().min(1),
|
|
256
273
|
arguments: v3.z.record(v3.z.string(), v3.z.unknown()).optional(),
|
|
257
274
|
idempotency_key: v3.z.string().optional(),
|
|
258
|
-
/**
|
|
259
|
-
idempotencyKey: v3.z.string().optional()
|
|
275
|
+
/** REMOVED (#3411): declared only so the resolver can refuse it by name, never accepted. */
|
|
276
|
+
idempotencyKey: v3.z.string().optional().describe("REMOVED (#3411): refused \u2014 send idempotency_key")
|
|
260
277
|
},
|
|
261
278
|
haven_quote_x402: {
|
|
262
279
|
url: v3.z.string().url(),
|
|
@@ -264,16 +281,18 @@ var toolSchemas = {
|
|
|
264
281
|
headers: headersSchema,
|
|
265
282
|
body: v3.z.string().optional(),
|
|
266
283
|
idempotency_key: v3.z.string().optional(),
|
|
267
|
-
/**
|
|
268
|
-
idempotencyKey: v3.z.string().optional()
|
|
284
|
+
/** REMOVED (#3411): declared only so the resolver can refuse it by name, never accepted. */
|
|
285
|
+
idempotencyKey: v3.z.string().optional().describe("REMOVED (#3411): refused \u2014 send idempotency_key")
|
|
269
286
|
},
|
|
270
287
|
haven_pay_x402_quote: {
|
|
271
288
|
quote: v3.z.unknown(),
|
|
272
289
|
idempotency_key: v3.z.string().optional(),
|
|
273
|
-
/**
|
|
274
|
-
idempotencyKey: v3.z.string().optional(),
|
|
290
|
+
/** REMOVED (#3411): declared only so the resolver can refuse it by name, never accepted. */
|
|
291
|
+
idempotencyKey: v3.z.string().optional().describe("REMOVED (#3411): refused \u2014 send idempotency_key"),
|
|
275
292
|
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
276
|
-
task_budget_id: v3.z.string().min(1).optional()
|
|
293
|
+
task_budget_id: v3.z.string().min(1).optional(),
|
|
294
|
+
/** #3330: spend against an open sub-budget this agent holds (it is sub-agent B). Mutually exclusive with task_budget_id. */
|
|
295
|
+
sub_budget_id: v3.z.string().min(1).optional()
|
|
277
296
|
},
|
|
278
297
|
haven_pay_x402: {
|
|
279
298
|
url: v3.z.string().url(),
|
|
@@ -281,10 +300,12 @@ var toolSchemas = {
|
|
|
281
300
|
headers: headersSchema,
|
|
282
301
|
body: v3.z.string().optional(),
|
|
283
302
|
idempotency_key: v3.z.string().optional(),
|
|
284
|
-
/**
|
|
285
|
-
idempotencyKey: v3.z.string().optional(),
|
|
303
|
+
/** REMOVED (#3411): declared only so the resolver can refuse it by name, never accepted. */
|
|
304
|
+
idempotencyKey: v3.z.string().optional().describe("REMOVED (#3411): refused \u2014 send idempotency_key"),
|
|
286
305
|
/** #3329: spend against an open task budget instead of the agent's period budget. */
|
|
287
|
-
task_budget_id: v3.z.string().min(1).optional()
|
|
306
|
+
task_budget_id: v3.z.string().min(1).optional(),
|
|
307
|
+
/** #3330: spend against an open sub-budget this agent holds (it is sub-agent B). Mutually exclusive with task_budget_id. */
|
|
308
|
+
sub_budget_id: v3.z.string().min(1).optional()
|
|
288
309
|
},
|
|
289
310
|
haven_resume_x402_payment: {
|
|
290
311
|
payment_id: v3.z.string().optional(),
|
|
@@ -312,7 +333,9 @@ var toolSchemas = {
|
|
|
312
333
|
haven_list_receipts: {
|
|
313
334
|
limit: v3.z.number().int().min(1).max(100).optional(),
|
|
314
335
|
/** #3128: the previous page's next_cursor (a receipt id). */
|
|
315
|
-
cursor: v3.z.string().min(1).optional()
|
|
336
|
+
cursor: v3.z.string().min(1).optional(),
|
|
337
|
+
/** #3423: drop each row's payload echoes; the default shape is unchanged. */
|
|
338
|
+
compact: v3.z.boolean().optional().describe("true drops challengePayload, selectedPayment and protocolReceiptPayload from each row; omit for the full row.")
|
|
316
339
|
},
|
|
317
340
|
haven_verify_receipt: {
|
|
318
341
|
receipt: v3.z.unknown()
|
|
@@ -401,12 +424,15 @@ function createToolHandlers(haven) {
|
|
|
401
424
|
token: args.asset,
|
|
402
425
|
amount: args.amount,
|
|
403
426
|
to: args.recipient,
|
|
404
|
-
// #1207: was accepted by the schema but silently dropped — now
|
|
405
|
-
// carried to the backend's replay contract.
|
|
406
|
-
idempotencyKey: args.
|
|
427
|
+
// #1207/#3411: was accepted by the schema but silently dropped — now
|
|
428
|
+
// carried to the backend's replay contract via the resolved carrier.
|
|
429
|
+
idempotencyKey: args.resolvedIdempotencyKey,
|
|
407
430
|
// #3329: spend against an open task budget instead of the
|
|
408
431
|
// agent's period budget, when the caller names one.
|
|
409
|
-
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {}
|
|
432
|
+
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {},
|
|
433
|
+
// #3330: spend through a sub-budget this agent holds (it is
|
|
434
|
+
// sub-agent B). Exactly one of the two ids, never both.
|
|
435
|
+
...typeof args.sub_budget_id === "string" ? { subBudgetId: args.sub_budget_id } : {}
|
|
410
436
|
});
|
|
411
437
|
return {
|
|
412
438
|
payment_id: result.paymentId,
|
|
@@ -444,7 +470,7 @@ function createToolHandlers(haven) {
|
|
|
444
470
|
body: JSON.stringify(envelope)
|
|
445
471
|
};
|
|
446
472
|
let merchantUrl = args.merchant_url;
|
|
447
|
-
const idempotencyKey = args.
|
|
473
|
+
const idempotencyKey = args.resolvedIdempotencyKey;
|
|
448
474
|
const attempt = () => haven.fetch(merchantUrl, init, { idempotencyKey });
|
|
449
475
|
let response = await attempt();
|
|
450
476
|
if (!response.ok) {
|
|
@@ -478,7 +504,7 @@ function createToolHandlers(haven) {
|
|
|
478
504
|
if ("success" in pf) return pf;
|
|
479
505
|
const { args, warnings } = pf;
|
|
480
506
|
try {
|
|
481
|
-
const data = await haven.quoteX402(args.url, requestInit(args), { idempotencyKey: args.
|
|
507
|
+
const data = await haven.quoteX402(args.url, requestInit(args), { idempotencyKey: args.resolvedIdempotencyKey });
|
|
482
508
|
return warnings.length > 0 ? { success: true, data, warnings } : { success: true, data };
|
|
483
509
|
} catch (err) {
|
|
484
510
|
return normalizeError(err);
|
|
@@ -505,8 +531,9 @@ function createToolHandlers(haven) {
|
|
|
505
531
|
}
|
|
506
532
|
return runTool(async () => {
|
|
507
533
|
const response = await haven.payX402Quote(args.quote, {
|
|
508
|
-
idempotencyKey: args.
|
|
509
|
-
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {}
|
|
534
|
+
idempotencyKey: args.resolvedIdempotencyKey,
|
|
535
|
+
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {},
|
|
536
|
+
...typeof args.sub_budget_id === "string" ? { subBudgetId: args.sub_budget_id } : {}
|
|
510
537
|
});
|
|
511
538
|
return responsePayload(response);
|
|
512
539
|
}, warnings);
|
|
@@ -517,8 +544,9 @@ function createToolHandlers(haven) {
|
|
|
517
544
|
const { args, warnings } = pf;
|
|
518
545
|
return runTool(async () => {
|
|
519
546
|
const response = await haven.fetch(args.url, requestInit(args), {
|
|
520
|
-
idempotencyKey: args.
|
|
521
|
-
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {}
|
|
547
|
+
idempotencyKey: args.resolvedIdempotencyKey,
|
|
548
|
+
...typeof args.task_budget_id === "string" ? { taskBudgetId: args.task_budget_id } : {},
|
|
549
|
+
...typeof args.sub_budget_id === "string" ? { subBudgetId: args.sub_budget_id } : {}
|
|
522
550
|
});
|
|
523
551
|
return responsePayload(response);
|
|
524
552
|
}, warnings);
|
|
@@ -615,7 +643,9 @@ function createToolHandlers(haven) {
|
|
|
615
643
|
},
|
|
616
644
|
haven_list_receipts: async (input) => {
|
|
617
645
|
const args = objectInput("haven_list_receipts", input);
|
|
618
|
-
return runTool(
|
|
646
|
+
return runTool(
|
|
647
|
+
async () => haven.listReceiptsPage({ limit: args.limit, cursor: args.cursor, compact: args.compact === true })
|
|
648
|
+
);
|
|
619
649
|
},
|
|
620
650
|
haven_verify_receipt: async (input) => {
|
|
621
651
|
const args = objectInput("haven_verify_receipt", input);
|
|
@@ -643,7 +673,8 @@ function createToolHandlers(haven) {
|
|
|
643
673
|
task_budget: result.taskBudget,
|
|
644
674
|
next_action: "sign",
|
|
645
675
|
next_tool: "mcp__haven-signer__haven_sign",
|
|
646
|
-
next_arguments: { task_budget_id: result.taskBudget.id }
|
|
676
|
+
next_arguments: { task_budget_id: result.taskBudget.id },
|
|
677
|
+
signer_compatibility: taskSignerCompatibilityNotice()
|
|
647
678
|
};
|
|
648
679
|
});
|
|
649
680
|
},
|
|
@@ -658,7 +689,8 @@ function createToolHandlers(haven) {
|
|
|
658
689
|
task_budget: result.taskBudget,
|
|
659
690
|
next_action: "sign",
|
|
660
691
|
next_tool: "mcp__haven-signer__haven_sign",
|
|
661
|
-
next_arguments: { task_budget_id: args.task_budget_id }
|
|
692
|
+
next_arguments: { task_budget_id: args.task_budget_id },
|
|
693
|
+
signer_compatibility: taskSignerCompatibilityNotice()
|
|
662
694
|
};
|
|
663
695
|
});
|
|
664
696
|
},
|
|
@@ -715,28 +747,20 @@ function preflight(name, input) {
|
|
|
715
747
|
}
|
|
716
748
|
const idem = resolveIdempotencyKey(args);
|
|
717
749
|
if ("success" in idem) return idem;
|
|
718
|
-
if (idem.key !== void 0) args = { ...args,
|
|
750
|
+
if (idem.key !== void 0) args = { ...args, resolvedIdempotencyKey: idem.key };
|
|
719
751
|
return { args, warnings: idem.warnings };
|
|
720
752
|
}
|
|
721
753
|
function resolveIdempotencyKey(args) {
|
|
722
754
|
const modern = typeof args.idempotency_key === "string" ? args.idempotency_key : void 0;
|
|
723
755
|
const legacy = typeof args.idempotencyKey === "string" ? args.idempotencyKey : void 0;
|
|
724
|
-
if (
|
|
756
|
+
if (legacy !== void 0) {
|
|
725
757
|
return {
|
|
726
758
|
success: false,
|
|
727
|
-
code: "
|
|
728
|
-
message: "
|
|
759
|
+
code: "IDEMPOTENCY_KEY_RENAMED",
|
|
760
|
+
message: "idempotencyKey has been renamed to idempotency_key and is no longer accepted. Send idempotency_key instead. Nothing was contacted or spent."
|
|
729
761
|
};
|
|
730
762
|
}
|
|
731
763
|
if (modern !== void 0) return { key: modern, warnings: [] };
|
|
732
|
-
if (legacy !== void 0) {
|
|
733
|
-
return {
|
|
734
|
-
key: legacy,
|
|
735
|
-
warnings: [
|
|
736
|
-
"idempotencyKey is deprecated and will be removed in a future release of @haven_ai/mcp. Send idempotency_key instead \u2014 it is the spelling the hosted Haven MCP surface and every other Haven wire contract use."
|
|
737
|
-
]
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
764
|
return { warnings: [] };
|
|
741
765
|
}
|
|
742
766
|
function buildMcpToolsCallEnvelope(toolName, args) {
|
|
@@ -967,7 +991,7 @@ function renderConsentBlock(input, hash) {
|
|
|
967
991
|
lines.push("signed delegation, not by Haven):");
|
|
968
992
|
for (const a of input.allowanceSummary) {
|
|
969
993
|
const reset = a.resetMinutes ? ` per ${a.resetMinutes} min` : " (no reset)";
|
|
970
|
-
lines.push(` \u2022 up to ${a
|
|
994
|
+
lines.push(` \u2022 up to ${describeBudgetAmount(a, input.chainId)}${reset}`);
|
|
971
995
|
}
|
|
972
996
|
}
|
|
973
997
|
lines.push("");
|
|
@@ -1064,7 +1088,14 @@ async function consentInputFromClient(haven, seed, toolNames) {
|
|
|
1064
1088
|
}
|
|
1065
1089
|
const liveAllowanceSummary = list.map((a) => ({
|
|
1066
1090
|
token: a.tokenSymbol ?? "UNKNOWN",
|
|
1067
|
-
|
|
1091
|
+
// #3410: `onchain.amount` is the atomic budget (`budget_atomic` on the
|
|
1092
|
+
// backend). The old `a.configuredAmount` fallback here was unreachable
|
|
1093
|
+
// — `onchain` is required in the SDK types and dereferenced
|
|
1094
|
+
// unconditionally, so a response without it throws into the seed path —
|
|
1095
|
+
// and it was the one HUMAN-decimal value in this chain; a human number
|
|
1096
|
+
// must never reach an atomic field. '0' keeps the (defensive) shape for
|
|
1097
|
+
// a malformed onchain object without inventing a unit.
|
|
1098
|
+
amount: a.onchain?.amount ?? "0",
|
|
1068
1099
|
resetMinutes: typeof a.onchain?.resetTimeMin === "number" ? a.onchain.resetTimeMin : typeof a.resetPeriodMin === "number" ? a.resetPeriodMin : null
|
|
1069
1100
|
}));
|
|
1070
1101
|
allowanceSummary = liveAllowanceSummary;
|
|
@@ -1087,6 +1118,11 @@ function isAllowanceSummary(value) {
|
|
|
1087
1118
|
function derivePrefix(apiKey) {
|
|
1088
1119
|
return apiKey.slice(0, 12);
|
|
1089
1120
|
}
|
|
1121
|
+
function describeBudgetAmount(a, chainId) {
|
|
1122
|
+
const token = chainId === void 0 ? null : sdk.resolveTokenBySymbol(chainId, a.token);
|
|
1123
|
+
if (!token) return `${a.amount} ${a.token} (atomic units)`;
|
|
1124
|
+
return `${sdk.formatTokenAmount(a.amount, token.decimals)} ${a.token}`;
|
|
1125
|
+
}
|
|
1090
1126
|
function registeredToolNames() {
|
|
1091
1127
|
return Object.keys(toolSchemas);
|
|
1092
1128
|
}
|
|
@@ -1111,7 +1147,7 @@ async function resolveHavenClient(options = {}) {
|
|
|
1111
1147
|
return { client, credentials };
|
|
1112
1148
|
}
|
|
1113
1149
|
var MCP_NAME = "@haven_ai/mcp";
|
|
1114
|
-
var MCP_VERSION = "0.
|
|
1150
|
+
var MCP_VERSION = "0.7.0-alpha.0";
|
|
1115
1151
|
var MCP_INSTRUCTIONS = [
|
|
1116
1152
|
"Haven local MCP server: signs in-process with the delegate key it holds on",
|
|
1117
1153
|
"this machine \u2014 the key never leaves this process. Call haven_get_agent",
|