@dcentralab/d402-client 0.3.4 → 0.3.7

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/index.js CHANGED
@@ -299,12 +299,12 @@ __export(signer_exports, {
299
299
  async function signD402Payment(params) {
300
300
  const {
301
301
  operatorAccount,
302
+ walletClient,
302
303
  paymentRequirement,
303
304
  iatpWalletAddress,
304
305
  requestPath,
305
306
  d402Version = 1
306
307
  } = params;
307
- const consumerWallet = iatpWalletAddress || operatorAccount.address;
308
308
  let finalRequestPath = requestPath || paymentRequirement.resource || "/mcp";
309
309
  if (!finalRequestPath || finalRequestPath.trim() === "") {
310
310
  finalRequestPath = "/mcp";
@@ -321,27 +321,39 @@ async function signD402Payment(params) {
321
321
  name: walletName,
322
322
  version: walletVersion,
323
323
  chainId,
324
- verifyingContract: consumerWallet
324
+ verifyingContract: iatpWalletAddress
325
325
  };
326
326
  const message = {
327
- wallet: consumerWallet,
327
+ wallet: iatpWalletAddress,
328
328
  provider: paymentRequirement.payTo,
329
329
  token: paymentRequirement.asset,
330
330
  amount: BigInt(paymentRequirement.maxAmountRequired),
331
331
  deadline: BigInt(validBefore),
332
332
  requestPath: finalRequestPath
333
333
  };
334
- if (!operatorAccount.signTypedData) {
335
- throw new Error("Account does not support signTypedData");
334
+ let signature;
335
+ if (walletClient) {
336
+ signature = await walletClient.signTypedData({
337
+ account: operatorAccount,
338
+ domain,
339
+ types: {
340
+ PullFundsForSettlement: EIP712_TYPES.PullFundsForSettlement
341
+ },
342
+ primaryType: "PullFundsForSettlement",
343
+ message
344
+ });
345
+ } else if (operatorAccount.signTypedData) {
346
+ signature = await operatorAccount.signTypedData({
347
+ domain,
348
+ types: {
349
+ PullFundsForSettlement: EIP712_TYPES.PullFundsForSettlement
350
+ },
351
+ primaryType: "PullFundsForSettlement",
352
+ message
353
+ });
354
+ } else {
355
+ throw new Error("Account does not support signTypedData. Pass walletClient for wagmi support.");
336
356
  }
337
- const signature = await operatorAccount.signTypedData({
338
- domain,
339
- types: {
340
- PullFundsForSettlement: EIP712_TYPES.PullFundsForSettlement
341
- },
342
- primaryType: "PullFundsForSettlement",
343
- message
344
- });
345
357
  const signedPayment = {
346
358
  d402Version,
347
359
  scheme: paymentRequirement.scheme,
@@ -349,7 +361,7 @@ async function signD402Payment(params) {
349
361
  payload: {
350
362
  signature,
351
363
  authorization: {
352
- from: consumerWallet,
364
+ from: iatpWalletAddress,
353
365
  to: paymentRequirement.payTo,
354
366
  value: paymentRequirement.maxAmountRequired,
355
367
  validAfter,
@@ -408,6 +420,131 @@ var init_encoder = __esm({
408
420
  }
409
421
  });
410
422
 
423
+ // src/mcp/session.ts
424
+ var session_exports = {};
425
+ __export(session_exports, {
426
+ DEFAULT_JSONRPC_VERSION: () => DEFAULT_JSONRPC_VERSION,
427
+ DEFAULT_REQUEST_ID: () => DEFAULT_REQUEST_ID,
428
+ initMcpSession: () => initMcpSession
429
+ });
430
+ async function initMcpSession(endpoint) {
431
+ const response = await fetch(endpoint, {
432
+ method: "POST",
433
+ headers: {
434
+ "Content-Type": "application/json",
435
+ "Accept": "application/json, text/event-stream"
436
+ },
437
+ body: JSON.stringify({
438
+ jsonrpc: DEFAULT_JSONRPC_VERSION,
439
+ id: DEFAULT_REQUEST_ID,
440
+ method: "initialize",
441
+ params: {
442
+ protocolVersion: "2024-11-05",
443
+ capabilities: {},
444
+ clientInfo: { name: "d402-client", version: "1.0" }
445
+ }
446
+ })
447
+ });
448
+ if (!response.ok) {
449
+ throw new Error(`MCP init failed: ${response.status} ${response.statusText}`);
450
+ }
451
+ return response.headers.get("mcp-session-id");
452
+ }
453
+ var DEFAULT_REQUEST_ID, DEFAULT_JSONRPC_VERSION;
454
+ var init_session = __esm({
455
+ "src/mcp/session.ts"() {
456
+ DEFAULT_REQUEST_ID = "init-1";
457
+ DEFAULT_JSONRPC_VERSION = "2.0";
458
+ }
459
+ });
460
+
461
+ // src/mcp/payload.ts
462
+ var payload_exports = {};
463
+ __export(payload_exports, {
464
+ buildMcpHeaders: () => buildMcpHeaders,
465
+ buildToolCallPayload: () => buildToolCallPayload
466
+ });
467
+ function buildToolCallPayload(toolName, args, requestId = DEFAULT_REQUEST_ID) {
468
+ return {
469
+ jsonrpc: DEFAULT_JSONRPC_VERSION,
470
+ id: requestId,
471
+ method: "tools/call",
472
+ params: {
473
+ name: toolName,
474
+ arguments: args
475
+ }
476
+ };
477
+ }
478
+ function buildMcpHeaders(sessionId) {
479
+ const headers = {
480
+ "Content-Type": "application/json",
481
+ "Accept": "application/json, text/event-stream"
482
+ };
483
+ if (sessionId) {
484
+ headers["mcp-session-id"] = sessionId;
485
+ }
486
+ return headers;
487
+ }
488
+ var init_payload = __esm({
489
+ "src/mcp/payload.ts"() {
490
+ init_session();
491
+ }
492
+ });
493
+
494
+ // src/mcp/response.ts
495
+ var response_exports = {};
496
+ __export(response_exports, {
497
+ extractToolResult: () => extractToolResult,
498
+ parseMcpResponse: () => parseMcpResponse
499
+ });
500
+ async function parseMcpResponse(response) {
501
+ const contentType = response.headers.get("content-type") || "";
502
+ if (contentType.includes("text/event-stream")) {
503
+ return parseSSEResponse(await response.text());
504
+ } else {
505
+ return await response.json();
506
+ }
507
+ }
508
+ function parseSSEResponse(text) {
509
+ const lines = text.split("\n");
510
+ for (const line of lines) {
511
+ if (line.startsWith("data: ")) {
512
+ const jsonStr = line.substring(6);
513
+ try {
514
+ return JSON.parse(jsonStr);
515
+ } catch {
516
+ continue;
517
+ }
518
+ }
519
+ }
520
+ return {
521
+ id: "unknown",
522
+ result: { raw: text },
523
+ error: {
524
+ code: -32700,
525
+ message: "Could not parse SSE response",
526
+ data: text
527
+ }
528
+ };
529
+ }
530
+ function extractToolResult(response) {
531
+ if (response.error) {
532
+ throw new Error(`MCP error ${response.error.code}: ${response.error.message}`);
533
+ }
534
+ if (response.result && typeof response.result === "object") {
535
+ const result = response.result;
536
+ if (result.structuredContent) {
537
+ return result.structuredContent;
538
+ }
539
+ return result;
540
+ }
541
+ return response.result;
542
+ }
543
+ var init_response = __esm({
544
+ "src/mcp/response.ts"() {
545
+ }
546
+ });
547
+
411
548
  // src/payment/selector.ts
412
549
  init_errors();
413
550
  function selectPaymentRequirement(requirements, options = {}) {
@@ -5041,7 +5178,7 @@ function getContractConfig(contractName, network = "sepolia") {
5041
5178
 
5042
5179
  // src/wallet/creation.ts
5043
5180
  async function createIATPWallet(params) {
5044
- const { ownerAccount, network = "sepolia", rpcUrl } = params;
5181
+ const { ownerAccount, walletClient: externalWalletClient, network = "sepolia", rpcUrl } = params;
5045
5182
  const factoryConfig = getContractConfig("IATPWalletFactory" /* IATP_WALLET_FACTORY */, network);
5046
5183
  if (!factoryConfig) {
5047
5184
  throw new Error(`IATPWalletFactory not found for network: ${network}`);
@@ -5053,7 +5190,7 @@ async function createIATPWallet(params) {
5053
5190
  chain,
5054
5191
  transport
5055
5192
  });
5056
- const walletClient = viem.createWalletClient({
5193
+ const walletClient = externalWalletClient ?? viem.createWalletClient({
5057
5194
  account: ownerAccount,
5058
5195
  chain,
5059
5196
  transport
@@ -5062,14 +5199,14 @@ async function createIATPWallet(params) {
5062
5199
  if (balance === 0n) {
5063
5200
  throw new Error("Owner has no ETH for gas. Please fund the account.");
5064
5201
  }
5065
- const { request } = await publicClient.simulateContract({
5202
+ const hash = await walletClient.writeContract({
5066
5203
  account: ownerAccount,
5067
5204
  address: factoryConfig.address,
5068
5205
  abi: factoryConfig.abi,
5069
5206
  functionName: "createWallet",
5070
- args: [operatorAddress, 0, "", ""]
5207
+ args: [operatorAddress, 0, "", ""],
5208
+ chain
5071
5209
  });
5072
- const hash = await walletClient.writeContract(request);
5073
5210
  const receipt = await publicClient.waitForTransactionReceipt({
5074
5211
  hash,
5075
5212
  timeout: 3e5
@@ -5247,6 +5384,7 @@ var D402Client = class {
5247
5384
  */
5248
5385
  constructor(config) {
5249
5386
  this.operatorAccount = config.operatorAccount;
5387
+ this.walletClient = config.walletClient;
5250
5388
  this.iatpWalletAddress = config.iatpWalletAddress;
5251
5389
  this.maxValue = config.maxValue;
5252
5390
  this.networkFilter = config.networkFilter;
@@ -5390,7 +5528,15 @@ var D402Client = class {
5390
5528
  const { parseAllPaymentRequirements: parseAllPaymentRequirements2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
5391
5529
  const { signD402Payment: signD402Payment2 } = await Promise.resolve().then(() => (init_signer(), signer_exports));
5392
5530
  const { encodePayment: encodePayment2 } = await Promise.resolve().then(() => (init_encoder(), encoder_exports));
5393
- let response = await fetch(url, init);
5531
+ const enhancedInit = {
5532
+ ...init,
5533
+ headers: {
5534
+ ...init?.headers,
5535
+ // Preserve existing Accept header or set default for MCP compatibility
5536
+ "Accept": init?.headers?.["Accept"] || "application/json, text/event-stream"
5537
+ }
5538
+ };
5539
+ let response = await fetch(url, enhancedInit);
5394
5540
  if (response.status !== 402) {
5395
5541
  return response;
5396
5542
  }
@@ -5399,14 +5545,15 @@ var D402Client = class {
5399
5545
  const selectedRequirement = this.selectPaymentRequirement(requirements);
5400
5546
  const signedPayment = await signD402Payment2({
5401
5547
  operatorAccount: this.operatorAccount,
5548
+ walletClient: this.walletClient,
5402
5549
  paymentRequirement: selectedRequirement,
5403
5550
  iatpWalletAddress: this.iatpWalletAddress
5404
5551
  });
5405
5552
  const paymentHeader = encodePayment2(signedPayment);
5406
5553
  response = await fetch(url, {
5407
- ...init,
5554
+ ...enhancedInit,
5408
5555
  headers: {
5409
- ...init?.headers,
5556
+ ...enhancedInit.headers,
5410
5557
  "X-Payment": paymentHeader,
5411
5558
  "Access-Control-Expose-Headers": "X-Payment-Response"
5412
5559
  }
@@ -5416,6 +5563,63 @@ var D402Client = class {
5416
5563
  throw error;
5417
5564
  }
5418
5565
  }
5566
+ // ========================================================================
5567
+ // MCP Methods - Simplified API for MCP tool calls
5568
+ // ========================================================================
5569
+ /**
5570
+ * Call an MCP tool with automatic session initialization and payment handling.
5571
+ *
5572
+ * This is the simplest way to call an MCP tool. It handles:
5573
+ * - MCP session initialization
5574
+ * - JSON-RPC payload construction
5575
+ * - HTTP 402 payment flow
5576
+ * - SSE/JSON response parsing
5577
+ *
5578
+ * @param endpoint - MCP server endpoint URL
5579
+ * @param toolName - Name of the tool to call
5580
+ * @param input - Tool arguments
5581
+ * @param options - Optional configuration
5582
+ * @returns The tool's result data
5583
+ *
5584
+ * @example
5585
+ * ```ts
5586
+ * const client = new D402Client({
5587
+ * operatorAccount: walletClient.account,
5588
+ * walletClient,
5589
+ * iatpWalletAddress,
5590
+ * })
5591
+ *
5592
+ * // Simple one-liner!
5593
+ * const result = await client.mcpCall(
5594
+ * 'https://my-mcp.example.com/mcp',
5595
+ * 'google_search',
5596
+ * { q: 'test search' }
5597
+ * )
5598
+ *
5599
+ * console.log(result) // { searchResults: [...] }
5600
+ * ```
5601
+ */
5602
+ async mcpCall(endpoint, toolName, input, options) {
5603
+ const { initMcpSession: initMcpSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
5604
+ const { buildToolCallPayload: buildToolCallPayload2, buildMcpHeaders: buildMcpHeaders2 } = await Promise.resolve().then(() => (init_payload(), payload_exports));
5605
+ const { parseMcpResponse: parseMcpResponse2, extractToolResult: extractToolResult2 } = await Promise.resolve().then(() => (init_response(), response_exports));
5606
+ let sessionId = null;
5607
+ if (!options?.skipInit) {
5608
+ try {
5609
+ sessionId = await initMcpSession2(endpoint);
5610
+ } catch {
5611
+ }
5612
+ }
5613
+ const headers = buildMcpHeaders2(sessionId);
5614
+ const payload = buildToolCallPayload2(toolName, input, options?.requestId);
5615
+ const response = await this.fetch(endpoint, {
5616
+ method: "POST",
5617
+ headers,
5618
+ body: JSON.stringify(payload)
5619
+ });
5620
+ const parsed = await parseMcpResponse2(response);
5621
+ return extractToolResult2(parsed);
5622
+ }
5419
5623
  };
5420
5624
 
5421
5625
  // src/index.ts
@@ -5423,6 +5627,11 @@ init_signer();
5423
5627
  init_parser();
5424
5628
  init_encoder();
5425
5629
 
5630
+ // src/mcp/index.ts
5631
+ init_session();
5632
+ init_payload();
5633
+ init_response();
5634
+
5426
5635
  // src/settlement/queries.ts
5427
5636
  async function getLockedBalanceForProvider(params) {
5428
5637
  const { publicClient, providerAddress, tokenAddress, network = "sepolia" } = params;
@@ -5497,12 +5706,15 @@ init_errors();
5497
5706
 
5498
5707
  exports.ContractName = ContractName;
5499
5708
  exports.D402Client = D402Client;
5709
+ exports.buildMcpHeaders = buildMcpHeaders;
5710
+ exports.buildToolCallPayload = buildToolCallPayload;
5500
5711
  exports.createIATPWallet = createIATPWallet;
5501
5712
  exports.createPaymentSelector = createPaymentSelector;
5502
5713
  exports.decodePayment = decodePayment;
5503
5714
  exports.decodePaymentResponse = decodePaymentResponse;
5504
5715
  exports.encodePayment = encodePayment;
5505
5716
  exports.executeWithdrawal = executeWithdrawal;
5717
+ exports.extractToolResult = extractToolResult;
5506
5718
  exports.findMatchingPaymentRequirement = findMatchingPaymentRequirement;
5507
5719
  exports.formatMoney = formatMoney;
5508
5720
  exports.generateNonce = generateNonce;
@@ -5517,9 +5729,11 @@ exports.getUnlockedBalanceForProvider = getUnlockedBalanceForProvider;
5517
5729
  exports.getUsdcAddress = getUsdcAddress;
5518
5730
  exports.getWalletsByOwner = getWalletsByOwner;
5519
5731
  exports.getWithdrawalRequest = getWithdrawalRequest;
5732
+ exports.initMcpSession = initMcpSession;
5520
5733
  exports.isValidAddress = isValidAddress;
5521
5734
  exports.normalizeAddress = normalizeAddress;
5522
5735
  exports.parseAllPaymentRequirements = parseAllPaymentRequirements;
5736
+ exports.parseMcpResponse = parseMcpResponse;
5523
5737
  exports.parseMoney = parseMoney;
5524
5738
  exports.parsePaymentRequirement = parsePaymentRequirement;
5525
5739
  exports.requestWithdrawal = requestWithdrawal;