@haven_ai/mcp 0.1.7-alpha → 0.1.10-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/dist/cli.cjs +209 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +210 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +209 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +210 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { composeDescription, toolDescriptions as toolDescriptions$1, HavenClient, HavenPaymentStateError, HavenSigningError,
|
|
4
|
+
import { composeDescription, toolDescriptions as toolDescriptions$1, HavenClient, HavenApiError, HavenPaymentStateError, HavenSigningError, AgentPaymentNextAction, HavenError } from '@haven_ai/sdk';
|
|
5
5
|
import { readFile, mkdir, writeFile, stat } from 'fs/promises';
|
|
6
6
|
import { z } from 'zod/v3';
|
|
7
7
|
import { createHash } from 'crypto';
|
|
@@ -216,6 +216,18 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
216
216
|
}
|
|
217
217
|
var headersSchema = z.record(z.string(), z.string()).optional();
|
|
218
218
|
var toolSchemas = {
|
|
219
|
+
haven_send: {
|
|
220
|
+
asset: z.enum(["ETH", "USDC"]),
|
|
221
|
+
recipient: z.string().min(1),
|
|
222
|
+
amount: z.string().min(1),
|
|
223
|
+
idempotencyKey: z.string().optional()
|
|
224
|
+
},
|
|
225
|
+
haven_pay_mcp_tool: {
|
|
226
|
+
merchant_url: z.string().url(),
|
|
227
|
+
tool_name: z.string().min(1),
|
|
228
|
+
arguments: z.record(z.string(), z.unknown()).optional(),
|
|
229
|
+
idempotencyKey: z.string().optional()
|
|
230
|
+
},
|
|
219
231
|
haven_quote_x402: {
|
|
220
232
|
url: z.string().url(),
|
|
221
233
|
method: z.string().optional(),
|
|
@@ -262,11 +274,18 @@ var toolSchemas = {
|
|
|
262
274
|
},
|
|
263
275
|
haven_get_agent: {},
|
|
264
276
|
haven_get_allowances: {},
|
|
277
|
+
haven_sweep_delegate: {},
|
|
278
|
+
haven_discover_tools: {
|
|
279
|
+
category: z.string().optional(),
|
|
280
|
+
rail: z.enum(["x402", "mpp"]).optional()
|
|
281
|
+
},
|
|
265
282
|
haven_list_receipts: {
|
|
266
283
|
limit: z.number().int().min(1).max(100).optional()
|
|
267
284
|
}
|
|
268
285
|
};
|
|
269
286
|
var toolDescriptions = {
|
|
287
|
+
haven_send: composeDescription(toolDescriptions$1.send),
|
|
288
|
+
haven_pay_mcp_tool: composeDescription(toolDescriptions$1.payMcpTool),
|
|
270
289
|
haven_quote_x402: composeDescription(toolDescriptions$1.quoteX402),
|
|
271
290
|
haven_pay_x402_quote: composeDescription(toolDescriptions$1.payX402),
|
|
272
291
|
haven_pay_x402: composeDescription(toolDescriptions$1.payX402OneShot),
|
|
@@ -278,16 +297,98 @@ var toolDescriptions = {
|
|
|
278
297
|
haven_get_resume_state: composeDescription(toolDescriptions$1.getResumeState),
|
|
279
298
|
haven_get_agent: composeDescription(toolDescriptions$1.getAgent),
|
|
280
299
|
haven_get_allowances: composeDescription(toolDescriptions$1.getAllowances),
|
|
300
|
+
haven_sweep_delegate: composeDescription(toolDescriptions$1.sweep_delegate),
|
|
301
|
+
haven_discover_tools: composeDescription(toolDescriptions$1.discoverTools),
|
|
281
302
|
haven_list_receipts: composeDescription(toolDescriptions$1.listReceipts)
|
|
282
303
|
};
|
|
283
304
|
function createToolHandlers(haven) {
|
|
284
305
|
return {
|
|
306
|
+
haven_send: async (input) => {
|
|
307
|
+
return runTool(async () => {
|
|
308
|
+
const args = objectInput("haven_send", input);
|
|
309
|
+
try {
|
|
310
|
+
const result = await haven.pay({
|
|
311
|
+
token: args.asset,
|
|
312
|
+
amount: args.amount,
|
|
313
|
+
to: args.recipient
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
payment_id: result.paymentId,
|
|
317
|
+
status: result.status,
|
|
318
|
+
tx_hash: result.txHash ?? null,
|
|
319
|
+
asset: args.asset,
|
|
320
|
+
amount: args.amount,
|
|
321
|
+
recipient: args.recipient
|
|
322
|
+
};
|
|
323
|
+
} catch (err) {
|
|
324
|
+
if (err instanceof HavenPaymentStateError && isPendingApproval(err.status)) {
|
|
325
|
+
return {
|
|
326
|
+
payment_id: err.paymentId,
|
|
327
|
+
status: "pending_approval",
|
|
328
|
+
asset: args.asset,
|
|
329
|
+
amount: args.amount,
|
|
330
|
+
recipient: args.recipient
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
},
|
|
337
|
+
haven_pay_mcp_tool: async (input) => {
|
|
338
|
+
return runTool(async () => {
|
|
339
|
+
const args = objectInput("haven_pay_mcp_tool", input);
|
|
340
|
+
const envelope = buildMcpToolsCallEnvelope(args.tool_name, args.arguments);
|
|
341
|
+
const response = await haven.fetch(
|
|
342
|
+
args.merchant_url,
|
|
343
|
+
{
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: { "Content-Type": "application/json" },
|
|
346
|
+
body: JSON.stringify(envelope)
|
|
347
|
+
},
|
|
348
|
+
{ idempotencyKey: args.idempotencyKey }
|
|
349
|
+
);
|
|
350
|
+
return responsePayload(response);
|
|
351
|
+
});
|
|
352
|
+
},
|
|
285
353
|
haven_quote_x402: async (input) => {
|
|
286
354
|
const args = objectInput("haven_quote_x402", input);
|
|
287
|
-
|
|
355
|
+
try {
|
|
356
|
+
return { success: true, data: await haven.quoteX402(args.url, requestInit(args), { idempotencyKey: args.idempotencyKey }) };
|
|
357
|
+
} catch (err) {
|
|
358
|
+
if (err instanceof HavenApiError && err.message.includes("quoteX402 only supports standard x402")) {
|
|
359
|
+
return wrongTool(
|
|
360
|
+
"WRONG_RAIL",
|
|
361
|
+
"The URL responds with an MPP machine-payment challenge, not an x402 payment. Use haven_quote_mpp to inspect this merchant.",
|
|
362
|
+
"haven_quote_mpp"
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
return normalizeError(err);
|
|
366
|
+
}
|
|
288
367
|
},
|
|
289
368
|
haven_pay_x402_quote: async (input) => {
|
|
290
369
|
const args = objectInput("haven_pay_x402_quote", input);
|
|
370
|
+
const quote = args.quote;
|
|
371
|
+
if (!quote || typeof quote !== "object") {
|
|
372
|
+
return wrongTool(
|
|
373
|
+
"WRONG_TOOL",
|
|
374
|
+
"The quote argument is missing or is not a valid x402 quote object. Call haven_quote_x402 first to obtain a quote, or use haven_pay_x402 to handle the full probe \u2192 pay \u2192 retry round trip automatically.",
|
|
375
|
+
"haven_quote_x402"
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
if (!quote.paymentRequired && quote.rail === "mpp") {
|
|
379
|
+
return wrongTool(
|
|
380
|
+
"WRONG_TOOL",
|
|
381
|
+
"The quote is for the MPP rail. Use haven_pay_mpp_challenge to pay an MPP quote.",
|
|
382
|
+
"haven_pay_mpp_challenge"
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (!quote.paymentRequired) {
|
|
386
|
+
return wrongTool(
|
|
387
|
+
"WRONG_TOOL",
|
|
388
|
+
"The quote is missing the required paymentRequired field. Call haven_quote_x402 first to obtain a valid x402 quote.",
|
|
389
|
+
"haven_quote_x402"
|
|
390
|
+
);
|
|
391
|
+
}
|
|
291
392
|
return runTool(async () => {
|
|
292
393
|
const response = await haven.payX402Quote(args.quote, { idempotencyKey: args.idempotencyKey });
|
|
293
394
|
return responsePayload(response);
|
|
@@ -302,6 +403,16 @@ function createToolHandlers(haven) {
|
|
|
302
403
|
},
|
|
303
404
|
haven_resume_x402_payment: async (input) => {
|
|
304
405
|
const args = objectInput("haven_resume_x402_payment", input);
|
|
406
|
+
if (args.resume_state && typeof args.resume_state === "object") {
|
|
407
|
+
const stateRail = args.resume_state.rail;
|
|
408
|
+
if (stateRail && stateRail !== "x402") {
|
|
409
|
+
return wrongTool(
|
|
410
|
+
"WRONG_TOOL",
|
|
411
|
+
`The resume state is for the '${stateRail}' rail, not x402. Use haven_resume_mpp_payment instead.`,
|
|
412
|
+
"haven_resume_mpp_payment"
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
305
416
|
return runTool(async () => {
|
|
306
417
|
const state = await resumeState(args, "x402");
|
|
307
418
|
const response = await haven.resumeX402Payment(state);
|
|
@@ -310,20 +421,54 @@ function createToolHandlers(haven) {
|
|
|
310
421
|
},
|
|
311
422
|
haven_quote_mpp: async (input) => {
|
|
312
423
|
const args = objectInput("haven_quote_mpp", input);
|
|
313
|
-
|
|
424
|
+
try {
|
|
314
425
|
if (args.challenge) {
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
|
|
426
|
+
return {
|
|
427
|
+
success: true,
|
|
428
|
+
data: await haven.quoteMpp(args.challenge, requestInit(args), {
|
|
429
|
+
idempotencyKey: args.idempotencyKey
|
|
430
|
+
})
|
|
431
|
+
};
|
|
318
432
|
}
|
|
319
433
|
if (!args.url) {
|
|
320
|
-
|
|
434
|
+
return normalizeError(new HavenApiError("haven_quote_mpp requires either url or challenge.", 400));
|
|
321
435
|
}
|
|
322
|
-
return haven.quoteMpp(args.url, requestInit(args), { idempotencyKey: args.idempotencyKey });
|
|
323
|
-
})
|
|
436
|
+
return { success: true, data: await haven.quoteMpp(args.url, requestInit(args), { idempotencyKey: args.idempotencyKey }) };
|
|
437
|
+
} catch (err) {
|
|
438
|
+
if (err instanceof Error && err.message.includes("No MACHINE-PAYMENT-CHALLENGE header found")) {
|
|
439
|
+
return wrongTool(
|
|
440
|
+
"WRONG_RAIL",
|
|
441
|
+
"The URL responds with an x402 payment requirement, not an MPP machine-payment challenge. Use haven_quote_x402 to inspect this merchant.",
|
|
442
|
+
"haven_quote_x402"
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
return normalizeError(err);
|
|
446
|
+
}
|
|
324
447
|
},
|
|
325
448
|
haven_pay_mpp_challenge: async (input) => {
|
|
326
449
|
const args = objectInput("haven_pay_mpp_challenge", input);
|
|
450
|
+
const quote = args.quote;
|
|
451
|
+
if (!quote || typeof quote !== "object") {
|
|
452
|
+
return wrongTool(
|
|
453
|
+
"WRONG_TOOL",
|
|
454
|
+
"The quote argument is missing or is not a valid MPP quote object. Call haven_quote_mpp first to obtain a quote.",
|
|
455
|
+
"haven_quote_mpp"
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
if (quote.paymentRequired || quote.rail === "x402") {
|
|
459
|
+
return wrongTool(
|
|
460
|
+
"WRONG_TOOL",
|
|
461
|
+
"The quote is for the x402 rail. Use haven_pay_x402_quote to pay an x402 quote.",
|
|
462
|
+
"haven_pay_x402_quote"
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
if (!quote.challenge) {
|
|
466
|
+
return wrongTool(
|
|
467
|
+
"WRONG_TOOL",
|
|
468
|
+
"The quote is missing the required challenge field. Call haven_quote_mpp first to obtain a valid MPP quote.",
|
|
469
|
+
"haven_quote_mpp"
|
|
470
|
+
);
|
|
471
|
+
}
|
|
327
472
|
return runTool(async () => {
|
|
328
473
|
const response = await haven.payMppChallenge(args.quote, { idempotencyKey: args.idempotencyKey });
|
|
329
474
|
return responsePayload(response);
|
|
@@ -331,6 +476,16 @@ function createToolHandlers(haven) {
|
|
|
331
476
|
},
|
|
332
477
|
haven_resume_mpp_payment: async (input) => {
|
|
333
478
|
const args = objectInput("haven_resume_mpp_payment", input);
|
|
479
|
+
if (args.resume_state && typeof args.resume_state === "object") {
|
|
480
|
+
const stateRail = args.resume_state.rail;
|
|
481
|
+
if (stateRail && stateRail !== "mpp") {
|
|
482
|
+
return wrongTool(
|
|
483
|
+
"WRONG_TOOL",
|
|
484
|
+
`The resume state is for the '${stateRail}' rail, not mpp. Use haven_resume_x402_payment instead.`,
|
|
485
|
+
"haven_resume_x402_payment"
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
334
489
|
return runTool(async () => {
|
|
335
490
|
const state = await resumeState(args, "mpp");
|
|
336
491
|
const response = await haven.resumeMppPayment(state);
|
|
@@ -347,6 +502,34 @@ function createToolHandlers(haven) {
|
|
|
347
502
|
},
|
|
348
503
|
haven_get_agent: async () => runTool(async () => haven.getAgent()),
|
|
349
504
|
haven_get_allowances: async () => runTool(async () => haven.getAllowances()),
|
|
505
|
+
haven_sweep_delegate: async () => runTool(async () => haven.sweepDelegate()),
|
|
506
|
+
haven_discover_tools: async (input) => {
|
|
507
|
+
const args = objectInput("haven_discover_tools", input);
|
|
508
|
+
return runTool(async () => {
|
|
509
|
+
const entries = await haven.discoverTools({
|
|
510
|
+
category: typeof args.category === "string" ? args.category : void 0,
|
|
511
|
+
rail: args.rail === "x402" || args.rail === "mpp" ? args.rail : void 0
|
|
512
|
+
});
|
|
513
|
+
return entries.map((entry) => ({
|
|
514
|
+
id: entry.id,
|
|
515
|
+
name: entry.name,
|
|
516
|
+
description: entry.description,
|
|
517
|
+
category: entry.category,
|
|
518
|
+
resource_url: entry.resourceUrl,
|
|
519
|
+
rail: entry.rail,
|
|
520
|
+
protocol: entry.protocol,
|
|
521
|
+
tool_name: entry.toolName,
|
|
522
|
+
price_display: entry.priceDisplay,
|
|
523
|
+
price_atomic: entry.priceAtomic,
|
|
524
|
+
asset: entry.asset,
|
|
525
|
+
network: entry.network,
|
|
526
|
+
status: entry.status,
|
|
527
|
+
verified_at: entry.verifiedAt,
|
|
528
|
+
// Which Haven pay tool reaches this entry from the local MCP surface.
|
|
529
|
+
suggested_tool: entry.protocol === "mcp" ? "haven_pay_mcp_tool" : entry.rail === "x402" ? "haven_pay_x402" : "haven_quote_mpp"
|
|
530
|
+
}));
|
|
531
|
+
});
|
|
532
|
+
},
|
|
350
533
|
haven_list_receipts: async (input) => {
|
|
351
534
|
const args = objectInput("haven_list_receipts", input);
|
|
352
535
|
return runTool(async () => haven.listReceipts({ limit: args.limit }));
|
|
@@ -363,6 +546,23 @@ function createToolHandlers(haven) {
|
|
|
363
546
|
return state;
|
|
364
547
|
}
|
|
365
548
|
}
|
|
549
|
+
function isPendingApproval(status) {
|
|
550
|
+
return status === "pending" || status === "pending_approval";
|
|
551
|
+
}
|
|
552
|
+
function wrongTool(code, message, suggested_tool) {
|
|
553
|
+
return { success: false, code, message, suggested_tool };
|
|
554
|
+
}
|
|
555
|
+
function buildMcpToolsCallEnvelope(toolName, args) {
|
|
556
|
+
return {
|
|
557
|
+
jsonrpc: "2.0",
|
|
558
|
+
id: `haven-mcp-${Date.now()}`,
|
|
559
|
+
method: "tools/call",
|
|
560
|
+
params: {
|
|
561
|
+
name: toolName,
|
|
562
|
+
arguments: args ?? {}
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
}
|
|
366
566
|
function objectInput(name, input) {
|
|
367
567
|
return z.object(toolSchemas[name]).parse(input ?? {});
|
|
368
568
|
}
|
|
@@ -638,7 +838,7 @@ async function resolveHavenClient(options = {}) {
|
|
|
638
838
|
return { client, credentials };
|
|
639
839
|
}
|
|
640
840
|
var MCP_NAME = "@haven_ai/mcp";
|
|
641
|
-
var MCP_VERSION = "0.1.
|
|
841
|
+
var MCP_VERSION = "0.1.10-alpha.0";
|
|
642
842
|
function buildMcpServer(haven) {
|
|
643
843
|
const server = new McpServer({
|
|
644
844
|
name: MCP_NAME,
|