@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/bin.js CHANGED
@@ -6,13 +6,55 @@ var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
6
6
  var sdk = require('@aura-payments/sdk');
7
7
  var zod = require('zod');
8
8
 
9
+ // src/api-key.ts
10
+ var API_KEY_PREFIXES = [
11
+ "sk_test_",
12
+ "sk_live_",
13
+ "pk_test_",
14
+ "pk_live_"
15
+ ];
16
+ function maskApiKey(apiKey) {
17
+ const revealed = Math.min(8, Math.floor(apiKey.length / 2));
18
+ return `${apiKey.slice(0, revealed)}\u2026(${apiKey.length} chars)`;
19
+ }
20
+ function parseApiKey(apiKey) {
21
+ if (!apiKey || apiKey.trim() === "") {
22
+ throw new MissingAuraApiKeyError();
23
+ }
24
+ const prefix = API_KEY_PREFIXES.find((p) => apiKey.startsWith(p));
25
+ if (!prefix) {
26
+ throw new MalformedAuraApiKeyError(apiKey);
27
+ }
28
+ return { prefix, mode: prefix.includes("_test_") ? "test" : "live" };
29
+ }
30
+ function resolveKeyMode(apiKey) {
31
+ try {
32
+ return parseApiKey(apiKey).mode;
33
+ } catch (err) {
34
+ return err instanceof MissingAuraApiKeyError ? "unconfigured" : "invalid";
35
+ }
36
+ }
37
+ var MissingAuraApiKeyError = class extends Error {
38
+ constructor() {
39
+ super(
40
+ '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.'
41
+ );
42
+ this.name = "MissingAuraApiKeyError";
43
+ }
44
+ };
45
+ var MalformedAuraApiKeyError = class extends Error {
46
+ constructor(apiKey) {
47
+ super(
48
+ `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.`
49
+ );
50
+ this.name = "MalformedAuraApiKeyError";
51
+ }
52
+ };
9
53
  var cached = null;
