@haven_ai/mcp 0.0.0-dev.202609061037.7cf43bb → 0.0.0-dev.202609061923.f3d53db

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
@@ -110,6 +110,22 @@ Environment variable form:
110
110
  - `haven_submit_catalog_entry`
111
111
  - `haven_list_receipts`
112
112
 
113
+ ### `idempotencyKey` is deprecated — send `idempotency_key`
114
+
115
+ The tools that take an idempotency key (`haven_send`, `haven_pay_mcp_tool`,
116
+ `haven_quote_x402`, `haven_pay_x402_quote`, `haven_pay_x402`) now accept
117
+ **`idempotency_key`**, the spelling the hosted Haven MCP surface and every other
118
+ Haven wire contract use. `idempotencyKey` still works and returns a deprecation
119
+ warning; it will be removed in a future release.
120
+
121
+ Send **one** of them. Sending both with different values is refused
122
+ (`AMBIGUOUS_IDEMPOTENCY_KEY`) with nothing contacted or spent — the key decides
123
+ whether a retry is the same payment or a second one, so Haven will not guess
124
+ which scope you meant. Sending both with the same value is fine.
125
+
126
+ The warning arrives in an optional `warnings` array on the success result. It is
127
+ additive: a caller that ignores it sees the response it always saw.
128
+
113
129
  ## First-launch consent
114
130
 
115
131
  The first time the MCP server runs against a credential file it refuses to
package/dist/cli.cjs CHANGED
@@ -222,12 +222,16 @@ var toolSchemas = {
222
222
  asset: v3.z.enum(["ETH", "USDC"]),
223
223
  recipient: v3.z.string().min(1),
224
224
  amount: v3.z.string().min(1),
225
+ idempotency_key: v3.z.string().optional(),
226
+ /** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
225
227
  idempotencyKey: v3.z.string().optional()
226
228
  },
227
229
  haven_pay_mcp_tool: {
228
230
  merchant_url: v3.z.string().url(),
229
231
  tool_name: v3.z.string().min(1),
230
232
  arguments: v3.z.record(v3.z.string(), v3.z.unknown()).optional(),
233
+ idempotency_key: v3.z.string().optional(),
234
+ /** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
231
235
  idempotencyKey: v3.z.string().optional()
232
236
  },
233
237
  haven_quote_x402: {
@@ -235,10 +239,14 @@ var toolSchemas = {
235
239
  method: v3.z.string().optional(),
236
240
  headers: headersSchema,
237
241
  body: v3.z.string().optional(),
242
+ idempotency_key: v3.z.string().optional(),
243
+ /** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
238
244
  idempotencyKey: v3.z.string().optional()
239
245
  },
240
246
  haven_pay_x402_quote: {
241
247
  quote: v3.z.unknown(),
248
+ idempotency_key: v3.z.string().optional(),
249
+ /** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
242
250
  idempotencyKey: v3.z.string().optional()
243
251
  },
244
252
  haven_pay_x402: {
@@ -246,6 +254,8 @@ var toolSchemas = {
246
254
  method: v3.z.string().optional(),
247
255
  headers: headersSchema,
248
256
  body: v3.z.string().optional(),
257
+ idempotency_key: v3.z.string().optional(),
258
+ /** Legacy spelling, accepted during the #2366 window. Warns; do not use. */
249
259
  idempotencyKey: v3.z.string().optional()
250
260
  },
