@aura-payments/mcp-server 1.0.0 → 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.mjs CHANGED
@@ -4,13 +4,55 @@ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mc
4
4
  import { AuraClient, AuraAuthenticationError, AuraNotFoundError, AuraValidationError, AuraRateLimitError, AuraTimeoutError, AuraAPIError, AuraError } from '@aura-payments/sdk';
5
5
  import { z } from 'zod';
6
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
+ };
7
51
  var cached = null;
8
52
  function getClient() {
9
53
  if (cached) return cached;
10
54
  const apiKey = process.env.AURA_API_KEY;
11
- if (!apiKey) {
12
- throw new MissingAuraApiKeyError();
13
- }
55
+ parseApiKey(apiKey);
14
56
  cached = new AuraClient({
15
57
  apiKey,
16
58
  baseUrl: process.env.AURA_BASE_URL,
@@ -19,14 +61,6 @@ function getClient() {
19
61
  });
20
62
  return cached;
21
63
  }
22
- var MissingAuraApiKeyError = class extends Error {
23
- constructor() {
24
- super(
25
- '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.'
26
- );
27
- this.name = "MissingAuraApiKeyError";
28
- }
29
- };
30
64
 
31
65
  // src/resources/agents.ts
32
66
  function registerAgentResources(server) {
@@ -65,6 +99,39 @@ function registerAgentResources(server) {
65
99
  }
66
100
  );
67
101
  }
102
+ var COLLECTION_LIMIT = 50;
103
+ function registerEscrowResources(server) {
104
+ server.resource("escrows", "aura://escrows", async (uri) => {
105
+ const client = getClient();
106
+ const page = await client.escrows.list({ limit: COLLECTION_LIMIT });
107
+ return {
108
+ contents: [
109
+ {
110
+ uri: uri.href,
111
+ mimeType: "application/json",
112
+ text: JSON.stringify(page, null, 2)
113
+ }
114
+ ]
115
+ };
116
+ });
117
+ server.resource(
118
+ "escrow",
119
+ new ResourceTemplate("aura://escrows/{id}", { list: void 0 }),
120
+ async (uri, { id }) => {
121
+ const client = getClient();
122
+ const escrow = await client.escrows.get(String(id));
123
+ return {
124
+ contents: [
125
+ {
126
+ uri: uri.href,
127
+ mimeType: "application/json",
128
+ text: JSON.stringify(escrow, null, 2)
129
+ }
130
+ ]
131
+ };
132
+ }
133
+ );
134
+ }
68
135
  function registerMandateResources(server) {
69
136
  server.resource(
70
137
  "mandates-pending",
@@ -101,6 +168,72 @@ function registerMandateResources(server) {
101
168
  }
102
169
  );
103
170
  }
