@aura-payments/mcp-server 1.0.1 → 1.2.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 +593 -18
- package/dist/bin.mjs +593 -18
- package/dist/index.d.mts +77 -8
- package/dist/index.d.ts +77 -8
- package/dist/index.js +586 -18
- package/dist/index.mjs +579 -19
- package/package.json +6 -4
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
|
-
|
|
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,55 @@ 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
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/resources/insights.ts
|
|
139
|
+
function registerInsightsResources(server, clientFactory = getClient) {
|
|
140
|
+
server.resource("insights-overview", "aura://insights/overview", async (uri) => {
|
|
141
|
+
const overview = await clientFactory().insights.overview();
|
|
142
|
+
return {
|
|
143
|
+
contents: [
|
|
144
|
+
{
|
|
145
|
+
uri: uri.href,
|
|
146
|
+
mimeType: "application/json",
|
|
147
|
+
text: JSON.stringify(overview, null, 2)
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
}
|
|
67
153
|
function registerMandateResources(server) {
|
|
68
154
|
server.resource(
|
|
69
155
|
"mandates-pending",
|
|
@@ -100,6 +186,108 @@ function registerMandateResources(server) {
|
|
|
100
186
|
}
|
|
101
187
|
);
|
|
102
188
|
}
|
|
189
|
+
var COLLECTION_LIMIT2 = 50;
|
|
190
|
+
function registerWalletResources(server) {
|
|
191
|
+
server.resource("wallets", "aura://wallets", async (uri) => {
|
|
192
|
+
const client = getClient();
|
|
193
|
+
const page = await client.wallets.list({ limit: COLLECTION_LIMIT2 });
|
|
194
|
+
return {
|
|
195
|
+
contents: [
|
|
196
|
+
{
|
|
197
|
+
uri: uri.href,
|
|
198
|
+
mimeType: "application/json",
|
|
199
|
+
text: JSON.stringify(page, null, 2)
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
server.resource(
|
|
205
|
+
"wallet",
|
|
206
|
+
new ResourceTemplate("aura://wallets/{id}", { list: void 0 }),
|
|
207
|
+
async (uri, { id }) => {
|
|
208
|
+
const client = getClient();
|
|
209
|
+
const wallet = await client.wallets.get(String(id));
|
|
210
|
+
return {
|
|
211
|
+
contents: [
|
|
212
|
+
{
|
|
213
|
+
uri: uri.href,
|
|
214
|
+
mimeType: "application/json",
|
|
215
|
+
text: JSON.stringify(wallet, null, 2)
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
server.resource(
|
|
222
|
+
"wallet-balance",
|
|
223
|
+
new ResourceTemplate("aura://wallets/{id}/balance", { list: void 0 }),
|
|
224
|
+
async (uri, { id }) => {
|
|
225
|
+
const client = getClient();
|
|
226
|
+
const balance = await client.wallets.getBalance(String(id));
|
|
227
|
+
return {
|
|
228
|
+
contents: [
|
|
229
|
+
{
|
|
230
|
+
uri: uri.href,
|
|
231
|
+
mimeType: "application/json",
|
|
232
|
+
text: JSON.stringify(balance, null, 2)
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
var COLLECTION_LIMIT3 = 50;
|
|
240
|
+
var WithdrawalIdSchema = z.string().uuid();
|
|
241
|
+
function registerWithdrawalResources(server, clientFactory = getClient) {
|
|
242
|
+
server.resource("withdrawals", "aura://withdrawals", async (uri) => {
|
|
243
|
+
const page = await clientFactory().withdrawals.list({ limit: COLLECTION_LIMIT3 });
|
|
244
|
+
return {
|
|
245
|
+
contents: [
|
|
246
|
+
{
|
|
247
|
+
uri: uri.href,
|
|
248
|
+
mimeType: "application/json",
|
|
249
|
+
text: JSON.stringify(page, null, 2)
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
server.resource(
|
|
255
|
+
"withdrawal",
|
|
256
|
+
new ResourceTemplate("aura://withdrawals/{id}", { list: void 0 }),
|
|
257
|
+
async (uri, { id }) => {
|
|
258
|
+
const parsed = WithdrawalIdSchema.safeParse(Array.isArray(id) ? id[0] : id);
|
|
259
|
+
if (!parsed.success) {
|
|
260
|
+
throw new Error(`Invalid withdrawal id in ${uri.href}: expected a UUID`);
|
|
261
|
+
}
|
|
262
|
+
const withdrawal = await clientFactory().withdrawals.get(parsed.data);
|
|
263
|
+
return {
|
|
264
|
+
contents: [
|
|
265
|
+
{
|
|
266
|
+
uri: uri.href,
|
|
267
|
+
mimeType: "application/json",
|
|
268
|
+
text: JSON.stringify(withdrawal, null, 2)
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/annotations.ts
|
|
277
|
+
function readOnly(title) {
|
|
278
|
+
return { title, readOnlyHint: true };
|
|
279
|
+
}
|
|
280
|
+
function write(title, idempotent = false) {
|
|
281
|
+
return {
|
|
282
|
+
title,
|
|
283
|
+
readOnlyHint: false,
|
|
284
|
+
destructiveHint: false,
|
|
285
|
+
...idempotent ? { idempotentHint: true } : {}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function destructive(title) {
|
|
289
|
+
return { title, readOnlyHint: false, destructiveHint: true };
|
|
290
|
+
}
|
|
103
291
|
async function withToolErrorHandling(body, successFormatter = defaultJsonFormatter) {
|
|
104
292
|
try {
|
|
105
293
|
const result = await body();
|
|
@@ -114,7 +302,7 @@ function defaultJsonFormatter(data) {
|
|
|
114
302
|
return JSON.stringify(data, null, 2);
|
|
115
303
|
}
|
|
116
304
|
function formatError(err) {
|
|
117
|
-
if (err instanceof MissingAuraApiKeyError) {
|
|
305
|
+
if (err instanceof MissingAuraApiKeyError || err instanceof MalformedAuraApiKeyError) {
|
|
118
306
|
return errorResult("CONFIG_ERROR", err.message);
|
|
119
307
|
}
|
|
120
308
|
if (err instanceof AuraAuthenticationError) {
|
|
@@ -202,6 +390,8 @@ function registerAgentTools(server) {
|
|
|
202
390
|
autoApproveBelow: z.string().optional()
|
|
203
391
|
}).optional()
|
|
204
392
|
},
|
|
393
|
+
// Additive: provisions a new agent and its wallet. No funds move.
|
|
394
|
+
write("Create Agent"),
|
|
205
395
|
async (args) => withToolErrorHandling(async () => {
|
|
206
396
|
const client = getClient();
|
|
207
397
|
return client.agents.create(args);
|
|
@@ -216,6 +406,7 @@ function registerAgentTools(server) {
|
|
|
216
406
|
limit: z.number().int().min(1).max(200).optional(),
|
|
217
407
|
offset: z.number().int().min(0).optional()
|
|
218
408
|
},
|
|
409
|
+
readOnly("List Agents"),
|
|
219
410
|
async (args) => withToolErrorHandling(async () => {
|
|
220
411
|
const client = getClient();
|
|
221
412
|
return client.agents.list(args);
|
|
@@ -227,6 +418,7 @@ function registerAgentTools(server) {
|
|
|
227
418
|
{
|
|
228
419
|
agentId: z.string().uuid()
|
|
229
420
|
},
|
|
421
|
+
readOnly("Get Agent"),
|
|
230
422
|
async ({ agentId }) => withToolErrorHandling(async () => {
|
|
231
423
|
const client = getClient();
|
|
232
424
|
return client.agents.get(agentId);
|
|
@@ -238,6 +430,7 @@ function registerAgentTools(server) {
|
|
|
238
430
|
{
|
|
239
431
|
agentId: z.string().uuid()
|
|
240
432
|
},
|
|
433
|
+
readOnly("Get Agent Balance"),
|
|
241
434
|
async ({ agentId }) => withToolErrorHandling(async () => {
|
|
242
435
|
const client = getClient();
|
|
243
436
|
return client.agents.getBalance(agentId);
|
|
@@ -250,6 +443,8 @@ function registerAgentTools(server) {
|
|
|
250
443
|
agentId: z.string().uuid(),
|
|
251
444
|
reason: z.string().max(500).optional().describe("Surfaced in activity event metadata for audit trail")
|
|
252
445
|
},
|
|
446
|
+
// Halts an existing agent AND cascade-rejects its in-flight mandates.
|
|
447
|
+
destructive("Freeze Agent (kill switch)"),
|
|
253
448
|
async (args) => withToolErrorHandling(async () => {
|
|
254
449
|
const client = getClient();
|
|
255
450
|
return client.agents.freeze(args);
|
|
@@ -261,6 +456,8 @@ function registerAgentTools(server) {
|
|
|
261
456
|
{
|
|
262
457
|
agentId: z.string().uuid()
|
|
263
458
|
},
|
|
459
|
+
// Re-enables autonomous spending on an existing agent.
|
|
460
|
+
destructive("Unfreeze Agent (resumes autonomous spending)"),
|
|
264
461
|
async (args) => withToolErrorHandling(async () => {
|
|
265
462
|
const client = getClient();
|
|
266
463
|
return client.agents.unfreeze(args);
|
|
@@ -277,11 +474,24 @@ function registerAgentTools(server) {
|
|
|
277
474
|
reasoning: z.string().optional(),
|
|
278
475
|
metadata: z.record(z.unknown()).optional()
|
|
279
476
|
},
|
|
477
|
+
// NOT read-only despite being a dry run: when the decision requires human
|
|
478
|
+
// approval the engine persists a new mandate row. Additive, never
|
|
479
|
+
// destructive — it cannot execute the transaction it evaluates.
|
|
480
|
+
write("Evaluate Policy (dry run, may create a mandate)"),
|
|
280
481
|
async (args) => withToolErrorHandling(async () => {
|
|
281
482
|
const client = getClient();
|
|
282
483
|
return client.agents.evaluate(args);
|
|
283
484
|
})
|
|
284
485
|
);
|
|
486
|
+
return [
|
|
487
|
+
"aura_create_agent",
|
|
488
|
+
"aura_list_agents",
|
|
489
|
+
"aura_get_agent",
|
|
490
|
+
"aura_get_agent_balance",
|
|
491
|
+
"aura_freeze_agent",
|
|
492
|
+
"aura_unfreeze_agent",
|
|
493
|
+
"aura_evaluate_policy"
|
|
494
|
+
];
|
|
285
495
|
}
|
|
286
496
|
var ChainSchema2 = z.enum(["ARC", "ARB", "BASE", "ETH", "MATIC"]);
|
|
287
497
|
var SplitInputSchema = z.object({
|
|
@@ -318,6 +528,8 @@ function registerEscrowTools(server) {
|
|
|
318
528
|
chain: ChainSchema2.optional().describe("Target chain. Defaults to ARC"),
|
|
319
529
|
splits: z.array(SplitInputSchema).min(1).describe("Payment splits. Percentages must sum to 100.")
|
|
320
530
|
},
|
|
531
|
+
// Additive: creates a new escrow record. No funds move until `fund`.
|
|
532
|
+
write("Create Escrow"),
|
|
321
533
|
async (args) => withToolErrorHandling(async () => {
|
|
322
534
|
const client = getClient();
|
|
323
535
|
return client.escrows.create(args);
|
|
@@ -329,6 +541,7 @@ function registerEscrowTools(server) {
|
|
|
329
541
|
{
|
|
330
542
|
escrowId: z.string().uuid()
|
|
331
543
|
},
|
|
544
|
+
readOnly("Get Escrow"),
|
|
332
545
|
async ({ escrowId }) => withToolErrorHandling(async () => {
|
|
333
546
|
const client = getClient();
|
|
334
547
|
return client.escrows.get(escrowId);
|
|
@@ -353,6 +566,7 @@ function registerEscrowTools(server) {
|
|
|
353
566
|
limit: z.number().int().min(1).max(200).optional(),
|
|
354
567
|
offset: z.number().int().min(0).optional()
|
|
355
568
|
},
|
|
569
|
+
readOnly("List Escrows"),
|
|
356
570
|
async (args) => withToolErrorHandling(async () => {
|
|
357
571
|
const client = getClient();
|
|
358
572
|
return client.escrows.list(args);
|
|
@@ -367,6 +581,8 @@ function registerEscrowTools(server) {
|
|
|
367
581
|
amount: z.string().describe("USDC amount to fund (whole-number string)"),
|
|
368
582
|
correlationId: z.string().optional()
|
|
369
583
|
},
|
|
584
|
+
// Moves USDC out of the buyer wallet.
|
|
585
|
+
destructive("Fund Escrow (moves USDC)"),
|
|
370
586
|
async (args) => withToolErrorHandling(async () => {
|
|
371
587
|
const client = getClient();
|
|
372
588
|
return client.escrows.fund(args);
|
|
@@ -381,6 +597,8 @@ function registerEscrowTools(server) {
|
|
|
381
597
|
reason: z.string().optional(),
|
|
382
598
|
correlationId: z.string().optional()
|
|
383
599
|
},
|
|
600
|
+
// Irreversible payout to the split recipients.
|
|
601
|
+
destructive("Release Escrow (pays out USDC)"),
|
|
384
602
|
async (args) => withToolErrorHandling(async () => {
|
|
385
603
|
const client = getClient();
|
|
386
604
|
return client.escrows.release(args);
|
|
@@ -394,6 +612,8 @@ function registerEscrowTools(server) {
|
|
|
394
612
|
reason: z.string().min(10),
|
|
395
613
|
actorId: z.string().uuid()
|
|
396
614
|
},
|
|
615
|
+
// Irreversible return of funds to the buyer.
|
|
616
|
+
destructive("Refund Escrow (returns USDC to buyer)"),
|
|
397
617
|
async (args) => withToolErrorHandling(async () => {
|
|
398
618
|
const client = getClient();
|
|
399
619
|
return client.escrows.refund(args);
|
|
@@ -413,11 +633,134 @@ function registerEscrowTools(server) {
|
|
|
413
633
|
waitForDeploymentMs: z.number().int().min(0).optional().describe("Max ms to wait for on-chain deployment. Default 60000."),
|
|
414
634
|
waitForFundingMs: z.number().int().min(0).optional().describe("Max ms to wait for funds to settle (funded/locked) before releasing. Default 60000.")
|
|
415
635
|
},
|
|
636
|
+
// Composes create -> fund -> release: money moves and is paid out.
|
|
637
|
+
destructive("Receive and Split (moves and pays out USDC)"),
|
|
416
638
|
async (args) => withToolErrorHandling(async () => {
|
|
417
639
|
const client = getClient();
|
|
418
640
|
return client.escrows.receiveAndSplit(args);
|
|
419
641
|
})
|
|
420
642
|
);
|
|
643
|
+
return [
|
|
644
|
+
"aura_create_escrow",
|
|
645
|
+
"aura_get_escrow",
|
|
646
|
+
"aura_list_escrows",
|
|
647
|
+
"aura_fund_escrow",
|
|
648
|
+
"aura_release_escrow",
|
|
649
|
+
"aura_refund_escrow",
|
|
650
|
+
"aura_receive_and_split"
|
|
651
|
+
];
|
|
652
|
+
}
|
|
653
|
+
function validationError(error) {
|
|
654
|
+
const detail = error.issues.map((issue) => `${issue.path.join(".") || "(input)"}: ${issue.message}`).join("; ");
|
|
655
|
+
return toolError("VALIDATION_ERROR", detail);
|
|
656
|
+
}
|
|
657
|
+
function addCustomRangeIssues(ctx, field, selector, start, end) {
|
|
658
|
+
if (selector === "custom" && (start === void 0 || end === void 0)) {
|
|
659
|
+
ctx.addIssue({
|
|
660
|
+
code: z.ZodIssueCode.custom,
|
|
661
|
+
path: [start === void 0 ? "start" : "end"],
|
|
662
|
+
message: `start and end are both required when ${field} is 'custom'`
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
if (start !== void 0 && end !== void 0 && Date.parse(start) > Date.parse(end)) {
|
|
666
|
+
ctx.addIssue({
|
|
667
|
+
code: z.ZodIssueCode.custom,
|
|
668
|
+
path: ["end"],
|
|
669
|
+
message: "end must be at or after start"
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/tools/insights.ts
|
|
675
|
+
var PeriodSchema = z.enum(["today", "7d", "30d", "90d", "custom"]);
|
|
676
|
+
var DateTimeSchema = z.string().datetime();
|
|
677
|
+
var overviewShape = {
|
|
678
|
+
period: PeriodSchema.optional().describe(
|
|
679
|
+
"Window to report on (default 7d). 'custom' requires start and end."
|
|
680
|
+
),
|
|
681
|
+
start: DateTimeSchema.optional().describe("ISO 8601 datetime; required when period is custom"),
|
|
682
|
+
end: DateTimeSchema.optional().describe("ISO 8601 datetime; required when period is custom")
|
|
683
|
+
};
|
|
684
|
+
var InsightsOverviewInput = z.object(overviewShape).superRefine(
|
|
685
|
+
(value, ctx) => addCustomRangeIssues(ctx, "period", value.period, value.start, value.end)
|
|
686
|
+
);
|
|
687
|
+
var auditShape = {
|
|
688
|
+
action: z.string().min(1).optional().describe("Audit action name, e.g. escrow.released"),
|
|
689
|
+
actor: z.enum(["user", "system", "api", "cron", "webhook"]).optional(),
|
|
690
|
+
resourceType: z.string().min(1).optional().describe("Resource kind, e.g. escrow"),
|
|
691
|
+
// `.min(1)`, not `.uuid()`: the route accepts any string here
|
|
692
|
+
// (`resourceId: z.string().optional()`, insights/audit/route.ts:26) and the
|
|
693
|
+
// audit log records ids from several subsystems, not all of them UUIDs.
|
|
694
|
+
resourceId: z.string().min(1).optional().describe("Id of the audited resource"),
|
|
695
|
+
range: PeriodSchema.optional().describe(
|
|
696
|
+
"Window to query (default 30d). 'custom' requires start and end."
|
|
697
|
+
),
|
|
698
|
+
start: DateTimeSchema.optional().describe("ISO 8601 datetime; required when range is custom"),
|
|
699
|
+
end: DateTimeSchema.optional().describe("ISO 8601 datetime; required when range is custom"),
|
|
700
|
+
page: z.number().int().min(1).optional().describe("1-based page number (default 1)"),
|
|
701
|
+
limit: z.number().int().min(1).max(100).optional().describe("Page size, 1-100 (default 50)")
|
|
702
|
+
};
|
|
703
|
+
var InsightsAuditInput = z.object(auditShape).superRefine(
|
|
704
|
+
(value, ctx) => addCustomRangeIssues(ctx, "range", value.range, value.start, value.end)
|
|
705
|
+
);
|
|
706
|
+
function registerInsightsTools(server, clientFactory = getClient) {
|
|
707
|
+
server.tool(
|
|
708
|
+
"aura_insights_overview",
|
|
709
|
+
"Revenue, transaction count, active escrows and commissions for a period, each with its change against the previous period, plus sparkline and success-rate charts.",
|
|
710
|
+
overviewShape,
|
|
711
|
+
readOnly("Insights Overview"),
|
|
712
|
+
async (args) => {
|
|
713
|
+
const parsed = InsightsOverviewInput.safeParse(args);
|
|
714
|
+
if (!parsed.success) return validationError(parsed.error);
|
|
715
|
+
const { period, start, end } = parsed.data;
|
|
716
|
+
return withToolErrorHandling(
|
|
717
|
+
async () => clientFactory().insights.overview(period, start, end)
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
server.tool(
|
|
722
|
+
"aura_insights_forecast",
|
|
723
|
+
"Project a metric forward over a horizon using the requested algorithm. Requires a Growth or higher plan \u2014 lower tiers return feature_not_available.",
|
|
724
|
+
{
|
|
725
|
+
metric: z.enum([
|
|
726
|
+
"revenue",
|
|
727
|
+
"transaction_count",
|
|
728
|
+
"escrow_count",
|
|
729
|
+
"commission",
|
|
730
|
+
"avg_transaction_value"
|
|
731
|
+
]),
|
|
732
|
+
algorithm: z.enum([
|
|
733
|
+
"linear_regression",
|
|
734
|
+
"moving_average",
|
|
735
|
+
"exponential_smoothing",
|
|
736
|
+
"arima",
|
|
737
|
+
"prophet",
|
|
738
|
+
"ensemble"
|
|
739
|
+
]).optional(),
|
|
740
|
+
period: z.enum(["daily", "weekly", "monthly"]).optional().describe("Granularity of each projected point"),
|
|
741
|
+
horizon: z.number().int().min(1).max(365).optional().describe("Number of periods to project"),
|
|
742
|
+
// Known platform quirk: the route parses this with `z.coerce.boolean()`
|
|
743
|
+
// on a query string, where any non-empty value — including "false" —
|
|
744
|
+
// coerces to true. Passing false therefore does not currently suppress
|
|
745
|
+
// history; that is a platform-side fix, not one to paper over here.
|
|
746
|
+
includeHistory: z.boolean().optional().describe("Include the historical series alongside the projection"),
|
|
747
|
+
historyPeriods: z.number().int().min(1).max(365).optional()
|
|
748
|
+
},
|
|
749
|
+
readOnly("Insights Forecast"),
|
|
750
|
+
async (args) => withToolErrorHandling(async () => clientFactory().insights.forecast(args))
|
|
751
|
+
);
|
|
752
|
+
server.tool(
|
|
753
|
+
"aura_insights_audit",
|
|
754
|
+
"Query the account audit log by action, actor, resource and date range. Paginated, most recent first.",
|
|
755
|
+
auditShape,
|
|
756
|
+
readOnly("Insights Audit"),
|
|
757
|
+
async (args) => {
|
|
758
|
+
const parsed = InsightsAuditInput.safeParse(args);
|
|
759
|
+
if (!parsed.success) return validationError(parsed.error);
|
|
760
|
+
return withToolErrorHandling(async () => clientFactory().insights.audit(parsed.data));
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
return ["aura_insights_overview", "aura_insights_forecast", "aura_insights_audit"];
|
|
421
764
|
}
|
|
422
765
|
function registerMandateTools(server) {
|
|
423
766
|
server.tool(
|
|
@@ -429,6 +772,7 @@ function registerMandateTools(server) {
|
|
|
429
772
|
limit: z.number().int().min(1).max(200).optional(),
|
|
430
773
|
cursor: z.string().uuid().optional().describe("Last mandate id from the previous page")
|
|
431
774
|
},
|
|
775
|
+
readOnly("List Mandates (approval inbox)"),
|
|
432
776
|
async (args) => withToolErrorHandling(async () => {
|
|
433
777
|
const client = getClient();
|
|
434
778
|
return client.mandates.list(args);
|
|
@@ -440,6 +784,7 @@ function registerMandateTools(server) {
|
|
|
440
784
|
{
|
|
441
785
|
mandateId: z.string().uuid()
|
|
442
786
|
},
|
|
787
|
+
readOnly("Get Mandate"),
|
|
443
788
|
async ({ mandateId }) => withToolErrorHandling(async () => {
|
|
444
789
|
const client = getClient();
|
|
445
790
|
return client.mandates.get(mandateId);
|
|
@@ -454,6 +799,8 @@ function registerMandateTools(server) {
|
|
|
454
799
|
decisionSignature: z.string().min(16).describe("HMAC-SHA256 hex digest, computed by the caller"),
|
|
455
800
|
decidedAt: z.string().datetime().optional().describe('ISO-8601 timestamp the signature commits to. Defaults to "now" server-side.')
|
|
456
801
|
},
|
|
802
|
+
// Authorizes the agent's intended payment — the money-moving decision.
|
|
803
|
+
destructive("Approve Mandate (authorizes payment)"),
|
|
457
804
|
async (args) => withToolErrorHandling(async () => {
|
|
458
805
|
const client = getClient();
|
|
459
806
|
return client.mandates.approve(args);
|
|
@@ -469,11 +816,31 @@ function registerMandateTools(server) {
|
|
|
469
816
|
decidedAt: z.string().datetime().optional(),
|
|
470
817
|
reason: z.string().optional().describe("Human-readable reason logged to the activity feed")
|
|
471
818
|
},
|
|
819
|
+
// Terminal decision on an existing mandate — the agent cannot resume it.
|
|
820
|
+
destructive("Reject Mandate"),
|
|
472
821
|
async (args) => withToolErrorHandling(async () => {
|
|
473
822
|
const client = getClient();
|
|
474
823
|
return client.mandates.reject(args);
|
|
475
824
|
})
|
|
476
825
|
);
|
|
826
|
+
return [
|
|
827
|
+
"aura_list_mandates",
|
|
828
|
+
"aura_get_mandate",
|
|
829
|
+
"aura_approve_mandate",
|
|
830
|
+
"aura_reject_mandate"
|
|
831
|
+
];
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/tools/treasury.ts
|
|
835
|
+
function registerTreasuryTools(server, clientFactory = getClient) {
|
|
836
|
+
server.tool(
|
|
837
|
+
"aura_treasury_optimize",
|
|
838
|
+
"Analyze treasury balances, active escrows and pending payouts, and return an AI recommendation on how to rebalance. Read-only: it recommends, it does not move funds. Requires a Growth or higher plan \u2014 lower tiers return feature_not_available.",
|
|
839
|
+
{},
|
|
840
|
+
readOnly("Treasury Optimize"),
|
|
841
|
+
async () => withToolErrorHandling(async () => clientFactory().treasury.optimize())
|
|
842
|
+
);
|
|
843
|
+
return ["aura_treasury_optimize"];
|
|
477
844
|
}
|
|
478
845
|
var TestnetChainSchema = z.enum(["ARC", "ARB", "BASE"]);
|
|
479
846
|
function walletCreateEnabled() {
|
|
@@ -487,6 +854,7 @@ function registerWalletTools(server) {
|
|
|
487
854
|
{
|
|
488
855
|
walletId: z.string().uuid()
|
|
489
856
|
},
|
|
857
|
+
readOnly("Get Wallet Balance"),
|
|
490
858
|
async ({ walletId }) => withToolErrorHandling(async () => {
|
|
491
859
|
const client = getClient();
|
|
492
860
|
return client.wallets.getBalance(walletId);
|
|
@@ -510,6 +878,8 @@ function registerWalletTools(server) {
|
|
|
510
878
|
"tax"
|
|
511
879
|
]).optional().describe("Wallet owner type. Defaults to BUSINESS.")
|
|
512
880
|
},
|
|
881
|
+
// Additive and create-or-get: provisions a receive address, moves no money.
|
|
882
|
+
write("Create Wallet (receive address)", true),
|
|
513
883
|
async ({ entityId, chain, type }) => {
|
|
514
884
|
if (!walletCreateEnabled()) {
|
|
515
885
|
return toolError(
|
|
@@ -531,26 +901,216 @@ function registerWalletTools(server) {
|
|
|
531
901
|
token: z.string().optional().describe("Token to drip. Defaults to 'USDC'."),
|
|
532
902
|
chain: TestnetChainSchema.optional().describe("Override the wallet's chain.")
|
|
533
903
|
},
|
|
904
|
+
// Additive: testnet-only drip that adds play funds, destroys nothing.
|
|
905
|
+
write("Request Testnet Funds"),
|
|
534
906
|
async ({ walletId, token, chain }) => withToolErrorHandling(async () => {
|
|
535
907
|
const client = getClient();
|
|
536
908
|
return client.wallets.requestTestnetFunds(walletId, { token, chain });
|
|
537
909
|
})
|
|
538
910
|
);
|
|
911
|
+
return [
|
|
912
|
+
"aura_get_wallet_balance",
|
|
913
|
+
"aura_create_wallet",
|
|
914
|
+
"aura_request_testnet_funds"
|
|
915
|
+
];
|
|
916
|
+
}
|
|
917
|
+
var WebhookEventTypeSchema = z.enum([
|
|
918
|
+
"*",
|
|
919
|
+
"escrow.created",
|
|
920
|
+
"escrow.funded",
|
|
921
|
+
"escrow.locked",
|
|
922
|
+
"escrow.released",
|
|
923
|
+
"escrow.refunded",
|
|
924
|
+
"escrow.disputed",
|
|
925
|
+
"transfer.completed",
|
|
926
|
+
"transfer.failed",
|
|
927
|
+
"withdrawal.created",
|
|
928
|
+
"withdrawal.processing",
|
|
929
|
+
"withdrawal.completed",
|
|
930
|
+
"withdrawal.failed",
|
|
931
|
+
"account.kyc_approved",
|
|
932
|
+
"account.kyc_rejected",
|
|
933
|
+
"account.tier_upgraded",
|
|
934
|
+
"security.api_key_created",
|
|
935
|
+
"security.api_key_revoked"
|
|
936
|
+
]);
|
|
937
|
+
function registerWebhookTools(server) {
|
|
938
|
+
server.tool(
|
|
939
|
+
"aura_get_webhook_config",
|
|
940
|
+
"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.",
|
|
941
|
+
{},
|
|
942
|
+
readOnly("Get Webhook Config"),
|
|
943
|
+
async () => withToolErrorHandling(async () => {
|
|
944
|
+
const client = getClient();
|
|
945
|
+
return client.webhooks.getConfig();
|
|
946
|
+
})
|
|
947
|
+
);
|
|
948
|
+
server.tool(
|
|
949
|
+
"aura_set_webhook_config",
|
|
950
|
+
"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.",
|
|
951
|
+
{
|
|
952
|
+
url: z.string().url().describe("HTTPS endpoint that will receive event POSTs"),
|
|
953
|
+
events: z.array(WebhookEventTypeSchema).min(1).optional().describe(
|
|
954
|
+
"Event types to subscribe to. Omit to subscribe to all events, or pass ['*'] explicitly."
|
|
955
|
+
),
|
|
956
|
+
description: z.string().optional().describe("Optional human-readable label")
|
|
957
|
+
},
|
|
958
|
+
destructive("Set Webhook Config (replaces existing)"),
|
|
959
|
+
async (args) => withToolErrorHandling(async () => {
|
|
960
|
+
const client = getClient();
|
|
961
|
+
return client.webhooks.configure(args);
|
|
962
|
+
})
|
|
963
|
+
);
|
|
964
|
+
server.tool(
|
|
965
|
+
"aura_delete_webhook_config",
|
|
966
|
+
"Delete the account webhook configuration. Event delivery stops immediately and the signing secret is invalidated \u2014 reconfiguring issues a new secret.",
|
|
967
|
+
{},
|
|
968
|
+
destructive("Delete Webhook Config"),
|
|
969
|
+
async () => withToolErrorHandling(async () => {
|
|
970
|
+
const client = getClient();
|
|
971
|
+
return client.webhooks.delete();
|
|
972
|
+
})
|
|
973
|
+
);
|
|
974
|
+
return [
|
|
975
|
+
"aura_get_webhook_config",
|
|
976
|
+
"aura_set_webhook_config",
|
|
977
|
+
"aura_delete_webhook_config"
|
|
978
|
+
];
|
|
979
|
+
}
|
|
980
|
+
var ChainSchema3 = z.enum(["ARC", "ETH", "BASE", "ARB", "MATIC"]);
|
|
981
|
+
var AmountSchema = z.string().regex(
|
|
982
|
+
/^\d+(\.\d{1,6})?$/,
|
|
983
|
+
'Amount must be a number string with up to 6 decimal places, e.g. "100.00"'
|
|
984
|
+
);
|
|
985
|
+
var EvmAddressSchema = z.string().regex(
|
|
986
|
+
/^0x[a-fA-F0-9]{40}$/,
|
|
987
|
+
"Must be an EVM address: 0x followed by 40 hexadecimal characters"
|
|
988
|
+
);
|
|
989
|
+
var ExchangeSchema = z.enum(["binance", "coinbase", "kraken", "other"]);
|
|
990
|
+
var FeeConfigSchema = z.enum(["FREE", "GAS_ONLY", "STANDARD"]);
|
|
991
|
+
var StatusSchema = z.enum(["pending", "submitted", "confirmed", "failed", "cancelled"]);
|
|
992
|
+
var WithdrawalIdSchema2 = z.string().uuid("Withdrawal id must be a UUID");
|
|
993
|
+
var estimateShape = {
|
|
994
|
+
// `.min(1)`, not `.uuid()`: the route accepts any non-empty string here
|
|
995
|
+
// (`walletId: z.string().min(1)`, estimate/route.ts:56). Requiring a UUID
|
|
996
|
+
// would make this tool reject wallet ids the platform itself accepts.
|
|
997
|
+
walletId: z.string().min(1).describe("Source wallet id to debit"),
|
|
998
|
+
amount: AmountSchema.describe('USDC amount as a string, e.g. "100.00"'),
|
|
999
|
+
destinationType: z.enum(["exchange", "wallet"]).describe("Where the funds land. 'exchange' also requires `exchange`."),
|
|
1000
|
+
destinationAddress: EvmAddressSchema.describe("Destination EVM address"),
|
|
1001
|
+
chain: ChainSchema3.describe("Chain the funds are delivered on"),
|
|
1002
|
+
exchange: ExchangeSchema.optional().describe(
|
|
1003
|
+
"Required when destinationType is 'exchange'"
|
|
1004
|
+
),
|
|
1005
|
+
sourceChain: ChainSchema3.optional().describe(
|
|
1006
|
+
"Chain the funds start on. Differs from `chain` for a bridged withdrawal; defaults to ARC."
|
|
1007
|
+
),
|
|
1008
|
+
feeConfig: FeeConfigSchema.optional()
|
|
1009
|
+
};
|
|
1010
|
+
var EstimateWithdrawalInput = z.object(estimateShape).superRefine((value, ctx) => {
|
|
1011
|
+
if (value.destinationType === "exchange" && value.exchange === void 0) {
|
|
1012
|
+
ctx.addIssue({
|
|
1013
|
+
code: z.ZodIssueCode.custom,
|
|
1014
|
+
path: ["exchange"],
|
|
1015
|
+
message: "exchange is required when destinationType is 'exchange'"
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
function registerWithdrawalTools(server, clientFactory = getClient) {
|
|
1020
|
+
server.tool(
|
|
1021
|
+
"aura_get_withdrawal",
|
|
1022
|
+
"Get a single withdrawal by id: status, amount, fees, destination, chain and transaction hash.",
|
|
1023
|
+
{ withdrawalId: WithdrawalIdSchema2.describe("The withdrawal id (UUID)") },
|
|
1024
|
+
readOnly("Get Withdrawal"),
|
|
1025
|
+
async ({ withdrawalId }) => withToolErrorHandling(async () => clientFactory().withdrawals.get(withdrawalId))
|
|
1026
|
+
);
|
|
1027
|
+
server.tool(
|
|
1028
|
+
"aura_list_withdrawals",
|
|
1029
|
+
"List withdrawals for the account, most recent first. Filter by status, chain and date range.",
|
|
1030
|
+
{
|
|
1031
|
+
limit: z.number().int().min(1).max(100).optional().describe("Page size, 1-100 (default 50)"),
|
|
1032
|
+
offset: z.number().int().min(0).optional().describe("Rows to skip (default 0)"),
|
|
1033
|
+
status: StatusSchema.optional(),
|
|
1034
|
+
chain: ChainSchema3.optional(),
|
|
1035
|
+
fromDate: z.string().datetime().optional().describe("ISO 8601 datetime, inclusive lower bound"),
|
|
1036
|
+
toDate: z.string().datetime().optional().describe("ISO 8601 datetime, inclusive upper bound")
|
|
1037
|
+
},
|
|
1038
|
+
readOnly("List Withdrawals"),
|
|
1039
|
+
async (args) => withToolErrorHandling(async () => clientFactory().withdrawals.list(args))
|
|
1040
|
+
);
|
|
1041
|
+
server.tool(
|
|
1042
|
+
"aura_estimate_withdrawal",
|
|
1043
|
+
"Preview the fees, net amount and confirmation time for a withdrawal WITHOUT creating it. Moves no funds. Use this before quoting a payout to a user.",
|
|
1044
|
+
estimateShape,
|
|
1045
|
+
readOnly("Estimate Withdrawal"),
|
|
1046
|
+
async (args) => {
|
|
1047
|
+
const parsed = EstimateWithdrawalInput.safeParse(args);
|
|
1048
|
+
if (!parsed.success) return validationError(parsed.error);
|
|
1049
|
+
return withToolErrorHandling(
|
|
1050
|
+
async () => clientFactory().withdrawals.estimate(parsed.data)
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
);
|
|
1054
|
+
server.tool(
|
|
1055
|
+
"aura_get_withdrawal_limits",
|
|
1056
|
+
"Get the account KYC-tier withdrawal limits and how much of the daily and monthly allowance is still available.",
|
|
1057
|
+
{},
|
|
1058
|
+
readOnly("Get Withdrawal Limits"),
|
|
1059
|
+
async () => withToolErrorHandling(async () => clientFactory().withdrawals.limits())
|
|
1060
|
+
);
|
|
1061
|
+
server.tool(
|
|
1062
|
+
"aura_validate_withdrawal_address",
|
|
1063
|
+
"Check that a destination address is well-formed and usable on a chain, and preview the fee when an amount is given. Moves no funds.",
|
|
1064
|
+
{
|
|
1065
|
+
address: EvmAddressSchema.describe("Destination EVM address to check"),
|
|
1066
|
+
chain: ChainSchema3,
|
|
1067
|
+
amount: AmountSchema.optional().describe(
|
|
1068
|
+
"Optional \u2014 when supplied the response includes a fee preview"
|
|
1069
|
+
),
|
|
1070
|
+
feeConfig: FeeConfigSchema.optional()
|
|
1071
|
+
},
|
|
1072
|
+
readOnly("Validate Withdrawal Address"),
|
|
1073
|
+
async (args) => withToolErrorHandling(async () => clientFactory().withdrawals.validate(args))
|
|
1074
|
+
);
|
|
1075
|
+
return [
|
|
1076
|
+
"aura_get_withdrawal",
|
|
1077
|
+
"aura_list_withdrawals",
|
|
1078
|
+
"aura_estimate_withdrawal",
|
|
1079
|
+
"aura_get_withdrawal_limits",
|
|
1080
|
+
"aura_validate_withdrawal_address"
|
|
1081
|
+
];
|
|
539
1082
|
}
|
|
540
1083
|
|
|
541
1084
|
// src/index.ts
|
|
1085
|
+
var SERVER_NAME = "aura-payments";
|
|
1086
|
+
var SERVER_VERSION = "1.2.0";
|
|
1087
|
+
var toolManifests = /* @__PURE__ */ new WeakMap();
|
|
1088
|
+
function getRegisteredToolNames(server) {
|
|
1089
|
+
return toolManifests.get(server) ?? [];
|
|
1090
|
+
}
|
|
542
1091
|
function createServer(options = {}) {
|
|
543
1092
|
const server = new McpServer({
|
|
544
|
-
name: options.name ??
|
|
545
|
-
version: options.version ??
|
|
1093
|
+
name: options.name ?? SERVER_NAME,
|
|
1094
|
+
version: options.version ?? SERVER_VERSION
|
|
546
1095
|
});
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
1096
|
+
const toolNames = [
|
|
1097
|
+
...registerEscrowTools(server),
|
|
1098
|
+
...registerAgentTools(server),
|
|
1099
|
+
...registerMandateTools(server),
|
|
1100
|
+
...registerWalletTools(server),
|
|
1101
|
+
...registerWebhookTools(server),
|
|
1102
|
+
...registerWithdrawalTools(server),
|
|
1103
|
+
...registerTreasuryTools(server),
|
|
1104
|
+
...registerInsightsTools(server)
|
|
1105
|
+
];
|
|
1106
|
+
toolManifests.set(server, Object.freeze(toolNames));
|
|
551
1107
|
registerAgentResources(server);
|
|
552
1108
|
registerMandateResources(server);
|
|
1109
|
+
registerEscrowResources(server);
|
|
1110
|
+
registerWalletResources(server);
|
|
1111
|
+
registerWithdrawalResources(server);
|
|
1112
|
+
registerInsightsResources(server);
|
|
553
1113
|
return server;
|
|
554
1114
|
}
|
|
555
1115
|
|
|
556
|
-
export { MissingAuraApiKeyError, createServer, getClient };
|
|
1116
|
+
export { API_KEY_PREFIXES, MalformedAuraApiKeyError, MissingAuraApiKeyError, SERVER_NAME, SERVER_VERSION, createServer, getClient, getRegisteredToolNames, maskApiKey, parseApiKey, resolveKeyMode };
|