10
54
  function getClient() {
11
55
  if (cached) return cached;
12
56
  const apiKey = process.env.AURA_API_KEY;
13
- if (!apiKey) {
14
- throw new MissingAuraApiKeyError();
15
- }
57
+ parseApiKey(apiKey);
16
58
  cached = new sdk.AuraClient({
17
59
  apiKey,
18
60
  baseUrl: process.env.AURA_BASE_URL,
@@ -21,14 +63,6 @@ function getClient() {
21
63
  });
22
64
  return cached;
23
65
  }
24
- var MissingAuraApiKeyError = class extends Error {
25
- constructor() {
26
- super(
27
- '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.'
28
- );
29
- this.name = "MissingAuraApiKeyError";
30
- }
31
- };
32
66
 
33
67
  // src/resources/agents.ts
34
68
  function registerAgentResources(server) {
@@ -67,6 +101,39 @@ function registerAgentResources(server) {
67
101
  }
68
102
  );
69
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 mcp_js.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
+ }
70
137
  function registerMandateResources(server) {
71
138
  server.resource(
72
139
  "mandates-pending",
@@ -103,6 +170,72 @@ function registerMandateResources(server) {
103
170
  }
104
171
  );
105
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 mcp_js.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 mcp_js.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
+ }
106
239
  async function withToolErrorHandling(body, successFormatter = defaultJsonFormatter) {
107
240
  try {
108
241
  const result = await body();
@@ -117,7 +250,7 @@ function defaultJsonFormatter(data) {
117
250
  return JSON.stringify(data, null, 2);
118
251
  }
119
252
  function formatError(err) {
120
- if (err instanceof MissingAuraApiKeyError) {
253
+ if (err instanceof MissingAuraApiKeyError || err instanceof MalformedAuraApiKeyError) {
121
254
  return errorResult("CONFIG_ERROR", err.message);
122
255
  }
123
256
  if (err instanceof sdk.AuraAuthenticationError) {
@@ -205,6 +338,8 @@ function registerAgentTools(server) {
205
338
  autoApproveBelow: zod.z.string().optional()
206
339
  }).optional()
207
340
  },
341
+ // Additive: provisions a new agent and its wallet. No funds move.
342
+ write("Create Agent"),
208
343
  async (args) => withToolErrorHandling(async () => {
209
344
  const client = getClient();
210
345
  return client.agents.create(args);
@@ -219,6 +354,7 @@ function registerAgentTools(server) {
219
354
  limit: zod.z.number().int().min(1).max(200).optional(),
220
355
  offset: zod.z.number().int().min(0).optional()
221
356
  },
357
+ readOnly("List Agents"),
222
358
  async (args) => withToolErrorHandling(async () => {
223
359
  const client = getClient();
224
360
  return client.agents.list(args);
@@ -230,6 +366,7 @@ function registerAgentTools(server) {
230
366
  {
231
367
  agentId: zod.z.string().uuid()
232
368
  },
369
+ readOnly("Get Agent"),
233
370
  async ({ agentId }) => withToolErrorHandling(async () => {
234
371
  const client = getClient();
235
372
  return client.agents.get(agentId);
@@ -241,6 +378,7 @@ function registerAgentTools(server) {
241
378
  {
242
379
  agentId: zod.z.string().uuid()
243
380
  },
381
+ readOnly("Get Agent Balance"),
244
382
  async ({ agentId }) => withToolErrorHandling(async () => {
245
383
  const client = getClient();
246
384
  return client.agents.getBalance(agentId);
@@ -253,6 +391,8 @@ function registerAgentTools(server) {
253
391
  agentId: zod.z.string().uuid(),
254
392
  reason: zod.z.string().max(500).optional().describe("Surfaced in activity event metadata for audit trail")
255
393
  },
394
+ // Halts an existing agent AND cascade-rejects its in-flight mandates.
395
+ destructive("Freeze Agent (kill switch)"),
256
396
  async (args) => withToolErrorHandling(async () => {
257
397
  const client = getClient();
258
398
  return client.agents.freeze(args);
@@ -264,6 +404,8 @@ function registerAgentTools(server) {
264
404
  {
265
405
  agentId: zod.z.string().uuid()
266
406
  },
407
+ // Re-enables autonomous spending on an existing agent.
408
+ destructive("Unfreeze Agent (resumes autonomous spending)"),
267
409
  async (args) => withToolErrorHandling(async () => {
268
410
  const client = getClient();
269
411
  return client.agents.unfreeze(args);
@@ -280,11 +422,24 @@ function registerAgentTools(server) {
280
422
  reasoning: zod.z.string().optional(),
281
423
  metadata: zod.z.record(zod.z.unknown()).optional()
282
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)"),
283
429
  async (args) => withToolErrorHandling(async () => {
284
430
  const client = getClient();
285
431
  return client.agents.evaluate(args);
286
432
  })
287
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
+ ];
288
443
  }
289
444
  var ChainSchema2 = zod.z.enum(["ARC", "ARB", "BASE", "ETH", "MATIC"]);
290
445
  var SplitInputSchema = zod.z.object({
@@ -321,6 +476,8 @@ function registerEscrowTools(server) {
321
476
  chain: ChainSchema2.optional().describe("Target chain. Defaults to ARC"),
322
477
  splits: zod.z.array(SplitInputSchema).min(1).describe("Payment splits. Percentages must sum to 100.")
323
478
  },
479
+ // Additive: creates a new escrow record. No funds move until `fund`.
480
+ write("Create Escrow"),
324
481
  async (args) => withToolErrorHandling(async () => {
325
482
  const client = getClient();
326
483
  return client.escrows.create(args);
@@ -332,6 +489,7 @@ function registerEscrowTools(server) {
332
489
  {
333
490
  escrowId: zod.z.string().uuid()
334
491
  },
492
+ readOnly("Get Escrow"),
335
493
  async ({ escrowId }) => withToolErrorHandling(async () => {
336
494
  const client = getClient();
337
495
  return client.escrows.get(escrowId);
@@ -356,6 +514,7 @@ function registerEscrowTools(server) {
356
514
  limit: zod.z.number().int().min(1).max(200).optional(),
357
515
  offset: zod.z.number().int().min(0).optional()
358
516
  },
517
+ readOnly("List Escrows"),
359
518
  async (args) => withToolErrorHandling(async () => {
360
519
  const client = getClient();
361
520
  return client.escrows.list(args);
@@ -370,6 +529,8 @@ function registerEscrowTools(server) {
370
529
  amount: zod.z.string().describe("USDC amount to fund (whole-number string)"),
371
530
  correlationId: zod.z.string().optional()
372
531
  },
532
+ // Moves USDC out of the buyer wallet.
533
+ destructive("Fund Escrow (moves USDC)"),
373
534
  async (args) => withToolErrorHandling(async () => {
374
535
  const client = getClient();
375
536
  return client.escrows.fund(args);
@@ -384,6 +545,8 @@ function registerEscrowTools(server) {
384
545
  reason: zod.z.string().optional(),
385
546
  correlationId: zod.z.string().optional()
386
547
  },
548
+ // Irreversible payout to the split recipients.
549
+ destructive("Release Escrow (pays out USDC)"),
387
550
  async (args) => withToolErrorHandling(async () => {
388
551
  const client = getClient();
389
552
  return client.escrows.release(args);
@@ -397,6 +560,8 @@ function registerEscrowTools(server) {
397
560
  reason: zod.z.string().min(10),
398
561
  actorId: zod.z.string().uuid()
399
562
  },
563
+ // Irreversible return of funds to the buyer.
564
+ destructive("Refund Escrow (returns USDC to buyer)"),
400
565
  async (args) => withToolErrorHandling(async () => {
401
566
  const client = getClient();
402
567
  return client.escrows.refund(args);
@@ -416,11 +581,22 @@ function registerEscrowTools(server) {
416
581
  waitForDeploymentMs: zod.z.number().int().min(0).optional().describe("Max ms to wait for on-chain deployment. Default 60000."),
417
582
  waitForFundingMs: zod.z.number().int().min(0).optional().describe("Max ms to wait for funds to settle (funded/locked) before releasing. Default 60000.")
418
583
  },
584
+ // Composes create -> fund -> release: money moves and is paid out.
585
+ destructive("Receive and Split (moves and pays out USDC)"),
419
586
  async (args) => withToolErrorHandling(async () => {
420
587
  const client = getClient();
421
588
  return client.escrows.receiveAndSplit(args);
422
589
  })
423
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
+ ];
424
600
  }
425
601
  function registerMandateTools(server) {
426
602
  server.tool(
@@ -432,6 +608,7 @@ function registerMandateTools(server) {
432
608
  limit: zod.z.number().int().min(1).max(200).optional(),
433
609
  cursor: zod.z.string().uuid().optional().describe("Last mandate id from the previous page")
434
610
  },
611
+ readOnly("List Mandates (approval inbox)"),
435
612
  async (args) => withToolErrorHandling(async () => {
436
613
  const client = getClient();
437
614
  return client.mandates.list(args);
@@ -443,6 +620,7 @@ function registerMandateTools(server) {
443
620
  {
444
621
  mandateId: zod.z.string().uuid()
445
622
  },
623
+ readOnly("Get Mandate"),
446
624
  async ({ mandateId }) => withToolErrorHandling(async () => {
447
625
  const client = getClient();
448
626
  return client.mandates.get(mandateId);
@@ -457,6 +635,8 @@ function registerMandateTools(server) {
457
635
  decisionSignature: zod.z.string().min(16).describe("HMAC-SHA256 hex digest, computed by the caller"),
458
636
  decidedAt: zod.z.string().datetime().optional().describe('ISO-8601 timestamp the signature commits to. Defaults to "now" server-side.')
459
637
  },
638
+ // Authorizes the agent's intended payment — the money-moving decision.
639
+ destructive("Approve Mandate (authorizes payment)"),
460
640
  async (args) => withToolErrorHandling(async () => {
461
641
  const client = getClient();
462
642
  return client.mandates.approve(args);
@@ -472,11 +652,19 @@ function registerMandateTools(server) {
472
652
  decidedAt: zod.z.string().datetime().optional(),
473
653
  reason: zod.z.string().optional().describe("Human-readable reason logged to the activity feed")
474
654
  },
655
+ // Terminal decision on an existing mandate — the agent cannot resume it.
656
+ destructive("Reject Mandate"),
475
657
  async (args) => withToolErrorHandling(async () => {
476
658
  const client = getClient();
477
659
  return client.mandates.reject(args);
478
660
  })
479
661
  );
662
+ return [
663
+ "aura_list_mandates",
664
+ "aura_get_mandate",
665
+ "aura_approve_mandate",
666
+ "aura_reject_mandate"
667
+ ];
480
668
  }
481
669
  var TestnetChainSchema = zod.z.enum(["ARC", "ARB", "BASE"]);
482
670
  function walletCreateEnabled() {
@@ -490,6 +678,7 @@ function registerWalletTools(server) {
490
678
  {
491
679
  walletId: zod.z.string().uuid()
492
680
  },
681
+ readOnly("Get Wallet Balance"),
493
682
  async ({ walletId }) => withToolErrorHandling(async () => {
494
683
  const client = getClient();
495
684
  return client.wallets.getBalance(walletId);
@@ -513,6 +702,8 @@ function registerWalletTools(server) {
513
702
  "tax"
514
703
  ]).optional().describe("Wallet owner type. Defaults to BUSINESS.")
515
704
  },
705
+ // Additive and create-or-get: provisions a receive address, moves no money.
706
+ write("Create Wallet (receive address)", true),
516
707
  async ({ entityId, chain, type }) => {
517
708
  if (!walletCreateEnabled()) {
518
709
  return toolError(
@@ -534,31 +725,131 @@ function registerWalletTools(server) {
534
725
  token: zod.z.string().optional().describe("Token to drip. Defaults to 'USDC'."),
535
726
  chain: TestnetChainSchema.optional().describe("Override the wallet's chain.")
536
727
  },
728
+ // Additive: testnet-only drip that adds play funds, destroys nothing.
729
+ write("Request Testnet Funds"),
537
730
  async ({ walletId, token, chain }) => withToolErrorHandling(async () => {
538
731
  const client = getClient();
539
732
  return client.wallets.requestTestnetFunds(walletId, { token, chain });
540
733
  })
541
734
  );
735
+ return [
736
+ "aura_get_wallet_balance",
737
+ "aura_create_wallet",
738
+ "aura_request_testnet_funds"
739
+ ];
740
+ }
741
+ var WebhookEventTypeSchema = zod.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: zod.z.string().url().describe("HTTPS endpoint that will receive event POSTs"),
777
+ events: zod.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: zod.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
+ ];
542
803
  }
543
804
 
544
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
+ }
545
812
  function createServer(options = {}) {
546
813
  const server = new mcp_js.McpServer({
547
- name: options.name ?? "aura-payments",
548
- version: options.version ?? "1.0.1"
814
+ name: options.name ?? SERVER_NAME,
815
+ version: options.version ?? SERVER_VERSION
549
816
  });
550
- registerEscrowTools(server);
551
- registerAgentTools(server);
552
- registerMandateTools(server);
553
- 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));
554
825
  registerAgentResources(server);
555
826
  registerMandateResources(server);
827
+ registerEscrowResources(server);
828
+ registerWalletResources(server);
556
829
  return server;
557
830
  }
558
831
 
559
832
  // src/bin.ts
833
+ var DEFAULT_BASE_URL = "https://getaura.sh/api";
834
+ function emitBanner(toolCount) {
835
+ const mode = resolveKeyMode(process.env.AURA_API_KEY);
836
+ const baseUrl = process.env.AURA_BASE_URL || DEFAULT_BASE_URL;
837
+ console.error(
838
+ `${SERVER_NAME} v${SERVER_VERSION} | mode=${mode} | baseUrl=${baseUrl} | tools=${toolCount}`
839
+ );
840
+ if (mode === "unconfigured") {
841
+ console.error(
842
+ `${SERVER_NAME}: AURA_API_KEY is not set \u2014 tool calls will fail until you add it to the \`env\` block of your MCP client config.`
843
+ );
844
+ } else if (mode === "invalid") {
845
+ console.error(
846
+ `${SERVER_NAME}: AURA_API_KEY is malformed \u2014 expected an sk_test_ / sk_live_ / pk_test_ / pk_live_ prefix. Tool calls will fail until it is corrected.`
847
+ );
848
+ }
849
+ }
560
850
  async function main() {
561
851
  const server = createServer();
852
+ emitBanner(getRegisteredToolNames(server).length);
562
853
  const transport = new stdio_js.StdioServerTransport();
563
854
  await server.connect(transport);
564
855
  }