@aura-payments/mcp-server 1.0.1 → 1.1.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/index.mjs CHANGED
@@ -3,13 +3,58 @@ import { AuraClient, AuraAuthenticationError, AuraNotFoundError, AuraValidationE
3
3
  import { z } from 'zod';
4
4
 
5
5
  // src/index.ts
6
+
7
+ // src/api-key.ts
8
+ var API_KEY_PREFIXES = [
9
+ "sk_test_",
10
+ "sk_live_",
11
+ "pk_test_",
12
+ "pk_live_"
13
+ ];
14
+ function maskApiKey(apiKey) {
15
+ const revealed = Math.min(8, Math.floor(apiKey.length / 2));
16
+ return `${apiKey.slice(0, revealed)}\u2026(${apiKey.length} chars)`;
17
+ }
18
+ function parseApiKey(apiKey) {
19
+ if (!apiKey || apiKey.trim() === "") {
20
+ throw new MissingAuraApiKeyError();
21
+ }
22
+ const prefix = API_KEY_PREFIXES.find((p) => apiKey.startsWith(p));
23
+ if (!prefix) {
24
+ throw new MalformedAuraApiKeyError(apiKey);
25
+ }
26
+ return { prefix, mode: prefix.includes("_test_") ? "test" : "live" };
27
+ }
28
+ function resolveKeyMode(apiKey) {
29
+ try {
30
+ return parseApiKey(apiKey).mode;
31
+ } catch (err) {
32
+ return err instanceof MissingAuraApiKeyError ? "unconfigured" : "invalid";
33
+ }
34
+ }
35
+ var MissingAuraApiKeyError = class extends Error {
36
+ constructor() {
37
+ super(
38
+ 'AURA_API_KEY not set. Configure it in your MCP client config \u2014 for example in mcp.json: { "mcpServers": { "aura": { "command": "aura-mcp", "env": { "AURA_API_KEY": "pk_live_..." } } } }. Get a key at https://getaura.sh.'
39
+ );
40
+ this.name = "MissingAuraApiKeyError";
41
+ }
42
+ };
43
+ var MalformedAuraApiKeyError = class extends Error {
44
+ constructor(apiKey) {
45
+ super(
46
+ `AURA_API_KEY is malformed (got ${maskApiKey(apiKey)}). An Aura API key must start with one of: ${API_KEY_PREFIXES.join(", ")}. Check for a truncated copy/paste, a shell-quoting problem, or a key from a different service. Issue a new key at https://getaura.sh.`
47
+ );
48
+ this.name = "MalformedAuraApiKeyError";
49
+ }
50
+ };
51
+
52
+ // src/client.ts
6
53
  var cached = null;
7
54
  function getClient() {
8
55
  if (cached) return cached;
9
56
  const apiKey = process.env.AURA_API_KEY;
10
- if (!apiKey) {
11
- throw new MissingAuraApiKeyError();
12
- }
57
+ parseApiKey(apiKey);
13
58
  cached = new AuraClient({
14
59
  apiKey,
15
60
  baseUrl: process.env.AURA_BASE_URL,
@@ -18,14 +63,6 @@ function getClient() {
18
63
  });
19
64
  return cached;
20
65
  }
21
- var MissingAuraApiKeyError = class extends Error {
22
- constructor() {
23
- super(
24
- 'AURA_API_KEY not set. Configure it in your MCP client config \u2014 for example in mcp.json: { "mcpServers": { "aura": { "command": "aura-mcp", "env": { "AURA_API_KEY": "pk_live_..." } } } }. Get a key at https://getaura.sh.'
25
- );
26
- this.name = "MissingAuraApiKeyError";
27
- }
28
- };
29
66
 
30
67
  // src/resources/agents.ts
31
68
  function registerAgentResources(server) {
@@ -64,6 +101,39 @@ function registerAgentResources(server) {
64
101
  }
65
102
  );
66
103
  }
104
+ var COLLECTION_LIMIT = 50;
105
+ function registerEscrowResources(server) {
106
+ server.resource("escrows", "aura://escrows", async (uri) => {
107
+ const client = getClient();
108
+ const page = await client.escrows.list({ limit: COLLECTION_LIMIT });
109
+ return {
110
+ contents: [
111
+ {
112
+ uri: uri.href,
113
+ mimeType: "application/json",
114
+ text: JSON.stringify(page, null, 2)
115
+ }
116
+ ]
117
+ };
118
+ });
119
+ server.resource(
120
+ "escrow",
121
+ new ResourceTemplate("aura://escrows/{id}", { list: void 0 }),
122
+ async (uri, { id }) => {
123
+ const client = getClient();
124
+ const escrow = await client.escrows.get(String(id));
125
+ return {
126
+ contents: [
127
+ {
128
+ uri: uri.href,
129
+ mimeType: "application/json",
130
+ text: JSON.stringify(escrow, null, 2)
131
+ }
132
+ ]
133
+ };
134
+ }
135
+ );
136
+ }
67
137
  function registerMandateResources(server) {
68
138
  server.resource(
69
139
  "mandates-pending",
@@ -100,6 +170,72 @@ function registerMandateResources(server) {
100
170
  }
101
171
  );
102
172
  }
173
+ var COLLECTION_LIMIT2 = 50;
174
+ function registerWalletResources(server) {
175
+ server.resource("wallets", "aura://wallets", async (uri) => {
176
+ const client = getClient();
177
+ const page = await client.wallets.list({ limit: COLLECTION_LIMIT2 });
178
+ return {
179
+ contents: [
180
+ {
181
+ uri: uri.href,
182
+ mimeType: "application/json",
183
+ text: JSON.stringify(page, null, 2)
184
+ }
185
+ ]
186
+ };
187
+ });
188
+ server.resource(
189
+ "wallet",
190
+ new ResourceTemplate("aura://wallets/{id}", { list: void 0 }),
191
+ async (uri, { id }) => {
192
+ const client = getClient();
193
+ const wallet = await client.wallets.get(String(id));
194
+ return {
195
+ contents: [
196
+ {
197
+ uri: uri.href,
198
+ mimeType: "application/json",
199
+ text: JSON.stringify(wallet, null, 2)
200
+ }
201
+ ]
202
+ };
203
+ }
204
+ );
205
+ server.resource(
206
+ "wallet-balance",
207
+ new ResourceTemplate("aura://wallets/{id}/balance", { list: void 0 }),
208
+ async (uri, { id }) => {
209
+ const client = getClient();
210
+ const balance = await client.wallets.getBalance(String(id));
211
+ return {
212
+ contents: [
213
+ {
214
+ uri: uri.href,
215
+ mimeType: "application/json",
216
+ text: JSON.stringify(balance, null, 2)
217
+ }
218
+ ]
219
+ };
220
+ }
221
+ );
222
+ }
223
+
224
+ // src/annotations.ts
225
+ function readOnly(title) {
226
+ return { title, readOnlyHint: true };
227
+ }
228
+ function write(title, idempotent = false) {
229
+ return {
230
+ title,
231
+ readOnlyHint: false,
232
+ destructiveHint: false,
233
+ ...idempotent ? { idempotentHint: true } : {}
234
+ };
235
+ }
236
+ function destructive(title) {
237
+ return { title, readOnlyHint: false, destructiveHint: true };
238
+ }
103
239
  async function withToolErrorHandling(body, successFormatter = defaultJsonFormatter) {
104
240
  try {
105
241
  const result = await body();
@@ -114,7 +250,7 @@ function defaultJsonFormatter(data) {
114
250
  return JSON.stringify(data, null, 2);
115
251
  }
116
252
  function formatError(err) {
117
- if (err instanceof MissingAuraApiKeyError) {
253
+ if (err instanceof MissingAuraApiKeyError || err instanceof MalformedAuraApiKeyError) {
118
254
  return errorResult("CONFIG_ERROR", err.message);
119
255
  }
120
256
  if (err instanceof AuraAuthenticationError) {
@@ -202,6 +338,8 @@ function registerAgentTools(server) {
202
338
  autoApproveBelow: z.string().optional()
203
339
  }).optional()
204
340
  },
341
+ // Additive: provisions a new agent and its wallet. No funds move.
342
+ write("Create Agent"),
205
343
  async (args) => withToolErrorHandling(async () => {
206
344
  const client = getClient();
207
345
  return client.agents.create(args);
@@ -216,6 +354,7 @@ function registerAgentTools(server) {
216
354
  limit: z.number().int().min(1).max(200).optional(),
217
355
  offset: z.number().int().min(0).optional()
218
356
  },
357
+ readOnly("List Agents"),
219
358
  async (args) => withToolErrorHandling(async () => {
220
359
  const client = getClient();
221
360
  return client.agents.list(args);
@@ -227,6 +366,7 @@ function registerAgentTools(server) {
227
366
  {
228
367
  agentId: z.string().uuid()
229
368
  },
369
+ readOnly("Get Agent"),
230
370
  async ({ agentId }) => withToolErrorHandling(async () => {
231
371
  const client = getClient();
232
372
  return client.agents.get(agentId);
@@ -238,6 +378,7 @@ function registerAgentTools(server) {
238
378
  {
239
379
  agentId: z.string().uuid()
240
380
  },
381
+ readOnly("Get Agent Balance"),
241
382
  async ({ agentId }) => withToolErrorHandling(async () => {
242
383
  const client = getClient();
243
384
  return client.agents.getBalance(agentId);
@@ -250,6 +391,8 @@ function registerAgentTools(server) {
250
391
  agentId: z.string().uuid(),
251
392
  reason: z.string().max(500).optional().describe("Surfaced in activity event metadata for audit trail")
252
393
  },
394
+ // Halts an existing agent AND cascade-rejects its in-flight mandates.
395
+ destructive("Freeze Agent (kill switch)"),
253
396
  async (args) => withToolErrorHandling(async () => {
254
397
  const client = getClient();
255
398
  return client.agents.freeze(args);
@@ -261,6 +404,8 @@ function registerAgentTools(server) {
261
404
  {
262
405
  agentId: z.string().uuid()
263
406
  },
407
+ // Re-enables autonomous spending on an existing agent.
408
+ destructive("Unfreeze Agent (resumes autonomous spending)"),
264
409
  async (args) => withToolErrorHandling(async () => {
265
410
  const client = getClient();
266
411
  return client.agents.unfreeze(args);
@@ -277,11 +422,24 @@ function registerAgentTools(server) {
277
422
  reasoning: z.string().optional(),
278
423
  metadata: z.record(z.unknown()).optional()
279
424
  },
425
+ // NOT read-only despite being a dry run: when the decision requires human
426
+ // approval the engine persists a new mandate row. Additive, never
427
+ // destructive — it cannot execute the transaction it evaluates.
428
+ write("Evaluate Policy (dry run, may create a mandate)"),
280
429
  async (args) => withToolErrorHandling(async () => {
281
430
  const client = getClient();
282
431
  return client.agents.evaluate(args);
283
432
  })
284
433
  );
434
+ return [
435
+ "aura_create_agent",
436
+ "aura_list_agents",
437
+ "aura_get_agent",
438
+ "aura_get_agent_balance",
439
+ "aura_freeze_agent",
440
+ "aura_unfreeze_agent",
441
+ "aura_evaluate_policy"
442
+ ];
285
443
  }
286
444
  var ChainSchema2 = z.enum(["ARC", "ARB", "BASE", "ETH", "MATIC"]);
287
445
  var SplitInputSchema = z.object({
@@ -318,6 +476,8 @@ function registerEscrowTools(server) {
318
476
  chain: ChainSchema2.optional().describe("Target chain. Defaults to ARC"),
319
477
  splits: z.array(SplitInputSchema).min(1).describe("Payment splits. Percentages must sum to 100.")
320
478
  },
479
+ // Additive: creates a new escrow record. No funds move until `fund`.
480
+ write("Create Escrow"),
321
481
  async (args) => withToolErrorHandling(async () => {
322
482
  const client = getClient();
323
483
  return client.escrows.create(args);
@@ -329,6 +489,7 @@ function registerEscrowTools(server) {
329
489
  {
330
490
  escrowId: z.string().uuid()
331
491
  },
492
+ readOnly("Get Escrow"),
332
493
  async ({ escrowId }) => withToolErrorHandling(async () => {
333
494
  const client = getClient();
334
495
  return client.escrows.get(escrowId);
@@ -353,6 +514,7 @@ function registerEscrowTools(server) {
353
514
  limit: z.number().int().min(1).max(200).optional(),
354
515
  offset: z.number().int().min(0).optional()
355
516
  },
517
+ readOnly("List Escrows"),
356
518
  async (args) => withToolErrorHandling(async () => {
357
519
  const client = getClient();
358
520
  return client.escrows.list(args);
@@ -367,6 +529,8 @@ function registerEscrowTools(server) {
367
529
  amount: z.string().describe("USDC amount to fund (whole-number string)"),
368
530
  correlationId: z.string().optional()
369
531
  },
532
+ // Moves USDC out of the buyer wallet.
533
+ destructive("Fund Escrow (moves USDC)"),
370
534
  async (args) => withToolErrorHandling(async () => {
371
535
  const client = getClient();
372
536
  return client.escrows.fund(args);
@@ -381,6 +545,8 @@ function registerEscrowTools(server) {
381
545
  reason: z.string().optional(),
382
546
  correlationId: z.string().optional()
383
547
  },
548
+ // Irreversible payout to the split recipients.
549
+ destructive("Release Escrow (pays out USDC)"),
384
550
  async (args) => withToolErrorHandling(async () => {
385
551
  const client = getClient();
386
552
  return client.escrows.release(args);
@@ -394,6 +560,8 @@ function registerEscrowTools(server) {
394
560
  reason: z.string().min(10),
395
561
  actorId: z.string().uuid()
396
562
  },
563
+ // Irreversible return of funds to the buyer.
564
+ destructive("Refund Escrow (returns USDC to buyer)"),
397
565
  async (args) => withToolErrorHandling(async () => {
398
566
  const client = getClient();
399
567
  return client.escrows.refund(args);
@@ -413,11 +581,22 @@ function registerEscrowTools(server) {
413
581
  waitForDeploymentMs: z.number().int().min(0).optional().describe("Max ms to wait for on-chain deployment. Default 60000."),
414
582
  waitForFundingMs: z.number().int().min(0).optional().describe("Max ms to wait for funds to settle (funded/locked) before releasing. Default 60000.")
415
583
  },
584
+ // Composes create -> fund -> release: money moves and is paid out.
585
+ destructive("Receive and Split (moves and pays out USDC)"),
416
586
  async (args) => withToolErrorHandling(async () => {
417
587
  const client = getClient();
418
588
  return client.escrows.receiveAndSplit(args);
419
589
  })
420
590
  );
591
+ return [
592
+ "aura_create_escrow",
593
+ "aura_get_escrow",
594
+ "aura_list_escrows",
595
+ "aura_fund_escrow",
596
+ "aura_release_escrow",
597
+ "aura_refund_escrow",
598
+ "aura_receive_and_split"
599
+ ];
421
600
  }
422
601
  function registerMandateTools(server) {
423
602
  server.tool(
@@ -429,6 +608,7 @@ function registerMandateTools(server) {
429
608
  limit: z.number().int().min(1).max(200).optional(),
430
609
  cursor: z.string().uuid().optional().describe("Last mandate id from the previous page")
431
610
  },
611
+ readOnly("List Mandates (approval inbox)"),
432
612
  async (args) => withToolErrorHandling(async () => {
433
613
  const client = getClient();
434
614
  return client.mandates.list(args);
@@ -440,6 +620,7 @@ function registerMandateTools(server) {
440
620
  {
441
621
  mandateId: z.string().uuid()
442
622
  },
623
+ readOnly("Get Mandate"),
443
624
  async ({ mandateId }) => withToolErrorHandling(async () => {
444
625
  const client = getClient();
445
626
  return client.mandates.get(mandateId);
@@ -454,6 +635,8 @@ function registerMandateTools(server) {
454
635
  decisionSignature: z.string().min(16).describe("HMAC-SHA256 hex digest, computed by the caller"),
455
636
  decidedAt: z.string().datetime().optional().describe('ISO-8601 timestamp the signature commits to. Defaults to "now" server-side.')
456
637
  },
638
+ // Authorizes the agent's intended payment — the money-moving decision.
639
+ destructive("Approve Mandate (authorizes payment)"),
457
640
  async (args) => withToolErrorHandling(async () => {
458
641
  const client = getClient();
459
642
  return client.mandates.approve(args);
@@ -469,11 +652,19 @@ function registerMandateTools(server) {
469
652
  decidedAt: z.string().datetime().optional(),
470
653
  reason: z.string().optional().describe("Human-readable reason logged to the activity feed")
471
654
  },
655
+ // Terminal decision on an existing mandate — the agent cannot resume it.
656
+ destructive("Reject Mandate"),
472
657
  async (args) => withToolErrorHandling(async () => {
473
658
  const client = getClient();
474
659
  return client.mandates.reject(args);
475
660
  })
476
661
  );
662
+ return [
663
+ "aura_list_mandates",
664
+ "aura_get_mandate",
665
+ "aura_approve_mandate",
666
+ "aura_reject_mandate"
667
+ ];
477
668
  }
478
669
  var TestnetChainSchema = z.enum(["ARC", "ARB", "BASE"]);
479
670
  function walletCreateEnabled() {
@@ -487,6 +678,7 @@ function registerWalletTools(server) {
487
678
  {
488
679
  walletId: z.string().uuid()
489
680
  },
681
+ readOnly("Get Wallet Balance"),
490
682
  async ({ walletId }) => withToolErrorHandling(async () => {
491
683
  const client = getClient();
492
684
  return client.wallets.getBalance(walletId);
@@ -510,6 +702,8 @@ function registerWalletTools(server) {
510
702
  "tax"
511
703
  ]).optional().describe("Wallet owner type. Defaults to BUSINESS.")
512
704
  },
705
+ // Additive and create-or-get: provisions a receive address, moves no money.
706
+ write("Create Wallet (receive address)", true),
513
707
  async ({ entityId, chain, type }) => {
514
708
  if (!walletCreateEnabled()) {
515
709
  return toolError(
@@ -531,26 +725,108 @@ function registerWalletTools(server) {
531
725
  token: z.string().optional().describe("Token to drip. Defaults to 'USDC'."),
532
726
  chain: TestnetChainSchema.optional().describe("Override the wallet's chain.")
533
727
  },
728
+ // Additive: testnet-only drip that adds play funds, destroys nothing.
729
+ write("Request Testnet Funds"),
534
730
  async ({ walletId, token, chain }) => withToolErrorHandling(async () => {
535
731
  const client = getClient();
536
732
  return client.wallets.requestTestnetFunds(walletId, { token, chain });
537
733
  })
538
734
  );
735
+ return [
736
+ "aura_get_wallet_balance",
737
+ "aura_create_wallet",
738
+ "aura_request_testnet_funds"
739
+ ];
740
+ }
741
+ var WebhookEventTypeSchema = z.enum([
742
+ "*",
743
+ "escrow.created",
744
+ "escrow.funded",
745
+ "escrow.locked",
746
+ "escrow.released",
747
+ "escrow.refunded",
748
+ "escrow.disputed",
749
+ "transfer.completed",
750
+ "transfer.failed",
751
+ "withdrawal.created",
752
+ "withdrawal.processing",
753
+ "withdrawal.completed",
754
+ "withdrawal.failed",
755
+ "account.kyc_approved",
756
+ "account.kyc_rejected",
757
+ "account.tier_upgraded",
758
+ "security.api_key_created",
759
+ "security.api_key_revoked"
760
+ ]);
761
+ function registerWebhookTools(server) {
762
+ server.tool(
763
+ "aura_get_webhook_config",
764
+ "Get the current webhook configuration for the authenticated account: delivery URL, subscribed event types, and status. The signing secret is never returned by this endpoint.",
765
+ {},
766
+ readOnly("Get Webhook Config"),
767
+ async () => withToolErrorHandling(async () => {
768
+ const client = getClient();
769
+ return client.webhooks.getConfig();
770
+ })
771
+ );
772
+ server.tool(
773
+ "aura_set_webhook_config",
774
+ "Set the webhook delivery endpoint for the account. An account has ONE configuration, so this REPLACES any existing one \u2014 events stop going to the previous URL. Must be an https:// URL you control. Returns the signing secret; store it, it is shown only on initial configuration.",
775
+ {
776
+ url: z.string().url().describe("HTTPS endpoint that will receive event POSTs"),
777
+ events: z.array(WebhookEventTypeSchema).min(1).optional().describe(
778
+ "Event types to subscribe to. Omit to subscribe to all events, or pass ['*'] explicitly."
779
+ ),
780
+ description: z.string().optional().describe("Optional human-readable label")
781
+ },
782
+ destructive("Set Webhook Config (replaces existing)"),
783
+ async (args) => withToolErrorHandling(async () => {
784
+ const client = getClient();
785
+ return client.webhooks.configure(args);
786
+ })
787
+ );
788
+ server.tool(
789
+ "aura_delete_webhook_config",
790
+ "Delete the account webhook configuration. Event delivery stops immediately and the signing secret is invalidated \u2014 reconfiguring issues a new secret.",
791
+ {},
792
+ destructive("Delete Webhook Config"),
793
+ async () => withToolErrorHandling(async () => {
794
+ const client = getClient();
795
+ return client.webhooks.delete();
796
+ })
797
+ );
798
+ return [
799
+ "aura_get_webhook_config",
800
+ "aura_set_webhook_config",
801
+ "aura_delete_webhook_config"
802
+ ];
539
803
  }
540
804
 
541
805
  // src/index.ts
806
+ var SERVER_NAME = "aura-payments";
807
+ var SERVER_VERSION = "1.1.0";
808
+ var toolManifests = /* @__PURE__ */ new WeakMap();
809
+ function getRegisteredToolNames(server) {
810
+ return toolManifests.get(server) ?? [];
811
+ }
542
812
  function createServer(options = {}) {
543
813
  const server = new McpServer({
544
- name: options.name ?? "aura-payments",
545
- version: options.version ?? "1.0.1"
814
+ name: options.name ?? SERVER_NAME,
815
+ version: options.version ?? SERVER_VERSION
546
816
  });
547
- registerEscrowTools(server);
548
- registerAgentTools(server);
549
- registerMandateTools(server);
550
- registerWalletTools(server);
817
+ const toolNames = [
818
+ ...registerEscrowTools(server),
819
+ ...registerAgentTools(server),
820
+ ...registerMandateTools(server),
821
+ ...registerWalletTools(server),
822
+ ...registerWebhookTools(server)
823
+ ];
824
+ toolManifests.set(server, Object.freeze(toolNames));
551
825
  registerAgentResources(server);
552
826
  registerMandateResources(server);
827
+ registerEscrowResources(server);
828
+ registerWalletResources(server);
553
829
  return server;
554
830
  }
555
831
 
556
- export { MissingAuraApiKeyError, createServer, getClient };
832
+ export { API_KEY_PREFIXES, MalformedAuraApiKeyError, MissingAuraApiKeyError, SERVER_NAME, SERVER_VERSION, createServer, getClient, getRegisteredToolNames, maskApiKey, parseApiKey, resolveKeyMode };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aura-payments/mcp-server",
3
- "version": "1.0.1",
4
- "description": "Model Context Protocol server for Aura Payments. Lets AI agents and LLMs (Claude Desktop, Cursor, Continue, etc.) drive Aura escrows, mandates, and agent governance natively.",
3
+ "version": "1.1.0",
4
+ "description": "Model Context Protocol server for Aura Payments. Lets AI agents and LLMs (Claude Desktop, Cursor, Continue, etc.) drive Aura escrows, mandates, wallets, webhooks, and agent governance natively.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -62,6 +62,8 @@
62
62
  "build": "tsup",
63
63
  "dev": "tsup --watch",
64
64
  "lint": "eslint src --ext .ts",
65
- "typecheck": "tsc --noEmit"
65
+ "test": "node --test --experimental-strip-types 'src/**/*.test.ts'",
66
+ "typecheck": "tsc --noEmit",
67
+ "pretest": "pnpm --filter @aura-payments/shared --filter @aura-payments/sdk build"
66
68
  }
67
69
  }