251
261
  haven_resume_x402_payment: {
@@ -298,8 +308,10 @@ var toolDescriptions = {
298
308
  function createToolHandlers(haven) {
299
309
  return {
300
310
  haven_send: async (input) => {
311
+ const pf = preflight("haven_send", input);
312
+ if ("success" in pf) return pf;
313
+ const { args, warnings } = pf;
301
314
  return runTool(async () => {
302
- const args = objectInput("haven_send", input);
303
315
  try {
304
316
  const result = await haven.pay({
305
317
  token: args.asset,
@@ -307,7 +319,7 @@ function createToolHandlers(haven) {
307
319
  to: args.recipient,
308
320
  // #1207: was accepted by the schema but silently dropped — now
309
321
  // carried to the backend's replay contract.
310
- idempotencyKey: typeof args.idempotencyKey === "string" ? args.idempotencyKey : void 0
322
+ idempotencyKey: args.idempotencyKey
311
323
  });
312
324
  return {
313
325
  payment_id: result.paymentId,
@@ -331,11 +343,13 @@ function createToolHandlers(haven) {
331
343
  }
332
344
  throw err;
333
345
  }
334
- });
346
+ }, warnings);
335
347
  },
336
348
  haven_pay_mcp_tool: async (input) => {
349
+ const pf = preflight("haven_pay_mcp_tool", input);
350
+ if ("success" in pf) return pf;
351
+ const { args, warnings } = pf;
337
352
  return runTool(async () => {
338
- const args = objectInput("haven_pay_mcp_tool", input);
339
353
  const envelope = buildMcpToolsCallEnvelope(args.tool_name, args.arguments);
340
354
  const init = {
341
355
  method: "POST",
@@ -366,18 +380,23 @@ function createToolHandlers(haven) {
366
380
  merchant_url: merchantUrl,
367
381
  ...merchantUrl !== args.merchant_url ? { merchant_url_discovered_from: args.merchant_url } : {}
368
382
  };
369
- });
383
+ }, warnings);
370
384
  },
371
385
  haven_quote_x402: async (input) => {
372
- const args = objectInput("haven_quote_x402", input);
386
+ const pf = preflight("haven_quote_x402", input);
387
+ if ("success" in pf) return pf;
388
+ const { args, warnings } = pf;
373
389
  try {
374
- return { success: true, data: await haven.quoteX402(args.url, requestInit(args), { idempotencyKey: args.idempotencyKey }) };
390
+ const data = await haven.quoteX402(args.url, requestInit(args), { idempotencyKey: args.idempotencyKey });
391
+ return warnings.length > 0 ? { success: true, data, warnings } : { success: true, data };
375
392
  } catch (err) {
376
393
  return normalizeError(err);
377
394
  }
378
395
  },
379
396
  haven_pay_x402_quote: async (input) => {
380
- const args = objectInput("haven_pay_x402_quote", input);
397
+ const pf = preflight("haven_pay_x402_quote", input);
398
+ if ("success" in pf) return pf;
399
+ const { args, warnings } = pf;
381
400
  const quote = args.quote;
382
401
  if (!quote || typeof quote !== "object") {
383
402
  return wrongTool(
@@ -396,14 +415,16 @@ function createToolHandlers(haven) {
396
415
  return runTool(async () => {
397
416
  const response = await haven.payX402Quote(args.quote, { idempotencyKey: args.idempotencyKey });
398
417
  return responsePayload(response);
399
- });
418
+ }, warnings);
400
419
  },
401
420
  haven_pay_x402: async (input) => {
402
- const args = objectInput("haven_pay_x402", input);
421
+ const pf = preflight("haven_pay_x402", input);
422
+ if ("success" in pf) return pf;
423
+ const { args, warnings } = pf;
403
424
  return runTool(async () => {
404
425
  const response = await haven.fetch(args.url, requestInit(args), { idempotencyKey: args.idempotencyKey });
405
426
  return responsePayload(response);
406
- });
427
+ }, warnings);
407
428
  },
408
429
  haven_resume_x402_payment: async (input) => {
409
430
  const args = objectInput("haven_resume_x402_payment", input);
@@ -501,6 +522,39 @@ function isPendingApproval(status) {
501
522
  function wrongTool(code, message, suggested_tool) {
502
523
  return { success: false, code, message, suggested_tool };
503
524
  }
525
+ function preflight(name, input) {
526
+ let args;
527
+ try {
528
+ args = objectInput(name, input);
529
+ } catch (err) {
530
+ return normalizeError(err);
531
+ }
532
+ const idem = resolveIdempotencyKey(args);
533
+ if ("success" in idem) return idem;
534
+ if (idem.key !== void 0) args = { ...args, idempotencyKey: idem.key };
535
+ return { args, warnings: idem.warnings };
536
+ }
537
+ function resolveIdempotencyKey(args) {
538
+ const modern = typeof args.idempotency_key === "string" ? args.idempotency_key : void 0;
539
+ const legacy = typeof args.idempotencyKey === "string" ? args.idempotencyKey : void 0;
540
+ if (modern !== void 0 && legacy !== void 0 && modern !== legacy) {
541
+ return {
542
+ success: false,
543
+ code: "AMBIGUOUS_IDEMPOTENCY_KEY",
544
+ message: "Both idempotency_key and idempotencyKey were sent with different values. Nothing was contacted or spent. Send exactly one \u2014 idempotency_key is the current spelling; idempotencyKey is deprecated and will be removed."
545
+ };
546
+ }
547
+ if (modern !== void 0) return { key: modern, warnings: [] };
548
+ if (legacy !== void 0) {
549
+ return {
550
+ key: legacy,
551
+ warnings: [
552
+ "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."
553
+ ]
554
+ };
555
+ }
556
+ return { warnings: [] };
557
+ }
504
558
  function buildMcpToolsCallEnvelope(toolName, args) {
505
559
  return {
506
560
  jsonrpc: "2.0",
@@ -523,9 +577,10 @@ function requestInit(input) {
523
577
  body: input.body
524
578
  };
525
579
  }
526
- async function runTool(fn) {
580
+ async function runTool(fn, warnings = []) {
527
581
  try {
528
- return { success: true, data: await fn() };
582
+ const data = await fn();
583
+ return warnings.length > 0 ? { success: true, data, warnings } : { success: true, data };
529
584
  } catch (err) {
530
585
  return normalizeError(err);
531
586
  }
@@ -796,7 +851,7 @@ async function resolveHavenClient(options = {}) {
796
851
  return { client, credentials };
797
852
  }
798
853
  var MCP_NAME = "@haven_ai/mcp";
799
- var MCP_VERSION = "0.0.0-dev.202609061037.7cf43bb";
854
+ var MCP_VERSION = "0.0.0-dev.202609061923.f3d53db";
800
855
  var MCP_INSTRUCTIONS = [
801
856
  "Haven local MCP server: signs in-process with the delegate key it holds on",
802
857
  "this machine \u2014 the key never leaves this process. Call haven_get_agent",