171
+ var COLLECTION_LIMIT2 = 50;
172
+ function registerWalletResources(server) {
173
+ server.resource("wallets", "aura://wallets", async (uri) => {
174
+ const client = getClient();
175
+ const page = await client.wallets.list({ limit: COLLECTION_LIMIT2 });
176
+ return {
177
+ contents: [
178
+ {
179
+ uri: uri.href,
180
+ mimeType: "application/json",
181
+ text: JSON.stringify(page, null, 2)
182
+ }
183
+ ]
184
+ };
185
+ });
186
+ server.resource(
187
+ "wallet",
188
+ new ResourceTemplate("aura://wallets/{id}", { list: void 0 }),
189
+ async (uri, { id }) => {
190
+ const client = getClient();
191
+ const wallet = await client.wallets.get(String(id));
192
+ return {
193
+ contents: [
194
+ {
195
+ uri: uri.href,
196
+ mimeType: "application/json",
197
+ text: JSON.stringify(wallet, null, 2)
198
+ }
199
+ ]
200
+ };
201
+ }
202
+ );
203
+ server.resource(
204
+ "wallet-balance",
205
+ new ResourceTemplate("aura://wallets/{id}/balance", { list: void 0 }),
206
+ async (uri, { id }) => {
207
+ const client = getClient();
208
+ const balance = await client.wallets.getBalance(String(id));
209
+ return {
210
+ contents: [
211
+ {
212
+ uri: uri.href,
213
+ mimeType: "application/json",
214
+ text: JSON.stringify(balance, null, 2)
215
+ }
216
+ ]
217
+ };
218
+ }
219
+ );
220
+ }
221
+
222
+ // src/annotations.ts
223
+ function readOnly(title) {
224
+ return { title, readOnlyHint: true };
225
+ }
226
+ function write(title, idempotent = false) {
227
+ return {
228
+ title,
229
+ readOnlyHint: false,
230
+ destructiveHint: false,
231
+ ...idempotent ? { idempotentHint: true } : {}
232
+ };
233
+ }
234
+ function destructive(title) {
235
+ return { title, readOnlyHint: false, destructiveHint: true };
236
+ }
104
237
  async function withToolErrorHandling(body, successFormatter = defaultJsonFormatter) {
105
238
  try {
106
239
  const result = await body();
@@ -115,7 +248,7 @@ function defaultJsonFormatter(data) {
115
248
  return JSON.stringify(data, null, 2);
116
249
  }
117
250
  function formatError(err) {
118
- if (err instanceof MissingAuraApiKeyError) {
251
+ if (err instanceof MissingAuraApiKeyError || err instanceof MalformedAuraApiKeyError) {
119
252
  return errorResult("CONFIG_ERROR", err.message);
120
253
  }
121
254
  if (err instanceof AuraAuthenticationError) {
@@ -203,6 +336,8 @@ function registerAgentTools(server) {
203
336
  autoApproveBelow: z.string().optional()
204
337
  }).optional()
205
338
  },
339
+ // Additive: provisions a new agent and its wallet. No funds move.
340
+ write("Create Agent"),
206
341
  async (args) => withToolErrorHandling(async () => {
207
342
  const client = getClient();
208
343
  return client.agents.create(args);
@@ -217,6 +352,7 @@ function registerAgentTools(server) {
217
352
  limit: z.number().int().min(1).max(200).optional(),
218
353
  offset: z.number().int().min(0).optional()
219
354
  },
355
+ readOnly("List Agents"),
220
356
  async (args) => withToolErrorHandling(async () => {
221
357
  const client = getClient();
222
358
  return client.agents.list(args);
@@ -228,6 +364,7 @@ function registerAgentTools(server) {
228
364
  {
229
365
  agentId: z.string().uuid()
230
366
  },
367
+ readOnly("Get Agent"),
231
368
  async ({ agentId }) => withToolErrorHandling(async () => {
232
369
  const client = getClient();
233
370
  return client.agents.get(agentId);
@@ -239,6 +376,7 @@ function registerAgentTools(server) {
239
376
  {
240
377
  agentId: z.string().uuid()
241
378
  },
379
+ readOnly("Get Agent Balance"),
242
380
  async ({ agentId }) => withToolErrorHandling(async () => {
243
381
  const client = getClient();
244
382
  return client.agents.getBalance(agentId);
@@ -251,6 +389,8 @@ function registerAgentTools(server) {
251
389
  agentId: z.string().uuid(),
252
390
  reason: z.string().max(500).optional().describe("Surfaced in activity event metadata for audit trail")
253
391
  },
392
+ // Halts an existing agent AND cascade-rejects its in-flight mandates.
393
+ destructive("Freeze Agent (kill switch)"),
254
394
  async (args) => withToolErrorHandling(async () => {
255
395
  const client = getClient();
256
396
  return client.agents.freeze(args);
@@ -262,6 +402,8 @@ function registerAgentTools(server) {
262
402
  {
263
403
  agentId: z.string().uuid()
264
404
  },
405
+ // Re-enables autonomous spending on an existing agent.
406
+ destructive("Unfreeze Agent (resumes autonomous spending)"),
265
407
  async (args) => withToolErrorHandling(async () => {
266
408
  const client = getClient();
267
409
  return client.agents.unfreeze(args);
@@ -278,11 +420,24 @@ function registerAgentTools(server) {
278
420
  reasoning: z.string().optional(),
279
421
  metadata: z.record(z.unknown()).optional()
280
422
  },
423
+ // NOT read-only despite being a dry run: when the decision requires human
424
+ // approval the engine persists a new mandate row. Additive, never
425
+ // destructive — it cannot execute the transaction it evaluates.
426
+ write("Evaluate Policy (dry run, may create a mandate)"),
281
427
  async (args) => withToolErrorHandling(async () => {
282
428
  const client = getClient();
283
429
  return client.agents.evaluate(args);
284
430
  })
285
431
  );
432
+ return [
433
+ "aura_create_agent",
434
+ "aura_list_agents",
435
+ "aura_get_agent",
436
+ "aura_get_agent_balance",
437
+ "aura_freeze_agent",
438
+ "aura_unfreeze_agent",
439
+ "aura_evaluate_policy"
440
+ ];
286
441
  }
287
442
  var ChainSchema2 = z.enum(["ARC", "ARB", "BASE", "ETH", "MATIC"]);
288
443
  var SplitInputSchema = z.object({
@@ -319,6 +474,8 @@ function registerEscrowTools(server) {
319
474
  chain: ChainSchema2.optional().describe("Target chain. Defaults to ARC"),
320
475
  splits: z.array(SplitInputSchema).min(1).describe("Payment splits. Percentages must sum to 100.")
321
476
  },
477
+ // Additive: creates a new escrow record. No funds move until `fund`.
478
+ write("Create Escrow"),
322
479
  async (args) => withToolErrorHandling(async () => {
323
480
  const client = getClient();
324
481
  return client.escrows.create(args);
@@ -330,6 +487,7 @@ function registerEscrowTools(server) {
330
487
  {
331
488
  escrowId: z.string().uuid()
332
489
  },
490
+ readOnly("Get Escrow"),
333
491
  async ({ escrowId }) => withToolErrorHandling(async () => {
334
492
  const client = getClient();
335
493
  return client.escrows.get(escrowId);
@@ -354,6 +512,7 @@ function registerEscrowTools(server) {
354
512
  limit: z.number().int().min(1).max(200).optional(),
355
513
  offset: z.number().int().min(0).optional()
356
514
  },
515
+ readOnly("List Escrows"),
357
516
  async (args) => withToolErrorHandling(async () => {
358
517
  const client = getClient();
359
518
  return client.escrows.list(args);
@@ -368,6 +527,8 @@ function registerEscrowTools(server) {
368
527
  amount: z.string().describe("USDC amount to fund (whole-number string)"),
369
528
  correlationId: z.string().optional()
370
529
  },
530
+ // Moves USDC out of the buyer wallet.
531
+ destructive("Fund Escrow (moves USDC)"),
371
532
  async (args) => withToolErrorHandling(async () => {
372
533
  const client = getClient();
373
534
  return client.escrows.fund(args);
@@ -382,6 +543,8 @@ function registerEscrowTools(server) {
382
543
  reason: z.string().optional(),
383
544
  correlationId: z.string().optional()
384
545
  },
546
+ // Irreversible payout to the split recipients.
547
+ destructive("Release Escrow (pays out USDC)"),
385
548
  async (args) => withToolErrorHandling(async () => {
386
549
  const client = getClient();
387
550
  return client.escrows.release(args);
@@ -395,6 +558,8 @@ function registerEscrowTools(server) {
395
558
  reason: z.string().min(10),
396
559
  actorId: z.string().uuid()
397
560
  },
561
+ // Irreversible return of funds to the buyer.
562
+ destructive("Refund Escrow (returns USDC to buyer)"),
398
563
  async (args) => withToolErrorHandling(async () => {
399
564
  const client = getClient();
400
565
  return client.escrows.refund(args);
@@ -414,11 +579,22 @@ function registerEscrowTools(server) {
414
579
  waitForDeploymentMs: z.number().int().min(0).optional().describe("Max ms to wait for on-chain deployment. Default 60000."),
415
580
  waitForFundingMs: z.number().int().min(0).optional().describe("Max ms to wait for funds to settle (funded/locked) before releasing. Default 60000.")
416
581
  },
582
+ // Composes create -> fund -> release: money moves and is paid out.
583
+ destructive("Receive and Split (moves and pays out USDC)"),
417
584
  async (args) => withToolErrorHandling(async () => {
418
585
  const client = getClient();
419
586
  return client.escrows.receiveAndSplit(args);
420
587
  })
421
588
  );
589
+ return [
590
+ "aura_create_escrow",
591
+ "aura_get_escrow",
592
+ "aura_list_escrows",
593
+ "aura_fund_escrow",
594
+ "aura_release_escrow",
595
+ "aura_refund_escrow",
596
+ "aura_receive_and_split"
597
+ ];
422
598
  }
423
599
  function registerMandateTools(server) {
424
600
  server.tool(
@@ -430,6 +606,7 @@ function registerMandateTools(server) {
430
606
  limit: z.number().int().min(1).max(200).optional(),
431
607
  cursor: z.string().uuid().optional().describe("Last mandate id from the previous page")
432
608
  },
609
+ readOnly("List Mandates (approval inbox)"),
433
610
  async (args) => withToolErrorHandling(async () => {
434
611
  const client = getClient();
435
612
  return client.mandates.list(args);
@@ -441,6 +618,7 @@ function registerMandateTools(server) {
441
618
  {
442
619
  mandateId: z.string().uuid()
443
620
  },
621
+ readOnly("Get Mandate"),
444
622
  async ({ mandateId }) => withToolErrorHandling(async () => {
445
623
  const client = getClient();
446
624
  return client.mandates.get(mandateId);
@@ -455,6 +633,8 @@ function registerMandateTools(server) {
455
633
  decisionSignature: z.string().min(16).describe("HMAC-SHA256 hex digest, computed by the caller"),
456
634
  decidedAt: z.string().datetime().optional().describe('ISO-8601 timestamp the signature commits to. Defaults to "now" server-side.')
457
635
  },
636
+ // Authorizes the agent's intended payment — the money-moving decision.
637
+ destructive("Approve Mandate (authorizes payment)"),
458
638
  async (args) => withToolErrorHandling(async () => {
459
639
  const client = getClient();
460
640
  return client.mandates.approve(args);
@@ -470,11 +650,19 @@ function registerMandateTools(server) {
470
650
  decidedAt: z.string().datetime().optional(),
471
651
  reason: z.string().optional().describe("Human-readable reason logged to the activity feed")
472
652
  },
653
+ // Terminal decision on an existing mandate — the agent cannot resume it.
654
+ destructive("Reject Mandate"),
473
655
  async (args) => withToolErrorHandling(async () => {
474
656
  const client = getClient();
475
657
  return client.mandates.reject(args);
476
658
  })
477
659
  );
660
+ return [
661
+ "aura_list_mandates",
662
+ "aura_get_mandate",
663
+ "aura_approve_mandate",
664
+ "aura_reject_mandate"
665
+ ];
478
666
  }
479
667
  var TestnetChainSchema = z.enum(["ARC", "ARB", "BASE"]);
480
668
  function walletCreateEnabled() {
@@ -488,6 +676,7 @@ function registerWalletTools(server) {
488
676
  {
489
677
  walletId: z.string().uuid()
490
678
  },
679
+ readOnly("Get Wallet Balance"),
491
680
  async ({ walletId }) => withToolErrorHandling(async () => {
492
681
  const client = getClient();
493
682
  return client.wallets.getBalance(walletId);
@@ -511,6 +700,8 @@ function registerWalletTools(server) {
511
700
  "tax"
512
701
  ]).optional().describe("Wallet owner type. Defaults to BUSINESS.")
513
702
  },
703
+ // Additive and create-or-get: provisions a receive address, moves no money.
704
+ write("Create Wallet (receive address)", true),
514
705
  async ({ entityId, chain, type }) => {
515
706
  if (!walletCreateEnabled()) {
516
707
  return toolError(
@@ -532,31 +723,131 @@ function registerWalletTools(server) {
532
723
  token: z.string().optional().describe("Token to drip. Defaults to 'USDC'."),
533
724
  chain: TestnetChainSchema.optional().describe("Override the wallet's chain.")
534
725
  },
726
+ // Additive: testnet-only drip that adds play funds, destroys nothing.
727
+ write("Request Testnet Funds"),
535
728
  async ({ walletId, token, chain }) => withToolErrorHandling(async () => {
536
729
  const client = getClient();
537
730
  return client.wallets.requestTestnetFunds(walletId, { token, chain });
538
731
  })
539
732
  );
733
+ return [
734
+ "aura_get_wallet_balance",
735
+ "aura_create_wallet",
736
+ "aura_request_testnet_funds"
737
+ ];
738
+ }
739
+ var WebhookEventTypeSchema = z.enum([
740
+ "*",
741
+ "escrow.created",
742
+ "escrow.funded",
743
+ "escrow.locked",
744
+ "escrow.released",
745
+ "escrow.refunded",
746
+ "escrow.disputed",
747
+ "transfer.completed",
748
+ "transfer.failed",
749
+ "withdrawal.created",
750
+ "withdrawal.processing",
751
+ "withdrawal.completed",
752
+ "withdrawal.failed",
753
+ "account.kyc_approved",
754
+ "account.kyc_rejected",
755
+ "account.tier_upgraded",
756
+ "security.api_key_created",
757
+ "security.api_key_revoked"
758
+ ]);
759
+ function registerWebhookTools(server) {
760
+ server.tool(
761
+ "aura_get_webhook_config",
762
+ "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.",
763
+ {},
764
+ readOnly("Get Webhook Config"),
765
+ async () => withToolErrorHandling(async () => {
766
+ const client = getClient();
767
+ return client.webhooks.getConfig();
768
+ })
769
+ );
770
+ server.tool(
771
+ "aura_set_webhook_config",
772
+ "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.",
773
+ {
774
+ url: z.string().url().describe("HTTPS endpoint that will receive event POSTs"),
775
+ events: z.array(WebhookEventTypeSchema).min(1).optional().describe(
776
+ "Event types to subscribe to. Omit to subscribe to all events, or pass ['*'] explicitly."
777
+ ),
778
+ description: z.string().optional().describe("Optional human-readable label")
779
+ },
780
+ destructive("Set Webhook Config (replaces existing)"),
781
+ async (args) => withToolErrorHandling(async () => {
782
+ const client = getClient();
783
+ return client.webhooks.configure(args);
784
+ })
785
+ );
786
+ server.tool(
787
+ "aura_delete_webhook_config",
788
+ "Delete the account webhook configuration. Event delivery stops immediately and the signing secret is invalidated \u2014 reconfiguring issues a new secret.",
789
+ {},
790
+ destructive("Delete Webhook Config"),
791
+ async () => withToolErrorHandling(async () => {
792
+ const client = getClient();
793
+ return client.webhooks.delete();
794
+ })
795
+ );
796
+ return [
797
+ "aura_get_webhook_config",
798
+ "aura_set_webhook_config",
799
+ "aura_delete_webhook_config"
800
+ ];
540
801
  }
541
802
 
542
803
  // src/index.ts
804
+ var SERVER_NAME = "aura-payments";
805
+ var SERVER_VERSION = "1.1.0";
806
+ var toolManifests = /* @__PURE__ */ new WeakMap();
807
+ function getRegisteredToolNames(server) {
808
+ return toolManifests.get(server) ?? [];
809
+ }
543
810
  function createServer(options = {}) {
544
811
  const server = new McpServer({
545
- name: options.name ?? "aura-payments",
546
- version: options.version ?? "1.0.0"
812
+ name: options.name ?? SERVER_NAME,
813
+ version: options.version ?? SERVER_VERSION
547
814
  });
548
- registerEscrowTools(server);
549
- registerAgentTools(server);
550
- registerMandateTools(server);
551
- registerWalletTools(server);
815
+ const toolNames = [
816
+ ...registerEscrowTools(server),
817
+ ...registerAgentTools(server),
818
+ ...registerMandateTools(server),
819
+ ...registerWalletTools(server),
820
+ ...registerWebhookTools(server)
821
+ ];
822
+ toolManifests.set(server, Object.freeze(toolNames));
552
823
  registerAgentResources(server);
553
824
  registerMandateResources(server);
825
+ registerEscrowResources(server);
826
+ registerWalletResources(server);
554
827
  return server;
555
828
  }
556
829
 
557
830
  // src/bin.ts
831
+ var DEFAULT_BASE_URL = "https://getaura.sh/api";
832
+ function emitBanner(toolCount) {
833
+ const mode = resolveKeyMode(process.env.AURA_API_KEY);
834
+ const baseUrl = process.env.AURA_BASE_URL || DEFAULT_BASE_URL;
835
+ console.error(
836
+ `${SERVER_NAME} v${SERVER_VERSION} | mode=${mode} | baseUrl=${baseUrl} | tools=${toolCount}`
837
+ );
838
+ if (mode === "unconfigured") {
839
+ console.error(
840
+ `${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.`
841
+ );
842
+ } else if (mode === "invalid") {
843
+ console.error(
844
+ `${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.`
845
+ );
846
+ }
847
+ }
558
848
  async function main() {
559
849
  const server = createServer();
850
+ emitBanner(getRegisteredToolNames(server).length);
560
851
  const transport = new StdioServerTransport();
561
852
  await server.connect(transport);
562
853
  }
@@ -564,5 +855,3 @@ main().catch((err) => {
564
855
  console.error("aura-mcp: fatal error:", err);
565
856
  process.exit(1);
566
857
  });
567
- //# sourceMappingURL=bin.mjs.map
568
- //# sourceMappingURL=bin.mjs.map
package/dist/index.d.mts CHANGED
@@ -1,6 +1,66 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { AuraClient } from '@aura-payments/sdk';
3
3
 
4
+ /**
5
+ * API key parsing and validation for the Aura MCP server.
6
+ *
7
+ * Deliberately dependency-free: this module imports nothing (not even the
8
+ * Aura SDK) so it can be unit-tested in isolation and imported from the
9
+ * startup banner without pulling the whole client graph into scope.
10
+ *
11
+ * SECURITY: no function here ever returns the full key. `maskApiKey` reveals
12
+ * at most half of the key (capped at 8 characters) so a malformed-key error
13
+ * can identify WHICH key is wrong without ever writing the secret into a log,
14
+ * a stderr banner, or an MCP tool result.
15
+ */
16
+ /**
17
+ * Recognized API key prefixes.
18
+ *
19
+ * `sk_` = secret key (server-side), `pk_` = publishable key. The middle
20
+ * segment selects the environment the key is bound to; a key issued for one
21
+ * environment is never valid in the other.
22
+ */
23
+ declare const API_KEY_PREFIXES: readonly ["sk_test_", "sk_live_", "pk_test_", "pk_live_"];
24
+ type AuraApiKeyPrefix = (typeof API_KEY_PREFIXES)[number];
25
+ /** Which Aura environment a key is bound to. */
26
+ type AuraKeyMode = 'test' | 'live';
27
+ interface ParsedAuraApiKey {
28
+ /** The matched prefix, e.g. `pk_test_`. Safe to log — carries no secret. */
29
+ prefix: AuraApiKeyPrefix;
30
+ /** Resolved environment, derived from the prefix. */
31
+ mode: AuraKeyMode;
32
+ }
33
+ /**
34
+ * Redact a key for display.
35
+ *
36
+ * Reveals `min(8, floor(length / 2))` leading characters, so the result can
37
+ * never contain the whole key for any input of length >= 1. The length is
38
+ * appended because "wrong length" is the most common misconfiguration (a
39
+ * truncated paste) and it leaks nothing.
40
+ */
41
+ declare function maskApiKey(apiKey: string): string;
42
+ /**
43
+ * Parse and validate `AURA_API_KEY`.
44
+ *
45
+ * @throws {MissingAuraApiKeyError} when the key is absent or blank.
46
+ * @throws {MalformedAuraApiKeyError} when the key carries no recognized prefix.
47
+ */
48
+ declare function parseApiKey(apiKey: string | undefined): ParsedAuraApiKey;
49
+ /**
50
+ * Resolve the environment for display without throwing.
51
+ *
52
+ * The startup banner must never crash the process — an unusable key still has
53
+ * to produce a running server whose FIRST TOOL CALL returns a clear error, so
54
+ * the MCP host surfaces something actionable instead of a dead subprocess.
55
+ */
56
+ declare function resolveKeyMode(apiKey: string | undefined): AuraKeyMode | 'unconfigured' | 'invalid';
57
+ declare class MissingAuraApiKeyError extends Error {
58
+ constructor();
59
+ }
60
+ declare class MalformedAuraApiKeyError extends Error {
61
+ constructor(apiKey: string);
62
+ }
63
+
4
64
  /**
5
65
  * Aura SDK client factory for the MCP server.
6
66
  *
@@ -9,20 +69,22 @@ import { AuraClient } from '@aura-payments/sdk';
9
69
  * the `env` field in mcp.json). The client is created lazily on first tool
10
70
  * invocation so missing env doesn't crash the server during the MCP handshake
11
71
  * — instead, the FIRST tool call returns a clear error the LLM can act on.
72
+ *
73
+ * The key is validated for BOTH presence and format before it is ever put on
74
+ * the wire. A malformed key would otherwise fail as an opaque 401 from the
75
+ * platform, which is indistinguishable from a revoked key — the local check
76
+ * turns that into an actionable configuration error.
12
77
  */
13
78
 
14
79
  declare function getClient(): AuraClient;
15
- declare class MissingAuraApiKeyError extends Error {
16
- constructor();
17
- }
18
80
 
19
81
  /**
20
82
  * Aura Payments MCP Server — programmatic entry point.
21
83
  *
22
- * The MCP server exposes Aura's escrow, agent, mandate, and wallet primitives
23
- * as Model Context Protocol tools and resources, so AI agents (Claude,
24
- * Cursor, Continue, custom orchestrators) can drive Aura natively over
25
- * stdio without writing HTTP boilerplate.
84
+ * The MCP server exposes Aura's escrow, agent, mandate, wallet, and webhook
85
+ * primitives as Model Context Protocol tools and resources, so AI agents
86
+ * (Claude, Cursor, Continue, custom orchestrators) can drive Aura natively
87
+ * over stdio without writing HTTP boilerplate.
26
88
  *
27
89
  * This file exports `createServer()` for programmatic use (tests, custom
28
90
  * transports, embedding in other MCP hosts). For the standalone binary,
@@ -33,10 +95,17 @@ declare class MissingAuraApiKeyError extends Error {
33
95
  * shape, idempotency, retries, and error mapping.
34
96
  */
35
97
 
98
+ declare const SERVER_NAME = "aura-payments";
99
+ declare const SERVER_VERSION = "1.1.0";
36
100
  interface CreateServerOptions {
37
101
  name?: string;
38
102
  version?: string;
39
103
  }
104
+ /**
105
+ * Names of the tools registered on `server`, in registration order.
106
+ * Returns an empty array for a server this module did not build.
107
+ */
108
+ declare function getRegisteredToolNames(server: McpServer): readonly string[];
40
109
  /**
41
110
  * Build a fully-configured MCP server with all Aura tools and resources
42
111
  * registered. The caller is responsible for connecting it to a transport
@@ -44,4 +113,4 @@ interface CreateServerOptions {
44
113
  */
45
114
  declare function createServer(options?: CreateServerOptions): McpServer;
46
115
 
47
- export { type CreateServerOptions, MissingAuraApiKeyError, createServer, getClient };
116
+ export { API_KEY_PREFIXES, type AuraKeyMode, type CreateServerOptions, MalformedAuraApiKeyError, MissingAuraApiKeyError, type ParsedAuraApiKey, SERVER_NAME, SERVER_VERSION, createServer, getClient, getRegisteredToolNames, maskApiKey, parseApiKey, resolveKeyMode };