@aura-payments/mcp-server 1.0.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/README.md +121 -0
- package/dist/bin.js +570 -0
- package/dist/bin.js.map +1 -0
- package/dist/bin.mjs +568 -0
- package/dist/bin.mjs.map +1 -0
- package/dist/index.d.mts +47 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +562 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +558 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# `@aura-payments/mcp-server`
|
|
2
|
+
|
|
3
|
+
Model Context Protocol server for Aura Payments. Drop it into Claude
|
|
4
|
+
Desktop, Cursor, Continue, or any other MCP host and your AI agent can
|
|
5
|
+
create escrows, manage agents, approve mandates, and check wallet
|
|
6
|
+
balances natively, without writing HTTP boilerplate.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @aura-payments/mcp-server
|
|
12
|
+
# or invoke directly via npx (no install)
|
|
13
|
+
npx @aura-payments/mcp-server
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Configure (Claude Desktop / Cursor / any MCP client)
|
|
17
|
+
|
|
18
|
+
Add this to your `mcp.json` (paths vary by host, e.g.
|
|
19
|
+
`~/Library/Application Support/Claude/mcp.json` for Claude Desktop on
|
|
20
|
+
macOS, `~/.cursor/mcp.json` for Cursor):
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"aura": {
|
|
26
|
+
"command": "aura-mcp",
|
|
27
|
+
"env": {
|
|
28
|
+
"AURA_API_KEY": "pk_live_..."
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Get an API key at <https://getaura.sh>.
|
|
36
|
+
|
|
37
|
+
### Optional env
|
|
38
|
+
|
|
39
|
+
| Variable | Default | Purpose |
|
|
40
|
+
| ----------------------- | ----------------------------- | ---------------------------------------------------- |
|
|
41
|
+
| `AURA_API_KEY` | (required) | Your platform API key (`pk_live_*` or `pk_test_*`) |
|
|
42
|
+
| `AURA_BASE_URL` | `https://tryaura.xyz/api` | Override for self-hosted or staging deployments |
|
|
43
|
+
| `AURA_AUTO_IDEMPOTENCY` | `true` | Auto-generate idempotency keys for POST/PUT requests |
|
|
44
|
+
|
|
45
|
+
## Tools
|
|
46
|
+
|
|
47
|
+
| Tool | What it does |
|
|
48
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `aura_create_escrow` | Create a new escrow with payment splits across vendor/platform/seller wallets |
|
|
50
|
+
| `aura_get_escrow` | Get the current state of an escrow |
|
|
51
|
+
| `aura_list_escrows` | List escrows with state/sort/search filters |
|
|
52
|
+
| `aura_fund_escrow` | Fund an escrow from the buyer wallet |
|
|
53
|
+
| `aura_release_escrow` | Release a funded escrow to recipients per the splits |
|
|
54
|
+
| `aura_refund_escrow` | Refund a funded escrow back to the buyer |
|
|
55
|
+
| `aura_create_agent` | Create a new AI agent with a dedicated wallet and optional initial spending policy |
|
|
56
|
+
| `aura_list_agents` | List agents (filter by status/type, paginated) |
|
|
57
|
+
| `aura_get_agent` | Get a single agent by id |
|
|
58
|
+
| `aura_get_agent_balance` | Get an agent's current USDC wallet balance (on-chain) |
|
|
59
|
+
| `aura_freeze_agent` | Kill switch — pause the agent and cascade-reject every pending mandate it has in flight |
|
|
60
|
+
| `aura_unfreeze_agent` | Resume a frozen agent |
|
|
61
|
+
| `aura_evaluate_policy` | Run a hypothetical transaction through the policy engine without executing it |
|
|
62
|
+
| `aura_list_mandates` | List operator mandates (defaults to status=pending — the inbox) |
|
|
63
|
+
| `aura_get_mandate` | Get a single mandate by id |
|
|
64
|
+
| `aura_approve_mandate` | Approve a pending mandate. Requires a precomputed HMAC decision signature (the LLM never sees the secret). |
|
|
65
|
+
| `aura_reject_mandate` | Reject a pending mandate. Same signature scheme. |
|
|
66
|
+
| `aura_get_wallet_balance` | Get any wallet's USDC balance |
|
|
67
|
+
|
|
68
|
+
## Resources
|
|
69
|
+
|
|
70
|
+
| URI | Returns |
|
|
71
|
+
| -------------------------------- | ------------------------------------------------------ |
|
|
72
|
+
| `aura://agents/{id}` | Full agent record (status, wallet, type, etc.) |
|
|
73
|
+
| `aura://agents/{id}/balance` | Current on-chain USDC balance |
|
|
74
|
+
| `aura://mandates/pending` | The operator's inbox (50 most-urgent pending mandates) |
|
|
75
|
+
| `aura://mandates/{id}` | Full mandate record (intent, status, decision history) |
|
|
76
|
+
|
|
77
|
+
## Try it
|
|
78
|
+
|
|
79
|
+
Once configured, ask your AI agent:
|
|
80
|
+
|
|
81
|
+
> "Using Aura, create a $50 USDC escrow on ARC, splitting 80% to vendor wallet
|
|
82
|
+
> `<vendor-uuid>` and 20% to platform wallet `<platform-uuid>`."
|
|
83
|
+
|
|
84
|
+
The agent should call `aura_create_escrow` and return the new escrow id.
|
|
85
|
+
|
|
86
|
+
> "List all my pending mandates."
|
|
87
|
+
|
|
88
|
+
The agent should call `aura_list_mandates` (defaults to pending) and show the
|
|
89
|
+
inbox.
|
|
90
|
+
|
|
91
|
+
## Approving mandates from an LLM
|
|
92
|
+
|
|
93
|
+
`aura_approve_mandate` requires a precomputed HMAC-SHA256 signature over
|
|
94
|
+
`${mandateId}:approve:${decidedAt}`. The LLM should NOT compute this — the
|
|
95
|
+
secret stays out of model context. Instead, use the Aura CLI (`aura mandates
|
|
96
|
+
approve <id>`) or the mobile app to approve, and the LLM can confirm via
|
|
97
|
+
`aura_get_mandate` that the decision landed.
|
|
98
|
+
|
|
99
|
+
If you want LLM-driven approval (e.g., for a fully autonomous ops agent),
|
|
100
|
+
run a thin trusted shim alongside the MCP server that holds the secret and
|
|
101
|
+
exposes a non-MCP signing endpoint. That's deliberately not bundled here.
|
|
102
|
+
|
|
103
|
+
## Errors
|
|
104
|
+
|
|
105
|
+
Every tool catches errors and returns them as structured `isError: true`
|
|
106
|
+
results so the LLM can read the message. The `errorCode` line tells the LLM
|
|
107
|
+
what kind of error it is:
|
|
108
|
+
|
|
109
|
+
| `errorCode` | What it means |
|
|
110
|
+
| ------------------- | ---------------------------------------------------------------------- |
|
|
111
|
+
| `CONFIG_ERROR` | `AURA_API_KEY` missing or unreadable |
|
|
112
|
+
| `AUTH_ERROR` | Key is invalid, revoked, or unauthorized for the requested resource |
|
|
113
|
+
| `NOT_FOUND` | The escrow / agent / mandate doesn't exist (or isn't yours) |
|
|
114
|
+
| `VALIDATION_ERROR` | Request body failed server-side validation |
|
|
115
|
+
| `RATE_LIMITED` | Too many requests. Includes `retryAfter` when available |
|
|
116
|
+
| `TIMEOUT` | Request exceeded the SDK timeout (default 30s) |
|
|
117
|
+
| `API_ERROR_<code>` | Generic platform error (5xx etc.) |
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT.
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
5
|
+
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
6
|
+
var sdk = require('@aura-payments/sdk');
|
|
7
|
+
var zod = require('zod');
|
|
8
|
+
|
|
9
|
+
var cached = null;
|
|
10
|
+
function getClient() {
|
|
11
|
+
if (cached) return cached;
|
|
12
|
+
const apiKey = process.env.AURA_API_KEY;
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
throw new MissingAuraApiKeyError();
|
|
15
|
+
}
|
|
16
|
+
cached = new sdk.AuraClient({
|
|
17
|
+
apiKey,
|
|
18
|
+
baseUrl: process.env.AURA_BASE_URL,
|
|
19
|
+
// Honor caller's preference; default to SDK default (auto on POST/PUT)
|
|
20
|
+
autoIdempotency: process.env.AURA_AUTO_IDEMPOTENCY !== "false"
|
|
21
|
+
});
|
|
22
|
+
return cached;
|
|
23
|
+
}
|
|
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
|
+
|
|
33
|
+
// src/resources/agents.ts
|
|
34
|
+
function registerAgentResources(server) {
|
|
35
|
+
server.resource(
|
|
36
|
+
"agent",
|
|
37
|
+
new mcp_js.ResourceTemplate("aura://agents/{id}", { list: void 0 }),
|
|
38
|
+
async (uri, { id }) => {
|
|
39
|
+
const client = getClient();
|
|
40
|
+
const agent = await client.agents.get(String(id));
|
|
41
|
+
return {
|
|
42
|
+
contents: [
|
|
43
|
+
{
|
|
44
|
+
uri: uri.href,
|
|
45
|
+
mimeType: "application/json",
|
|
46
|
+
text: JSON.stringify(agent, null, 2)
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
server.resource(
|
|
53
|
+
"agent-balance",
|
|
54
|
+
new mcp_js.ResourceTemplate("aura://agents/{id}/balance", { list: void 0 }),
|
|
55
|
+
async (uri, { id }) => {
|
|
56
|
+
const client = getClient();
|
|
57
|
+
const balance = await client.agents.getBalance(String(id));
|
|
58
|
+
return {
|
|
59
|
+
contents: [
|
|
60
|
+
{
|
|
61
|
+
uri: uri.href,
|
|
62
|
+
mimeType: "application/json",
|
|
63
|
+
text: JSON.stringify(balance, null, 2)
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
function registerMandateResources(server) {
|
|
71
|
+
server.resource(
|
|
72
|
+
"mandates-pending",
|
|
73
|
+
"aura://mandates/pending",
|
|
74
|
+
async (uri) => {
|
|
75
|
+
const client = getClient();
|
|
76
|
+
const page = await client.mandates.listPending({ limit: 50 });
|
|
77
|
+
return {
|
|
78
|
+
contents: [
|
|
79
|
+
{
|
|
80
|
+
uri: uri.href,
|
|
81
|
+
mimeType: "application/json",
|
|
82
|
+
text: JSON.stringify(page, null, 2)
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
server.resource(
|
|
89
|
+
"mandate",
|
|
90
|
+
new mcp_js.ResourceTemplate("aura://mandates/{id}", { list: void 0 }),
|
|
91
|
+
async (uri, { id }) => {
|
|
92
|
+
const client = getClient();
|
|
93
|
+
const mandate = await client.mandates.get(String(id));
|
|
94
|
+
return {
|
|
95
|
+
contents: [
|
|
96
|
+
{
|
|
97
|
+
uri: uri.href,
|
|
98
|
+
mimeType: "application/json",
|
|
99
|
+
text: JSON.stringify(mandate, null, 2)
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
async function withToolErrorHandling(body, successFormatter = defaultJsonFormatter) {
|
|
107
|
+
try {
|
|
108
|
+
const result = await body();
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: "text", text: successFormatter(result) }]
|
|
111
|
+
};
|
|
112
|
+
} catch (err) {
|
|
113
|
+
return formatError(err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function defaultJsonFormatter(data) {
|
|
117
|
+
return JSON.stringify(data, null, 2);
|
|
118
|
+
}
|
|
119
|
+
function formatError(err) {
|
|
120
|
+
if (err instanceof MissingAuraApiKeyError) {
|
|
121
|
+
return errorResult("CONFIG_ERROR", err.message);
|
|
122
|
+
}
|
|
123
|
+
if (err instanceof sdk.AuraAuthenticationError) {
|
|
124
|
+
return errorResult(
|
|
125
|
+
"AUTH_ERROR",
|
|
126
|
+
`Authentication failed: ${err.message}. Check that AURA_API_KEY is correct and not revoked.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (err instanceof sdk.AuraNotFoundError) {
|
|
130
|
+
return errorResult("NOT_FOUND", err.message);
|
|
131
|
+
}
|
|
132
|
+
if (err instanceof sdk.AuraValidationError) {
|
|
133
|
+
return errorResult("VALIDATION_ERROR", err.message);
|
|
134
|
+
}
|
|
135
|
+
if (err instanceof sdk.AuraRateLimitError) {
|
|
136
|
+
return errorResult(
|
|
137
|
+
"RATE_LIMITED",
|
|
138
|
+
`Rate limited. ${err.retryAfter ? `Retry after ${err.retryAfter}s.` : "Back off and retry."}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (err instanceof sdk.AuraTimeoutError) {
|
|
142
|
+
return errorResult("TIMEOUT", err.message);
|
|
143
|
+
}
|
|
144
|
+
if (err instanceof sdk.AuraAPIError) {
|
|
145
|
+
return errorResult(`API_ERROR_${err.statusCode}`, err.message);
|
|
146
|
+
}
|
|
147
|
+
if (err instanceof sdk.AuraError) {
|
|
148
|
+
return errorResult(err.code || "AURA_ERROR", err.message);
|
|
149
|
+
}
|
|
150
|
+
if (err instanceof Error) {
|
|
151
|
+
return errorResult("UNKNOWN_ERROR", err.message);
|
|
152
|
+
}
|
|
153
|
+
return errorResult("UNKNOWN_ERROR", String(err));
|
|
154
|
+
}
|
|
155
|
+
function errorResult(code, message) {
|
|
156
|
+
return {
|
|
157
|
+
isError: true,
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
text: `errorCode: ${code}
|
|
162
|
+
message: ${message}`
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function toolError(code, message) {
|
|
168
|
+
return errorResult(code, message);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/tools/agents.ts
|
|
172
|
+
var ChainSchema = zod.z.enum(["ARC", "ARB", "BASE", "ETH", "MATIC"]);
|
|
173
|
+
var SpendingLimitSchema = zod.z.object({
|
|
174
|
+
limitType: zod.z.enum([
|
|
175
|
+
"per_transaction",
|
|
176
|
+
"daily",
|
|
177
|
+
"weekly",
|
|
178
|
+
"monthly",
|
|
179
|
+
"lifetime"
|
|
180
|
+
]),
|
|
181
|
+
maxAmount: zod.z.string().describe("Whole-number USDC amount as a string"),
|
|
182
|
+
currency: zod.z.string().optional().default("USDC")
|
|
183
|
+
});
|
|
184
|
+
var MerchantRuleSchema = zod.z.object({
|
|
185
|
+
ruleType: zod.z.enum(["allowlist", "blocklist"]),
|
|
186
|
+
destinationType: zod.z.enum(["address", "domain", "category", "entity_id"]),
|
|
187
|
+
destinationValue: zod.z.string().min(1)
|
|
188
|
+
});
|
|
189
|
+
function registerAgentTools(server) {
|
|
190
|
+
server.tool(
|
|
191
|
+
"aura_create_agent",
|
|
192
|
+
"Create a new AI agent with a dedicated wallet and optional initial spending policy. The wallet is provisioned on-chain during this call (a few seconds latency).",
|
|
193
|
+
{
|
|
194
|
+
name: zod.z.string().min(1).max(100),
|
|
195
|
+
description: zod.z.string().max(500).optional(),
|
|
196
|
+
type: zod.z.enum(["treasury", "payment", "trading", "custom"]),
|
|
197
|
+
chain: ChainSchema.optional().default("ARC"),
|
|
198
|
+
metadata: zod.z.record(zod.z.unknown()).optional(),
|
|
199
|
+
initialPolicy: zod.z.object({
|
|
200
|
+
name: zod.z.string().optional(),
|
|
201
|
+
description: zod.z.string().optional(),
|
|
202
|
+
spendingLimits: zod.z.array(SpendingLimitSchema).optional(),
|
|
203
|
+
merchantRules: zod.z.array(MerchantRuleSchema).optional(),
|
|
204
|
+
requiresApprovalAbove: zod.z.string().optional(),
|
|
205
|
+
autoApproveBelow: zod.z.string().optional()
|
|
206
|
+
}).optional()
|
|
207
|
+
},
|
|
208
|
+
async (args) => withToolErrorHandling(async () => {
|
|
209
|
+
const client = getClient();
|
|
210
|
+
return client.agents.create(args);
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
server.tool(
|
|
214
|
+
"aura_list_agents",
|
|
215
|
+
"List agents for the authenticated account, paginated. Filter by status or type.",
|
|
216
|
+
{
|
|
217
|
+
status: zod.z.enum(["active", "paused", "archived"]).optional(),
|
|
218
|
+
type: zod.z.enum(["treasury", "payment", "trading", "custom"]).optional(),
|
|
219
|
+
limit: zod.z.number().int().min(1).max(200).optional(),
|
|
220
|
+
offset: zod.z.number().int().min(0).optional()
|
|
221
|
+
},
|
|
222
|
+
async (args) => withToolErrorHandling(async () => {
|
|
223
|
+
const client = getClient();
|
|
224
|
+
return client.agents.list(args);
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
server.tool(
|
|
228
|
+
"aura_get_agent",
|
|
229
|
+
"Get a single agent by id with its current status, wallet, and recent activity.",
|
|
230
|
+
{
|
|
231
|
+
agentId: zod.z.string().uuid()
|
|
232
|
+
},
|
|
233
|
+
async ({ agentId }) => withToolErrorHandling(async () => {
|
|
234
|
+
const client = getClient();
|
|
235
|
+
return client.agents.get(agentId);
|
|
236
|
+
})
|
|
237
|
+
);
|
|
238
|
+
server.tool(
|
|
239
|
+
"aura_get_agent_balance",
|
|
240
|
+
"Get the current USDC balance of an agent's wallet (on-chain at request time).",
|
|
241
|
+
{
|
|
242
|
+
agentId: zod.z.string().uuid()
|
|
243
|
+
},
|
|
244
|
+
async ({ agentId }) => withToolErrorHandling(async () => {
|
|
245
|
+
const client = getClient();
|
|
246
|
+
return client.agents.getBalance(agentId);
|
|
247
|
+
})
|
|
248
|
+
);
|
|
249
|
+
server.tool(
|
|
250
|
+
"aura_freeze_agent",
|
|
251
|
+
"Kill switch \u2014 halt all autonomous activity for an agent. Sets status=paused AND auto-rejects every pending mandate the agent has in flight. Idempotent.",
|
|
252
|
+
{
|
|
253
|
+
agentId: zod.z.string().uuid(),
|
|
254
|
+
reason: zod.z.string().max(500).optional().describe("Surfaced in activity event metadata for audit trail")
|
|
255
|
+
},
|
|
256
|
+
async (args) => withToolErrorHandling(async () => {
|
|
257
|
+
const client = getClient();
|
|
258
|
+
return client.agents.freeze(args);
|
|
259
|
+
})
|
|
260
|
+
);
|
|
261
|
+
server.tool(
|
|
262
|
+
"aura_unfreeze_agent",
|
|
263
|
+
"Resume autonomous activity for a frozen agent (status -> active). Previously cascade-rejected mandates are NOT restored \u2014 the agent re-creates them on its next loop.",
|
|
264
|
+
{
|
|
265
|
+
agentId: zod.z.string().uuid()
|
|
266
|
+
},
|
|
267
|
+
async (args) => withToolErrorHandling(async () => {
|
|
268
|
+
const client = getClient();
|
|
269
|
+
return client.agents.unfreeze(args);
|
|
270
|
+
})
|
|
271
|
+
);
|
|
272
|
+
server.tool(
|
|
273
|
+
"aura_evaluate_policy",
|
|
274
|
+
"Run a hypothetical transaction through the policy engine WITHOUT executing it. Returns the decision (auto_approve / auto_deny / requires_human_approval) and which rules matched. When the decision requires human approval, a mandate row is created and its id is returned.",
|
|
275
|
+
{
|
|
276
|
+
agentId: zod.z.string().uuid(),
|
|
277
|
+
amount: zod.z.string().describe("USDC amount as decimal string"),
|
|
278
|
+
destination: zod.z.string().describe("Counterparty wallet (0x...) or merchant identifier"),
|
|
279
|
+
destinationType: zod.z.enum(["address", "domain", "category", "entity_id"]).optional(),
|
|
280
|
+
reasoning: zod.z.string().optional(),
|
|
281
|
+
metadata: zod.z.record(zod.z.unknown()).optional()
|
|
282
|
+
},
|
|
283
|
+
async (args) => withToolErrorHandling(async () => {
|
|
284
|
+
const client = getClient();
|
|
285
|
+
return client.agents.evaluate(args);
|
|
286
|
+
})
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
var ChainSchema2 = zod.z.enum(["ARC", "ARB", "BASE", "ETH", "MATIC"]);
|
|
290
|
+
var SplitInputSchema = zod.z.object({
|
|
291
|
+
ownerType: zod.z.enum([
|
|
292
|
+
"buyer",
|
|
293
|
+
"seller",
|
|
294
|
+
"platform",
|
|
295
|
+
"vendor",
|
|
296
|
+
"creator",
|
|
297
|
+
"affiliate",
|
|
298
|
+
"logistics",
|
|
299
|
+
"tax"
|
|
300
|
+
]),
|
|
301
|
+
ownerId: zod.z.string().uuid(),
|
|
302
|
+
role: zod.z.enum([
|
|
303
|
+
"seller",
|
|
304
|
+
"platform",
|
|
305
|
+
"vendor",
|
|
306
|
+
"creator",
|
|
307
|
+
"affiliate",
|
|
308
|
+
"logistics",
|
|
309
|
+
"tax"
|
|
310
|
+
]),
|
|
311
|
+
percentage: zod.z.number().min(0).max(100)
|
|
312
|
+
});
|
|
313
|
+
function registerEscrowTools(server) {
|
|
314
|
+
server.tool(
|
|
315
|
+
"aura_create_escrow",
|
|
316
|
+
"Create a new escrow with payment splits across vendor/platform/seller wallets. Default chain is ARC. Async by default \u2014 returns an escrowId immediately while contract deployment continues in background.",
|
|
317
|
+
{
|
|
318
|
+
orderId: zod.z.string().describe("Caller-provided order id, unique per account"),
|
|
319
|
+
buyerOwnerId: zod.z.string().uuid().describe("UUID of the buyer in the platform"),
|
|
320
|
+
amount: zod.z.string().describe('Whole-number USDC amount as a string (e.g. "100" for $100)'),
|
|
321
|
+
chain: ChainSchema2.optional().describe("Target chain. Defaults to ARC"),
|
|
322
|
+
splits: zod.z.array(SplitInputSchema).min(1).describe("Payment splits. Percentages must sum to 100.")
|
|
323
|
+
},
|
|
324
|
+
async (args) => withToolErrorHandling(async () => {
|
|
325
|
+
const client = getClient();
|
|
326
|
+
return client.escrows.create(args);
|
|
327
|
+
})
|
|
328
|
+
);
|
|
329
|
+
server.tool(
|
|
330
|
+
"aura_get_escrow",
|
|
331
|
+
"Get the current state of an escrow by id, including splits and on-chain deployment status.",
|
|
332
|
+
{
|
|
333
|
+
escrowId: zod.z.string().uuid()
|
|
334
|
+
},
|
|
335
|
+
async ({ escrowId }) => withToolErrorHandling(async () => {
|
|
336
|
+
const client = getClient();
|
|
337
|
+
return client.escrows.get(escrowId);
|
|
338
|
+
})
|
|
339
|
+
);
|
|
340
|
+
server.tool(
|
|
341
|
+
"aura_list_escrows",
|
|
342
|
+
"List escrows for the authenticated account, optionally filtered by state. Returns paginated results.",
|
|
343
|
+
{
|
|
344
|
+
state: zod.z.enum([
|
|
345
|
+
"initiated",
|
|
346
|
+
"deployed",
|
|
347
|
+
"funded",
|
|
348
|
+
"locked",
|
|
349
|
+
"released",
|
|
350
|
+
"refunded",
|
|
351
|
+
"disputed",
|
|
352
|
+
"all"
|
|
353
|
+
]).optional(),
|
|
354
|
+
sortBy: zod.z.enum(["created_desc", "created_asc", "amount_desc", "amount_asc"]).optional(),
|
|
355
|
+
search: zod.z.string().optional(),
|
|
356
|
+
limit: zod.z.number().int().min(1).max(200).optional(),
|
|
357
|
+
offset: zod.z.number().int().min(0).optional()
|
|
358
|
+
},
|
|
359
|
+
async (args) => withToolErrorHandling(async () => {
|
|
360
|
+
const client = getClient();
|
|
361
|
+
return client.escrows.list(args);
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
server.tool(
|
|
365
|
+
"aura_fund_escrow",
|
|
366
|
+
"Fund an existing escrow from the buyer wallet. Once funded, the escrow can be released (after unlock conditions) or refunded.",
|
|
367
|
+
{
|
|
368
|
+
escrowId: zod.z.string().uuid(),
|
|
369
|
+
buyerWalletId: zod.z.string().uuid(),
|
|
370
|
+
amount: zod.z.string().describe("USDC amount to fund (whole-number string)"),
|
|
371
|
+
correlationId: zod.z.string().optional()
|
|
372
|
+
},
|
|
373
|
+
async (args) => withToolErrorHandling(async () => {
|
|
374
|
+
const client = getClient();
|
|
375
|
+
return client.escrows.fund(args);
|
|
376
|
+
})
|
|
377
|
+
);
|
|
378
|
+
server.tool(
|
|
379
|
+
"aura_release_escrow",
|
|
380
|
+
"Release a funded escrow to the recipients per the original splits. Requires the unlock conditions to be met (or manual override with actorId).",
|
|
381
|
+
{
|
|
382
|
+
escrowId: zod.z.string().uuid(),
|
|
383
|
+
actorId: zod.z.string().uuid().describe("UUID of the user/agent performing the release"),
|
|
384
|
+
reason: zod.z.string().optional(),
|
|
385
|
+
correlationId: zod.z.string().optional()
|
|
386
|
+
},
|
|
387
|
+
async (args) => withToolErrorHandling(async () => {
|
|
388
|
+
const client = getClient();
|
|
389
|
+
return client.escrows.release(args);
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
server.tool(
|
|
393
|
+
"aura_refund_escrow",
|
|
394
|
+
"Refund a funded escrow back to the buyer. Requires a reason (audit trail).",
|
|
395
|
+
{
|
|
396
|
+
escrowId: zod.z.string().uuid(),
|
|
397
|
+
reason: zod.z.string().min(10),
|
|
398
|
+
actorId: zod.z.string().uuid()
|
|
399
|
+
},
|
|
400
|
+
async (args) => withToolErrorHandling(async () => {
|
|
401
|
+
const client = getClient();
|
|
402
|
+
return client.escrows.refund(args);
|
|
403
|
+
})
|
|
404
|
+
);
|
|
405
|
+
server.tool(
|
|
406
|
+
"aura_receive_and_split",
|
|
407
|
+
'Receive USDC and split it to recipients in ONE call \u2014 composes escrow create\u2192fund\u2192release. The payer (payerOwnerId) may be the agent itself (splitting funds it holds) or a counterparty; funds move from that party\'s Aura wallet. Returns a `stage` (created|deployed|funded|released): if it stops at "deployed" with `fundingRequired`, fund that wallet (e.g. aura_request_testnet_funds) and retry. Testnet only.',
|
|
408
|
+
{
|
|
409
|
+
orderId: zod.z.string().describe("Caller-provided order id, unique per account"),
|
|
410
|
+
payerOwnerId: zod.z.string().describe("Entity that funds the escrow (the agent itself or a counterparty)"),
|
|
411
|
+
amount: zod.z.string().describe('Whole-number USDC amount as a string (e.g. "50"). Each split must net \u2265 $1 after fees.'),
|
|
412
|
+
splits: zod.z.array(SplitInputSchema).min(1).describe("Payment splits. Percentages must sum to 100."),
|
|
413
|
+
chain: zod.z.enum(["ARC", "ARB", "BASE"]).optional().describe("Testnet chain. Defaults to ARC."),
|
|
414
|
+
actorId: zod.z.string().optional().describe("Actor recorded on release. Defaults to payerOwnerId."),
|
|
415
|
+
autoRelease: zod.z.boolean().optional().describe("Release immediately after funding. Default true."),
|
|
416
|
+
waitForDeploymentMs: zod.z.number().int().min(0).optional().describe("Max ms to wait for on-chain deployment. Default 60000."),
|
|
417
|
+
waitForFundingMs: zod.z.number().int().min(0).optional().describe("Max ms to wait for funds to settle (funded/locked) before releasing. Default 60000.")
|
|
418
|
+
},
|
|
419
|
+
async (args) => withToolErrorHandling(async () => {
|
|
420
|
+
const client = getClient();
|
|
421
|
+
return client.escrows.receiveAndSplit(args);
|
|
422
|
+
})
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
function registerMandateTools(server) {
|
|
426
|
+
server.tool(
|
|
427
|
+
"aura_list_mandates",
|
|
428
|
+
"List mandates for the authenticated account. Defaults to status=pending so it acts as the operator's inbox by default. Most-urgent (soonest-expiring) first.",
|
|
429
|
+
{
|
|
430
|
+
status: zod.z.enum(["pending", "approved", "rejected", "cancelled"]).optional().default("pending"),
|
|
431
|
+
agentId: zod.z.string().uuid().optional(),
|
|
432
|
+
limit: zod.z.number().int().min(1).max(200).optional(),
|
|
433
|
+
cursor: zod.z.string().uuid().optional().describe("Last mandate id from the previous page")
|
|
434
|
+
},
|
|
435
|
+
async (args) => withToolErrorHandling(async () => {
|
|
436
|
+
const client = getClient();
|
|
437
|
+
return client.mandates.list(args);
|
|
438
|
+
})
|
|
439
|
+
);
|
|
440
|
+
server.tool(
|
|
441
|
+
"aura_get_mandate",
|
|
442
|
+
"Get a single mandate by id, including its full intent payload (the action the agent wants to take), expiry, and decision history.",
|
|
443
|
+
{
|
|
444
|
+
mandateId: zod.z.string().uuid()
|
|
445
|
+
},
|
|
446
|
+
async ({ mandateId }) => withToolErrorHandling(async () => {
|
|
447
|
+
const client = getClient();
|
|
448
|
+
return client.mandates.get(mandateId);
|
|
449
|
+
})
|
|
450
|
+
);
|
|
451
|
+
server.tool(
|
|
452
|
+
"aura_approve_mandate",
|
|
453
|
+
"Approve a pending mandate. The decisionSignature MUST be precomputed by the caller (HMAC-SHA256 over `${mandateId}:approve:${decidedAt}`) \u2014 the LLM should never see the HMAC secret. Use the CLI or mobile app to compute it. Idempotent: re-approving an already-approved mandate returns the existing decision.",
|
|
454
|
+
{
|
|
455
|
+
mandateId: zod.z.string().uuid(),
|
|
456
|
+
decisionMethod: zod.z.enum(["biometric", "passcode", "manual"]),
|
|
457
|
+
decisionSignature: zod.z.string().min(16).describe("HMAC-SHA256 hex digest, computed by the caller"),
|
|
458
|
+
decidedAt: zod.z.string().datetime().optional().describe('ISO-8601 timestamp the signature commits to. Defaults to "now" server-side.')
|
|
459
|
+
},
|
|
460
|
+
async (args) => withToolErrorHandling(async () => {
|
|
461
|
+
const client = getClient();
|
|
462
|
+
return client.mandates.approve(args);
|
|
463
|
+
})
|
|
464
|
+
);
|
|
465
|
+
server.tool(
|
|
466
|
+
"aura_reject_mandate",
|
|
467
|
+
"Reject a pending mandate. Same signature scheme as approve \u2014 caller computes it, LLM forwards.",
|
|
468
|
+
{
|
|
469
|
+
mandateId: zod.z.string().uuid(),
|
|
470
|
+
decisionMethod: zod.z.enum(["biometric", "passcode", "manual"]),
|
|
471
|
+
decisionSignature: zod.z.string().min(16),
|
|
472
|
+
decidedAt: zod.z.string().datetime().optional(),
|
|
473
|
+
reason: zod.z.string().optional().describe("Human-readable reason logged to the activity feed")
|
|
474
|
+
},
|
|
475
|
+
async (args) => withToolErrorHandling(async () => {
|
|
476
|
+
const client = getClient();
|
|
477
|
+
return client.mandates.reject(args);
|
|
478
|
+
})
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
var TestnetChainSchema = zod.z.enum(["ARC", "ARB", "BASE"]);
|
|
482
|
+
function walletCreateEnabled() {
|
|
483
|
+
const v = (process.env.AURA_MCP_ALLOW_WALLET_CREATE ?? "").trim().toLowerCase();
|
|
484
|
+
return v === "1" || v === "true" || v === "yes";
|
|
485
|
+
}
|
|
486
|
+
function registerWalletTools(server) {
|
|
487
|
+
server.tool(
|
|
488
|
+
"aura_get_wallet_balance",
|
|
489
|
+
"Get a wallet's current USDC balance (on-chain at request time).",
|
|
490
|
+
{
|
|
491
|
+
walletId: zod.z.string().uuid()
|
|
492
|
+
},
|
|
493
|
+
async ({ walletId }) => withToolErrorHandling(async () => {
|
|
494
|
+
const client = getClient();
|
|
495
|
+
return client.wallets.getBalance(walletId);
|
|
496
|
+
})
|
|
497
|
+
);
|
|
498
|
+
server.tool(
|
|
499
|
+
"aura_create_wallet",
|
|
500
|
+
"Provision (create-or-get) a wallet to RECEIVE USDC into. Idempotent and moves no money. Testnet only. Disabled unless the host sets AURA_MCP_ALLOW_WALLET_CREATE=1.",
|
|
501
|
+
{
|
|
502
|
+
entityId: zod.z.string().min(1).describe("Your unique identifier for the wallet owner (e.g. the agent id)"),
|
|
503
|
+
chain: TestnetChainSchema.optional().describe("Testnet chain. Defaults to ARC."),
|
|
504
|
+
type: zod.z.enum([
|
|
505
|
+
"BUSINESS",
|
|
506
|
+
"buyer",
|
|
507
|
+
"seller",
|
|
508
|
+
"platform",
|
|
509
|
+
"vendor",
|
|
510
|
+
"creator",
|
|
511
|
+
"affiliate",
|
|
512
|
+
"logistics",
|
|
513
|
+
"tax"
|
|
514
|
+
]).optional().describe("Wallet owner type. Defaults to BUSINESS.")
|
|
515
|
+
},
|
|
516
|
+
async ({ entityId, chain, type }) => {
|
|
517
|
+
if (!walletCreateEnabled()) {
|
|
518
|
+
return toolError(
|
|
519
|
+
"WALLET_CREATE_DISABLED",
|
|
520
|
+
"Wallet creation is disabled on this MCP host. Set AURA_MCP_ALLOW_WALLET_CREATE=1 in the server env to enable it, or create the wallet via the Aura CLI (`aura wallet create`) or SDK."
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
return withToolErrorHandling(async () => {
|
|
524
|
+
const client = getClient();
|
|
525
|
+
return client.wallets.create({ entityId, chain, type });
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
server.tool(
|
|
530
|
+
"aura_request_testnet_funds",
|
|
531
|
+
"Request testnet USDC for a wallet from the platform faucet (testnet only). Drips are async \u2014 balance reflects after a few seconds. Returns FAUCET_UNAVAILABLE if the deployment has no faucet enabled.",
|
|
532
|
+
{
|
|
533
|
+
walletId: zod.z.string().uuid(),
|
|
534
|
+
token: zod.z.string().optional().describe("Token to drip. Defaults to 'USDC'."),
|
|
535
|
+
chain: TestnetChainSchema.optional().describe("Override the wallet's chain.")
|
|
536
|
+
},
|
|
537
|
+
async ({ walletId, token, chain }) => withToolErrorHandling(async () => {
|
|
538
|
+
const client = getClient();
|
|
539
|
+
return client.wallets.requestTestnetFunds(walletId, { token, chain });
|
|
540
|
+
})
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/index.ts
|
|
545
|
+
function createServer(options = {}) {
|
|
546
|
+
const server = new mcp_js.McpServer({
|
|
547
|
+
name: options.name ?? "aura-payments",
|
|
548
|
+
version: options.version ?? "1.0.0"
|
|
549
|
+
});
|
|
550
|
+
registerEscrowTools(server);
|
|
551
|
+
registerAgentTools(server);
|
|
552
|
+
registerMandateTools(server);
|
|
553
|
+
registerWalletTools(server);
|
|
554
|
+
registerAgentResources(server);
|
|
555
|
+
registerMandateResources(server);
|
|
556
|
+
return server;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/bin.ts
|
|
560
|
+
async function main() {
|
|
561
|
+
const server = createServer();
|
|
562
|
+
const transport = new stdio_js.StdioServerTransport();
|
|
563
|
+
await server.connect(transport);
|
|
564
|
+
}
|
|
565
|
+
main().catch((err) => {
|
|
566
|
+
console.error("aura-mcp: fatal error:", err);
|
|
567
|
+
process.exit(1);
|
|
568
|
+
});
|
|
569
|
+
//# sourceMappingURL=bin.js.map
|
|
570
|
+
//# sourceMappingURL=bin.js.map
|