@sardis/mcp-server 0.2.7 → 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/CHANGELOG.md +11 -1
- package/CLAUDE_DESKTOP_CONFIG.md +10 -19
- package/README.md +179 -221
- package/dist/api.d.ts +1 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +4 -2
- package/dist/api.js.map +1 -1
- package/dist/cli.js +185 -20
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/events.d.ts +7 -0
- package/dist/tools/events.d.ts.map +1 -0
- package/dist/tools/events.js +413 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/fiat.d.ts +2 -2
- package/dist/tools/fiat.d.ts.map +1 -1
- package/dist/tools/fiat.js +649 -266
- package/dist/tools/fiat.js.map +1 -1
- package/dist/tools/guardrails.d.ts +7 -0
- package/dist/tools/guardrails.d.ts.map +1 -0
- package/dist/tools/guardrails.js +379 -0
- package/dist/tools/guardrails.js.map +1 -0
- package/dist/tools/index.d.ts +29 -8
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +108 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/jobs.d.ts +17 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +583 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/payments.d.ts +2 -0
- package/dist/tools/payments.d.ts.map +1 -1
- package/dist/tools/payments.js +2 -0
- package/dist/tools/payments.js.map +1 -1
- package/dist/tools/policy.d.ts.map +1 -1
- package/dist/tools/policy.js +32 -4
- package/dist/tools/policy.js.map +1 -1
- package/dist/tools/sandbox.js +1 -1
- package/dist/tools/trust.d.ts +7 -0
- package/dist/tools/trust.d.ts.map +1 -0
- package/dist/tools/trust.js +188 -0
- package/dist/tools/trust.js.map +1 -0
- package/dist/tools/types.d.ts +51 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +20 -0
- package/dist/tools/types.js.map +1 -1
- package/dist/tools/wallet-management.d.ts.map +1 -1
- package/dist/tools/wallet-management.js +55 -45
- package/dist/tools/wallet-management.js.map +1 -1
- package/dist/tools/x402.d.ts +12 -0
- package/dist/tools/x402.d.ts.map +1 -0
- package/dist/tools/x402.js +299 -0
- package/dist/tools/x402.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/mcp.json +139 -53
- package/package.json +30 -14
package/dist/tools/fiat.js
CHANGED
|
@@ -1,393 +1,776 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Fiat
|
|
2
|
+
* Fiat/Treasury tools for MCP server
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* USD-first launch with multi-currency-ready schemas.
|
|
5
5
|
*/
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import { getConfig } from '../config.js';
|
|
8
8
|
import { apiRequest } from '../api.js';
|
|
9
|
-
|
|
9
|
+
const AccountHolderSyncSchema = z.object({
|
|
10
|
+
account_token: z.string().optional(),
|
|
11
|
+
});
|
|
12
|
+
const ListFinancialAccountsSchema = z.object({
|
|
13
|
+
account_token: z.string().optional(),
|
|
14
|
+
refresh: z.boolean().optional(),
|
|
15
|
+
});
|
|
16
|
+
const LinkExternalBankSchema = z.object({
|
|
17
|
+
financial_account_token: z.string(),
|
|
18
|
+
verification_method: z.enum(['MICRO_DEPOSIT', 'PRENOTE', 'EXTERNALLY_VERIFIED']).optional(),
|
|
19
|
+
owner_type: z.enum(['INDIVIDUAL', 'BUSINESS']).optional(),
|
|
20
|
+
owner: z.string(),
|
|
21
|
+
account_type: z.enum(['CHECKING', 'SAVINGS']).optional(),
|
|
22
|
+
routing_number: z.string(),
|
|
23
|
+
account_number: z.string(),
|
|
24
|
+
name: z.string().optional(),
|
|
25
|
+
currency: z.string().optional(),
|
|
26
|
+
country: z.string().optional(),
|
|
27
|
+
account_token: z.string().optional(),
|
|
28
|
+
company_id: z.string().optional(),
|
|
29
|
+
user_defined_id: z.string().optional(),
|
|
30
|
+
});
|
|
31
|
+
const VerifyMicroDepositsSchema = z.object({
|
|
32
|
+
token: z.string(),
|
|
33
|
+
micro_deposits: z.tuple([z.string(), z.string()]),
|
|
34
|
+
});
|
|
10
35
|
const FundWalletSchema = z.object({
|
|
11
|
-
wallet_id: z.string().optional().describe('
|
|
12
|
-
amount: z.union([z.string(), z.number()]).describe('
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
36
|
+
wallet_id: z.string().optional().describe('Optional wallet reference for metadata'),
|
|
37
|
+
amount: z.union([z.string(), z.number()]).optional().describe('Legacy amount in major units'),
|
|
38
|
+
amount_minor: z.number().int().positive().optional().describe('Amount in minor units'),
|
|
39
|
+
source: z.enum(['ach', 'wire', 'card', 'bank_account']).optional(),
|
|
40
|
+
currency: z.string().optional(),
|
|
41
|
+
source_id: z.string().optional(),
|
|
42
|
+
financial_account_token: z.string().optional(),
|
|
43
|
+
external_bank_account_token: z.string().optional(),
|
|
44
|
+
method: z.enum(['ACH_NEXT_DAY', 'ACH_SAME_DAY']).optional(),
|
|
45
|
+
sec_code: z.enum(['CCD', 'PPD', 'WEB']).optional(),
|
|
46
|
+
memo: z.string().optional(),
|
|
47
|
+
idempotency_key: z.string().optional(),
|
|
48
|
+
user_defined_id: z.string().optional(),
|
|
16
49
|
});
|
|
17
50
|
const WithdrawSchema = z.object({
|
|
18
|
-
wallet_id: z.string().
|
|
19
|
-
amount: z.string().
|
|
20
|
-
|
|
51
|
+
wallet_id: z.string().optional(),
|
|
52
|
+
amount: z.union([z.string(), z.number()]).optional(),
|
|
53
|
+
amount_minor: z.number().int().positive().optional(),
|
|
54
|
+
destination_id: z.string().optional().describe('Legacy destination alias'),
|
|
55
|
+
account_id: z.string().optional().describe('Legacy destination alias'),
|
|
56
|
+
financial_account_token: z.string().optional(),
|
|
57
|
+
external_bank_account_token: z.string().optional(),
|
|
58
|
+
method: z.enum(['ACH_NEXT_DAY', 'ACH_SAME_DAY']).optional(),
|
|
59
|
+
sec_code: z.enum(['CCD', 'PPD', 'WEB']).optional(),
|
|
60
|
+
memo: z.string().optional(),
|
|
61
|
+
idempotency_key: z.string().optional(),
|
|
62
|
+
user_defined_id: z.string().optional(),
|
|
21
63
|
});
|
|
22
64
|
const StatusSchema = z.object({
|
|
23
|
-
|
|
24
|
-
|
|
65
|
+
payment_token: z.string().optional(),
|
|
66
|
+
transfer_id: z.string().optional(),
|
|
67
|
+
funding_id: z.string().optional(),
|
|
68
|
+
});
|
|
69
|
+
const FundingListSchema = z.object({
|
|
70
|
+
type: z.enum(['deposit', 'withdrawal', 'all']).optional(),
|
|
71
|
+
limit: z.number().int().positive().optional(),
|
|
25
72
|
});
|
|
26
|
-
|
|
73
|
+
function toMinorUnits(input) {
|
|
74
|
+
if (input === undefined)
|
|
75
|
+
return 0;
|
|
76
|
+
const value = typeof input === 'number' ? input : Number.parseFloat(input);
|
|
77
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
78
|
+
return 0;
|
|
79
|
+
return Math.round(value * 100);
|
|
80
|
+
}
|
|
81
|
+
function buildSimulatedPayment(prefix, direction, amountMinor) {
|
|
82
|
+
const token = `${prefix}_${Date.now().toString(36)}`;
|
|
83
|
+
const common = {
|
|
84
|
+
payment_token: token,
|
|
85
|
+
status: 'PENDING',
|
|
86
|
+
result: 'APPROVED',
|
|
87
|
+
direction,
|
|
88
|
+
method: 'ACH_NEXT_DAY',
|
|
89
|
+
currency: 'USD',
|
|
90
|
+
pending_amount: amountMinor,
|
|
91
|
+
settled_amount: 0,
|
|
92
|
+
financial_account_token: 'fa_simulated',
|
|
93
|
+
external_bank_account_token: 'eba_simulated',
|
|
94
|
+
};
|
|
95
|
+
return prefix === 'fund' ? { ...common, funding_id: token } : { ...common, withdrawal_id: token };
|
|
96
|
+
}
|
|
97
|
+
function serialize(result) {
|
|
98
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
99
|
+
}
|
|
100
|
+
function validateLiveTreasuryArgs(financialAccountToken, externalBankAccountToken, amountMinor) {
|
|
101
|
+
if (!financialAccountToken)
|
|
102
|
+
return 'financial_account_token is required in live mode';
|
|
103
|
+
if (!externalBankAccountToken)
|
|
104
|
+
return 'external_bank_account_token is required in live mode';
|
|
105
|
+
if (amountMinor <= 0)
|
|
106
|
+
return 'amount or amount_minor must be greater than 0';
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
27
109
|
export const fiatToolDefinitions = [
|
|
110
|
+
{
|
|
111
|
+
name: 'sardis_sync_treasury_account_holder',
|
|
112
|
+
description: 'Sync financial accounts from Lithic for an account holder token.',
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: { account_token: { type: 'string' } },
|
|
116
|
+
required: [],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'sardis_list_financial_accounts',
|
|
121
|
+
description: 'List treasury financial accounts available for ACH funding and settlement.',
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
account_token: { type: 'string' },
|
|
126
|
+
refresh: { type: 'boolean' },
|
|
127
|
+
},
|
|
128
|
+
required: [],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'sardis_link_external_bank_account',
|
|
133
|
+
description: 'Create and link an external bank account for ACH rails.',
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: 'object',
|
|
136
|
+
properties: {
|
|
137
|
+
financial_account_token: { type: 'string' },
|
|
138
|
+
verification_method: { type: 'string', enum: ['MICRO_DEPOSIT', 'PRENOTE', 'EXTERNALLY_VERIFIED'] },
|
|
139
|
+
owner_type: { type: 'string', enum: ['INDIVIDUAL', 'BUSINESS'] },
|
|
140
|
+
owner: { type: 'string' },
|
|
141
|
+
account_type: { type: 'string', enum: ['CHECKING', 'SAVINGS'] },
|
|
142
|
+
routing_number: { type: 'string' },
|
|
143
|
+
account_number: { type: 'string' },
|
|
144
|
+
name: { type: 'string' },
|
|
145
|
+
currency: { type: 'string' },
|
|
146
|
+
country: { type: 'string' },
|
|
147
|
+
},
|
|
148
|
+
required: ['financial_account_token', 'owner', 'routing_number', 'account_number'],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'sardis_verify_micro_deposits',
|
|
153
|
+
description: 'Verify external bank account ownership with two micro-deposit amounts.',
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
token: { type: 'string' },
|
|
158
|
+
micro_deposits: { type: 'array', items: { type: 'string' }, minItems: 2, maxItems: 2 },
|
|
159
|
+
},
|
|
160
|
+
required: ['token', 'micro_deposits'],
|
|
161
|
+
},
|
|
162
|
+
},
|
|
28
163
|
{
|
|
29
164
|
name: 'sardis_fund_wallet',
|
|
30
|
-
description: '
|
|
165
|
+
description: 'fund treasury via ACH collection. USD-first route for card spend backing.',
|
|
31
166
|
inputSchema: {
|
|
32
167
|
type: 'object',
|
|
33
168
|
properties: {
|
|
34
|
-
amount: { type: 'number', description: '
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
currency: { type: 'string', description: 'Currency code (default: USD)' },
|
|
42
|
-
source_id: { type: 'string', description: 'Saved payment method ID' },
|
|
169
|
+
amount: { type: 'number', description: 'Legacy amount in major units (USD)' },
|
|
170
|
+
amount_minor: { type: 'number', description: 'Amount in minor units (e.g. cents)' },
|
|
171
|
+
financial_account_token: { type: 'string' },
|
|
172
|
+
external_bank_account_token: { type: 'string' },
|
|
173
|
+
method: { type: 'string', enum: ['ACH_NEXT_DAY', 'ACH_SAME_DAY'] },
|
|
174
|
+
sec_code: { type: 'string', enum: ['CCD', 'PPD', 'WEB'] },
|
|
175
|
+
memo: { type: 'string' },
|
|
43
176
|
},
|
|
44
|
-
required: [
|
|
177
|
+
required: [],
|
|
45
178
|
},
|
|
46
179
|
},
|
|
47
180
|
{
|
|
48
181
|
name: 'sardis_withdraw_to_bank',
|
|
49
|
-
description: 'Withdraw funds
|
|
182
|
+
description: 'Withdraw treasury funds to an external bank account via ACH payment.',
|
|
50
183
|
inputSchema: {
|
|
51
184
|
type: 'object',
|
|
52
185
|
properties: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
186
|
+
amount: { type: 'number', description: 'Legacy amount in major units (USD)' },
|
|
187
|
+
amount_minor: { type: 'number', description: 'Amount in minor units (e.g. cents)' },
|
|
188
|
+
financial_account_token: { type: 'string' },
|
|
189
|
+
external_bank_account_token: { type: 'string' },
|
|
190
|
+
method: { type: 'string', enum: ['ACH_NEXT_DAY', 'ACH_SAME_DAY'] },
|
|
191
|
+
sec_code: { type: 'string', enum: ['CCD', 'PPD', 'WEB'] },
|
|
56
192
|
},
|
|
57
|
-
required: [
|
|
193
|
+
required: [],
|
|
58
194
|
},
|
|
59
195
|
},
|
|
60
196
|
{
|
|
61
197
|
name: 'sardis_withdraw',
|
|
62
|
-
description: '
|
|
198
|
+
description: 'Alias for sardis_withdraw_to_bank.',
|
|
63
199
|
inputSchema: {
|
|
64
200
|
type: 'object',
|
|
65
201
|
properties: {
|
|
66
|
-
amount: { type: 'number'
|
|
67
|
-
|
|
68
|
-
account_id: { type: 'string'
|
|
69
|
-
|
|
202
|
+
amount: { type: 'number' },
|
|
203
|
+
amount_minor: { type: 'number' },
|
|
204
|
+
account_id: { type: 'string' },
|
|
205
|
+
financial_account_token: { type: 'string' },
|
|
206
|
+
external_bank_account_token: { type: 'string' },
|
|
70
207
|
},
|
|
71
|
-
required: [
|
|
208
|
+
required: [],
|
|
72
209
|
},
|
|
73
210
|
},
|
|
74
211
|
{
|
|
75
212
|
name: 'sardis_get_funding_status',
|
|
76
|
-
description: '
|
|
213
|
+
description: 'Get status of a treasury payment token.',
|
|
77
214
|
inputSchema: {
|
|
78
215
|
type: 'object',
|
|
79
216
|
properties: {
|
|
80
|
-
|
|
217
|
+
payment_token: { type: 'string' },
|
|
218
|
+
transfer_id: { type: 'string' },
|
|
219
|
+
funding_id: { type: 'string' },
|
|
81
220
|
},
|
|
82
|
-
required: [
|
|
221
|
+
required: [],
|
|
83
222
|
},
|
|
84
223
|
},
|
|
85
224
|
{
|
|
86
225
|
name: 'sardis_get_withdrawal_status',
|
|
87
|
-
description: '
|
|
226
|
+
description: 'Get withdrawal status by treasury payment token.',
|
|
88
227
|
inputSchema: {
|
|
89
228
|
type: 'object',
|
|
90
229
|
properties: {
|
|
91
|
-
|
|
230
|
+
payment_token: { type: 'string' },
|
|
231
|
+
transfer_id: { type: 'string' },
|
|
92
232
|
},
|
|
93
|
-
required: [
|
|
233
|
+
required: [],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'sardis_get_treasury_balances',
|
|
238
|
+
description: 'Get latest treasury balances across linked financial accounts.',
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: 'object',
|
|
241
|
+
properties: {},
|
|
242
|
+
required: [],
|
|
94
243
|
},
|
|
95
244
|
},
|
|
96
245
|
{
|
|
97
246
|
name: 'sardis_list_funding_transactions',
|
|
98
|
-
description: 'List
|
|
247
|
+
description: 'List treasury funding and withdrawal activity (simulated summary in sandbox mode).',
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: 'object',
|
|
250
|
+
properties: {
|
|
251
|
+
type: { type: 'string', enum: ['deposit', 'withdrawal', 'all'] },
|
|
252
|
+
limit: { type: 'number' },
|
|
253
|
+
},
|
|
254
|
+
required: [],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'sardis_get_multi_balance',
|
|
259
|
+
description: 'Get all balances (crypto + fiat + card) for an agent in one unified call.',
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: 'object',
|
|
262
|
+
properties: {
|
|
263
|
+
wallet_id: { type: 'string', description: 'Wallet ID to get balances for' },
|
|
264
|
+
agent_id: { type: 'string', description: 'Agent ID to get balances for' },
|
|
265
|
+
},
|
|
266
|
+
required: [],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'sardis_offramp_usdc_to_fiat',
|
|
271
|
+
description: 'Convert USDC to fiat via best available ramp provider.',
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: 'object',
|
|
274
|
+
properties: {
|
|
275
|
+
amount: { type: 'number', description: 'Amount in major units (USD)' },
|
|
276
|
+
amount_minor: { type: 'number', description: 'Amount in minor units (e.g. cents)' },
|
|
277
|
+
source_chain: { type: 'string', description: 'Source blockchain (e.g. base, polygon, ethereum)' },
|
|
278
|
+
destination_currency: { type: 'string', description: 'Target fiat currency (e.g. USD, EUR)' },
|
|
279
|
+
external_bank_account_token: { type: 'string', description: 'Destination bank account token' },
|
|
280
|
+
wallet_id: { type: 'string', description: 'Source wallet ID' },
|
|
281
|
+
},
|
|
282
|
+
required: [],
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: 'sardis_onramp_fiat_to_usdc',
|
|
287
|
+
description: 'Convert fiat to USDC via best available ramp provider.',
|
|
99
288
|
inputSchema: {
|
|
100
289
|
type: 'object',
|
|
101
290
|
properties: {
|
|
102
|
-
|
|
103
|
-
|
|
291
|
+
amount: { type: 'number', description: 'Amount in major units (USD)' },
|
|
292
|
+
amount_minor: { type: 'number', description: 'Amount in minor units (e.g. cents)' },
|
|
293
|
+
source_currency: { type: 'string', description: 'Source fiat currency (e.g. USD, EUR)' },
|
|
294
|
+
destination_chain: { type: 'string', description: 'Target blockchain (e.g. base, polygon, ethereum)' },
|
|
295
|
+
external_bank_account_token: { type: 'string', description: 'Source bank account token' },
|
|
296
|
+
wallet_id: { type: 'string', description: 'Destination wallet ID' },
|
|
297
|
+
},
|
|
298
|
+
required: [],
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'sardis_get_ramp_quote',
|
|
303
|
+
description: 'Get price quotes from available ramp providers for fiat<>crypto conversion.',
|
|
304
|
+
inputSchema: {
|
|
305
|
+
type: 'object',
|
|
306
|
+
properties: {
|
|
307
|
+
direction: { type: 'string', enum: ['onramp', 'offramp'], description: 'Conversion direction' },
|
|
308
|
+
amount: { type: 'number', description: 'Amount in major units' },
|
|
309
|
+
amount_minor: { type: 'number', description: 'Amount in minor units' },
|
|
310
|
+
source_currency: { type: 'string', description: 'Source currency (USD, EUR, USDC)' },
|
|
311
|
+
destination_currency: { type: 'string', description: 'Destination currency (USD, EUR, USDC)' },
|
|
312
|
+
chain: { type: 'string', description: 'Blockchain for crypto side (e.g. base, polygon)' },
|
|
104
313
|
},
|
|
105
314
|
required: [],
|
|
106
315
|
},
|
|
107
316
|
},
|
|
108
317
|
];
|
|
109
|
-
// Tool handlers
|
|
110
318
|
export const fiatToolHandlers = {
|
|
319
|
+
sardis_sync_treasury_account_holder: async (args) => {
|
|
320
|
+
const parsed = AccountHolderSyncSchema.safeParse(args);
|
|
321
|
+
if (!parsed.success)
|
|
322
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
323
|
+
const config = getConfig();
|
|
324
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
325
|
+
return serialize([
|
|
326
|
+
{
|
|
327
|
+
organization_id: 'org_simulated',
|
|
328
|
+
financial_account_token: 'fa_simulated',
|
|
329
|
+
account_role: 'ISSUING',
|
|
330
|
+
currency: 'USD',
|
|
331
|
+
status: 'OPEN',
|
|
332
|
+
is_program_level: false,
|
|
333
|
+
},
|
|
334
|
+
]);
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
return serialize(await apiRequest('POST', '/api/v2/treasury/account-holders/sync', parsed.data));
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
return { content: [{ type: 'text', text: `Failed to sync account holder: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
sardis_list_financial_accounts: async (args) => {
|
|
344
|
+
const parsed = ListFinancialAccountsSchema.safeParse(args);
|
|
345
|
+
if (!parsed.success)
|
|
346
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
347
|
+
const config = getConfig();
|
|
348
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
349
|
+
return serialize([
|
|
350
|
+
{
|
|
351
|
+
organization_id: 'org_simulated',
|
|
352
|
+
financial_account_token: 'fa_simulated',
|
|
353
|
+
account_role: 'ISSUING',
|
|
354
|
+
currency: 'USD',
|
|
355
|
+
status: 'OPEN',
|
|
356
|
+
is_program_level: false,
|
|
357
|
+
},
|
|
358
|
+
]);
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
const search = new URLSearchParams();
|
|
362
|
+
if (parsed.data.account_token)
|
|
363
|
+
search.set('account_token', parsed.data.account_token);
|
|
364
|
+
search.set('refresh', String(parsed.data.refresh ?? false));
|
|
365
|
+
return serialize(await apiRequest('GET', `/api/v2/treasury/financial-accounts?${search.toString()}`));
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
return { content: [{ type: 'text', text: `Failed to list financial accounts: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
sardis_link_external_bank_account: async (args) => {
|
|
372
|
+
const parsed = LinkExternalBankSchema.safeParse(args);
|
|
373
|
+
if (!parsed.success)
|
|
374
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
375
|
+
const config = getConfig();
|
|
376
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
377
|
+
return serialize({
|
|
378
|
+
organization_id: 'org_simulated',
|
|
379
|
+
external_bank_account_token: 'eba_simulated',
|
|
380
|
+
financial_account_token: parsed.data.financial_account_token,
|
|
381
|
+
owner: parsed.data.owner,
|
|
382
|
+
owner_type: parsed.data.owner_type ?? 'BUSINESS',
|
|
383
|
+
verification_method: parsed.data.verification_method ?? 'MICRO_DEPOSIT',
|
|
384
|
+
verification_state: 'PENDING',
|
|
385
|
+
state: 'ENABLED',
|
|
386
|
+
account_type: parsed.data.account_type ?? 'CHECKING',
|
|
387
|
+
currency: parsed.data.currency ?? 'USD',
|
|
388
|
+
country: parsed.data.country ?? 'USA',
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
return serialize(await apiRequest('POST', '/api/v2/treasury/external-bank-accounts', parsed.data));
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
return { content: [{ type: 'text', text: `Failed to link bank account: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
sardis_verify_micro_deposits: async (args) => {
|
|
399
|
+
const parsed = VerifyMicroDepositsSchema.safeParse(args);
|
|
400
|
+
if (!parsed.success)
|
|
401
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
402
|
+
const config = getConfig();
|
|
403
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
404
|
+
return serialize({
|
|
405
|
+
external_bank_account_token: parsed.data.token,
|
|
406
|
+
verification_state: 'ENABLED',
|
|
407
|
+
state: 'ENABLED',
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
return serialize(await apiRequest('POST', `/api/v2/treasury/external-bank-accounts/${parsed.data.token}/verify-micro-deposits`, { micro_deposits: parsed.data.micro_deposits }));
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
return { content: [{ type: 'text', text: `Failed to verify micro deposits: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
415
|
+
}
|
|
416
|
+
},
|
|
111
417
|
sardis_fund_wallet: async (args) => {
|
|
112
418
|
const parsed = FundWalletSchema.safeParse(args);
|
|
113
|
-
if (!parsed.success)
|
|
114
|
-
return {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
};
|
|
419
|
+
if (!parsed.success)
|
|
420
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
421
|
+
const amountMinor = parsed.data.amount_minor ?? toMinorUnits(parsed.data.amount);
|
|
422
|
+
if (amountMinor <= 0) {
|
|
423
|
+
return { content: [{ type: 'text', text: 'Invalid request: amount or amount_minor is required' }], isError: true };
|
|
118
424
|
}
|
|
119
425
|
const config = getConfig();
|
|
120
|
-
const walletId = parsed.data.wallet_id || config.walletId || 'wallet_default';
|
|
121
|
-
const amount = typeof parsed.data.amount === 'number'
|
|
122
|
-
? parsed.data.amount.toString()
|
|
123
|
-
: parsed.data.amount;
|
|
124
426
|
if (!config.apiKey || config.mode === 'simulated') {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
funding_id: transferId,
|
|
133
|
-
wallet_id: walletId,
|
|
134
|
-
amount: amount,
|
|
135
|
-
source_type: source,
|
|
136
|
-
currency: parsed.data.currency || 'USD',
|
|
137
|
-
status: 'processing',
|
|
138
|
-
estimated_arrival: eta,
|
|
139
|
-
created_at: new Date().toISOString(),
|
|
140
|
-
message: `Funding of $${amount} initiated via ${source.toUpperCase()}`,
|
|
141
|
-
}, null, 2),
|
|
142
|
-
}],
|
|
143
|
-
};
|
|
427
|
+
const simulated = buildSimulatedPayment('fund', 'DEBIT', amountMinor || 10000);
|
|
428
|
+
return serialize({
|
|
429
|
+
...simulated,
|
|
430
|
+
wallet_id: parsed.data.wallet_id || config.walletId || 'wallet_default',
|
|
431
|
+
source_type: parsed.data.source || 'ach',
|
|
432
|
+
message: `Funding initiated for ${((simulated.pending_amount || 0) / 100).toFixed(2)} USD`,
|
|
433
|
+
});
|
|
144
434
|
}
|
|
435
|
+
const validationError = validateLiveTreasuryArgs(parsed.data.financial_account_token, parsed.data.external_bank_account_token, amountMinor);
|
|
436
|
+
if (validationError)
|
|
437
|
+
return { content: [{ type: 'text', text: `Invalid request: ${validationError}` }], isError: true };
|
|
145
438
|
try {
|
|
146
|
-
const result = await apiRequest('POST', '/api/v2/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
439
|
+
const result = await apiRequest('POST', '/api/v2/treasury/fund', {
|
|
440
|
+
financial_account_token: parsed.data.financial_account_token,
|
|
441
|
+
external_bank_account_token: parsed.data.external_bank_account_token,
|
|
442
|
+
amount_minor: amountMinor,
|
|
443
|
+
method: parsed.data.method ?? 'ACH_NEXT_DAY',
|
|
444
|
+
sec_code: parsed.data.sec_code ?? 'CCD',
|
|
445
|
+
memo: parsed.data.memo,
|
|
446
|
+
idempotency_key: parsed.data.idempotency_key,
|
|
447
|
+
user_defined_id: parsed.data.user_defined_id,
|
|
448
|
+
});
|
|
449
|
+
return serialize({ ...result, funding_id: result.payment_token });
|
|
150
450
|
}
|
|
151
451
|
catch (error) {
|
|
152
|
-
return {
|
|
153
|
-
content: [{
|
|
154
|
-
type: 'text',
|
|
155
|
-
text: `Failed to fund wallet: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
156
|
-
}],
|
|
157
|
-
isError: true,
|
|
158
|
-
};
|
|
452
|
+
return { content: [{ type: 'text', text: `Failed to fund wallet: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
159
453
|
}
|
|
160
454
|
},
|
|
161
455
|
sardis_withdraw_to_bank: async (args) => {
|
|
162
456
|
const parsed = WithdrawSchema.safeParse(args);
|
|
163
|
-
if (!parsed.success)
|
|
164
|
-
return {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
};
|
|
457
|
+
if (!parsed.success)
|
|
458
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
459
|
+
const amountMinor = parsed.data.amount_minor ?? toMinorUnits(parsed.data.amount);
|
|
460
|
+
if (amountMinor <= 0) {
|
|
461
|
+
return { content: [{ type: 'text', text: 'Invalid request: amount or amount_minor is required' }], isError: true };
|
|
168
462
|
}
|
|
463
|
+
const externalBankAccountToken = parsed.data.external_bank_account_token ?? parsed.data.destination_id ?? parsed.data.account_id;
|
|
169
464
|
const config = getConfig();
|
|
170
465
|
if (!config.apiKey || config.mode === 'simulated') {
|
|
171
|
-
const
|
|
172
|
-
return {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
amount: parsed.data.amount,
|
|
179
|
-
destination_bank: '****1234',
|
|
180
|
-
status: 'processing',
|
|
181
|
-
estimated_arrival: '1-2 business days',
|
|
182
|
-
created_at: new Date().toISOString(),
|
|
183
|
-
message: `Withdrawal of $${parsed.data.amount} initiated`,
|
|
184
|
-
}, null, 2),
|
|
185
|
-
}],
|
|
186
|
-
};
|
|
466
|
+
const simulated = buildSimulatedPayment('wd', 'CREDIT', amountMinor || 10000);
|
|
467
|
+
return serialize({
|
|
468
|
+
...simulated,
|
|
469
|
+
wallet_id: parsed.data.wallet_id || config.walletId || 'wallet_default',
|
|
470
|
+
destination_bank: externalBankAccountToken || 'eba_simulated',
|
|
471
|
+
estimated_arrival: '1-2 business days',
|
|
472
|
+
});
|
|
187
473
|
}
|
|
474
|
+
const validationError = validateLiveTreasuryArgs(parsed.data.financial_account_token, externalBankAccountToken, amountMinor);
|
|
475
|
+
if (validationError)
|
|
476
|
+
return { content: [{ type: 'text', text: `Invalid request: ${validationError}` }], isError: true };
|
|
188
477
|
try {
|
|
189
|
-
const result = await apiRequest('POST', '/api/v2/
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
478
|
+
const result = await apiRequest('POST', '/api/v2/treasury/withdraw', {
|
|
479
|
+
financial_account_token: parsed.data.financial_account_token,
|
|
480
|
+
external_bank_account_token: externalBankAccountToken,
|
|
481
|
+
amount_minor: amountMinor,
|
|
482
|
+
method: parsed.data.method ?? 'ACH_NEXT_DAY',
|
|
483
|
+
sec_code: parsed.data.sec_code ?? 'CCD',
|
|
484
|
+
memo: parsed.data.memo,
|
|
485
|
+
idempotency_key: parsed.data.idempotency_key,
|
|
486
|
+
user_defined_id: parsed.data.user_defined_id,
|
|
487
|
+
});
|
|
488
|
+
return serialize({ ...result, withdrawal_id: result.payment_token });
|
|
193
489
|
}
|
|
194
490
|
catch (error) {
|
|
195
|
-
return {
|
|
196
|
-
content: [{
|
|
197
|
-
type: 'text',
|
|
198
|
-
text: `Failed to withdraw: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
199
|
-
}],
|
|
200
|
-
isError: true,
|
|
201
|
-
};
|
|
491
|
+
return { content: [{ type: 'text', text: `Failed to withdraw: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
202
492
|
}
|
|
203
493
|
},
|
|
204
494
|
sardis_get_funding_status: async (args) => {
|
|
205
495
|
const parsed = StatusSchema.safeParse(args);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
};
|
|
212
|
-
}
|
|
496
|
+
if (!parsed.success)
|
|
497
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
498
|
+
const token = parsed.data.payment_token || parsed.data.transfer_id || parsed.data.funding_id;
|
|
499
|
+
if (!token)
|
|
500
|
+
return { content: [{ type: 'text', text: 'Invalid request: payment_token or transfer_id is required' }], isError: true };
|
|
213
501
|
const config = getConfig();
|
|
214
502
|
if (!config.apiKey || config.mode === 'simulated') {
|
|
215
|
-
return {
|
|
216
|
-
content: [{
|
|
217
|
-
type: 'text',
|
|
218
|
-
text: JSON.stringify({
|
|
219
|
-
funding_id: transferId,
|
|
220
|
-
id: transferId,
|
|
221
|
-
type: 'funding',
|
|
222
|
-
status: 'completed',
|
|
223
|
-
amount: '100.00',
|
|
224
|
-
completed_at: new Date().toISOString(),
|
|
225
|
-
}, null, 2),
|
|
226
|
-
}],
|
|
227
|
-
};
|
|
503
|
+
return serialize({ payment_token: token, funding_id: token, status: 'SETTLED', type: 'funding' });
|
|
228
504
|
}
|
|
229
505
|
try {
|
|
230
|
-
const result = await apiRequest('GET', `/api/v2/
|
|
231
|
-
return {
|
|
232
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
233
|
-
};
|
|
506
|
+
const result = await apiRequest('GET', `/api/v2/treasury/payments/${token}`);
|
|
507
|
+
return serialize({ ...result, funding_id: result.payment_token });
|
|
234
508
|
}
|
|
235
509
|
catch (error) {
|
|
236
|
-
return {
|
|
237
|
-
content: [{
|
|
238
|
-
type: 'text',
|
|
239
|
-
text: `Failed to get status: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
240
|
-
}],
|
|
241
|
-
isError: true,
|
|
242
|
-
};
|
|
510
|
+
return { content: [{ type: 'text', text: `Failed to get status: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
243
511
|
}
|
|
244
512
|
},
|
|
245
513
|
sardis_get_withdrawal_status: async (args) => {
|
|
246
514
|
const parsed = StatusSchema.safeParse(args);
|
|
247
|
-
if (!parsed.success)
|
|
248
|
-
return {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
};
|
|
252
|
-
}
|
|
515
|
+
if (!parsed.success)
|
|
516
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
517
|
+
const token = parsed.data.payment_token || parsed.data.transfer_id;
|
|
518
|
+
if (!token)
|
|
519
|
+
return { content: [{ type: 'text', text: 'Invalid request: payment_token or transfer_id is required' }], isError: true };
|
|
253
520
|
const config = getConfig();
|
|
254
521
|
if (!config.apiKey || config.mode === 'simulated') {
|
|
255
|
-
return {
|
|
256
|
-
content: [{
|
|
257
|
-
type: 'text',
|
|
258
|
-
text: JSON.stringify({
|
|
259
|
-
id: parsed.data.transfer_id,
|
|
260
|
-
type: 'withdrawal',
|
|
261
|
-
status: 'processing',
|
|
262
|
-
amount: '100.00',
|
|
263
|
-
estimated_arrival: '1-2 business days',
|
|
264
|
-
}, null, 2),
|
|
265
|
-
}],
|
|
266
|
-
};
|
|
522
|
+
return serialize({ payment_token: token, withdrawal_id: token, status: 'PROCESSING', type: 'withdrawal' });
|
|
267
523
|
}
|
|
268
524
|
try {
|
|
269
|
-
const result = await apiRequest('GET', `/api/v2/
|
|
270
|
-
return {
|
|
271
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
272
|
-
};
|
|
525
|
+
const result = await apiRequest('GET', `/api/v2/treasury/payments/${token}`);
|
|
526
|
+
return serialize({ ...result, withdrawal_id: result.payment_token });
|
|
273
527
|
}
|
|
274
528
|
catch (error) {
|
|
275
|
-
return {
|
|
276
|
-
content: [{
|
|
277
|
-
type: 'text',
|
|
278
|
-
text: `Failed to get status: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
279
|
-
}],
|
|
280
|
-
isError: true,
|
|
281
|
-
};
|
|
529
|
+
return { content: [{ type: 'text', text: `Failed to get status: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
282
530
|
}
|
|
283
531
|
},
|
|
284
|
-
|
|
532
|
+
sardis_get_treasury_balances: async () => {
|
|
285
533
|
const config = getConfig();
|
|
286
|
-
const amount = typeof args === 'object' && args !== null && 'amount' in args
|
|
287
|
-
? args.amount
|
|
288
|
-
: 0;
|
|
289
|
-
const accountId = typeof args === 'object' && args !== null && 'account_id' in args
|
|
290
|
-
? args.account_id
|
|
291
|
-
: 'acct_default';
|
|
292
|
-
const walletId = typeof args === 'object' && args !== null && 'wallet_id' in args
|
|
293
|
-
? args.wallet_id || config.walletId
|
|
294
|
-
: config.walletId;
|
|
295
|
-
if (amount === 0) {
|
|
296
|
-
return {
|
|
297
|
-
content: [{ type: 'text', text: 'Invalid request: amount is required' }],
|
|
298
|
-
isError: true,
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
534
|
if (!config.apiKey || config.mode === 'simulated') {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
status: 'processing',
|
|
313
|
-
estimated_arrival: '1-2 business days',
|
|
314
|
-
created_at: new Date().toISOString(),
|
|
315
|
-
message: `Withdrawal of $${amount} initiated`,
|
|
316
|
-
}, null, 2),
|
|
317
|
-
}],
|
|
318
|
-
};
|
|
535
|
+
return serialize([
|
|
536
|
+
{
|
|
537
|
+
organization_id: 'org_simulated',
|
|
538
|
+
financial_account_token: 'fa_simulated',
|
|
539
|
+
currency: 'USD',
|
|
540
|
+
available_amount_minor: 250000,
|
|
541
|
+
pending_amount_minor: 10000,
|
|
542
|
+
total_amount_minor: 260000,
|
|
543
|
+
},
|
|
544
|
+
]);
|
|
319
545
|
}
|
|
320
546
|
try {
|
|
321
|
-
|
|
322
|
-
wallet_id: walletId,
|
|
323
|
-
amount: amount.toString(),
|
|
324
|
-
destination_id: accountId,
|
|
325
|
-
});
|
|
326
|
-
return {
|
|
327
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
328
|
-
};
|
|
547
|
+
return serialize(await apiRequest('GET', '/api/v2/treasury/balances'));
|
|
329
548
|
}
|
|
330
549
|
catch (error) {
|
|
331
|
-
return {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
};
|
|
550
|
+
return { content: [{ type: 'text', text: `Failed to fetch balances: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
sardis_withdraw: async (args) => {
|
|
554
|
+
const withdrawToBankHandler = fiatToolHandlers['sardis_withdraw_to_bank'];
|
|
555
|
+
if (!withdrawToBankHandler) {
|
|
556
|
+
return { content: [{ type: 'text', text: 'withdraw handler is unavailable' }], isError: true };
|
|
338
557
|
}
|
|
558
|
+
return withdrawToBankHandler(args);
|
|
339
559
|
},
|
|
340
560
|
sardis_list_funding_transactions: async (args) => {
|
|
561
|
+
const parsed = FundingListSchema.safeParse(args);
|
|
562
|
+
if (!parsed.success)
|
|
563
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
341
564
|
const config = getConfig();
|
|
342
|
-
const typeFilter =
|
|
343
|
-
|
|
344
|
-
: 'all';
|
|
565
|
+
const typeFilter = parsed.data.type ?? 'all';
|
|
566
|
+
const limit = parsed.data.limit ?? 20;
|
|
345
567
|
if (!config.apiKey || config.mode === 'simulated') {
|
|
346
568
|
const mockTransactions = [
|
|
347
|
-
{
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
569
|
+
{ id: 'fund_001', type: 'deposit', amount_minor: 100000, status: 'SETTLED' },
|
|
570
|
+
{ id: 'wd_001', type: 'withdrawal', amount_minor: 25000, status: 'PENDING' },
|
|
571
|
+
];
|
|
572
|
+
const filtered = typeFilter === 'all' ? mockTransactions : mockTransactions.filter((tx) => tx.type === typeFilter);
|
|
573
|
+
return serialize(filtered.slice(0, limit));
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
content: [{
|
|
577
|
+
type: 'text',
|
|
578
|
+
text: JSON.stringify({
|
|
579
|
+
message: 'List endpoint is not exposed yet. Use sardis_get_funding_status/sardis_get_withdrawal_status per payment token.',
|
|
580
|
+
supported_paths: ['/api/v2/treasury/payments/{payment_token}', '/api/v2/treasury/balances'],
|
|
581
|
+
}, null, 2),
|
|
582
|
+
}],
|
|
583
|
+
};
|
|
584
|
+
},
|
|
585
|
+
sardis_get_multi_balance: async (args) => {
|
|
586
|
+
const parsed = z.object({
|
|
587
|
+
wallet_id: z.string().optional(),
|
|
588
|
+
agent_id: z.string().optional(),
|
|
589
|
+
}).safeParse(args);
|
|
590
|
+
if (!parsed.success)
|
|
591
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
592
|
+
const config = getConfig();
|
|
593
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
594
|
+
return serialize({
|
|
595
|
+
wallet_id: parsed.data.wallet_id || config.walletId || 'wallet_default',
|
|
596
|
+
agent_id: parsed.data.agent_id || 'agent_default',
|
|
597
|
+
crypto: {
|
|
598
|
+
base: { USDC: { available_minor: 500000, pending_minor: 0, total_minor: 500000 } },
|
|
599
|
+
polygon: { USDC: { available_minor: 250000, pending_minor: 0, total_minor: 250000 } },
|
|
354
600
|
},
|
|
355
|
-
{
|
|
356
|
-
|
|
357
|
-
type: 'withdrawal',
|
|
358
|
-
amount: '200.00',
|
|
359
|
-
status: 'processing',
|
|
360
|
-
destination: 'bank_account',
|
|
361
|
-
created_at: new Date(Date.now() - 3600000).toISOString(),
|
|
601
|
+
fiat: {
|
|
602
|
+
USD: { available_minor: 250000, pending_minor: 10000, total_minor: 260000 },
|
|
362
603
|
},
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
604
|
+
cards: {
|
|
605
|
+
total_limit_minor: 1000000,
|
|
606
|
+
total_spent_minor: 125000,
|
|
607
|
+
available_minor: 875000,
|
|
608
|
+
},
|
|
609
|
+
total_value_usd_minor: 1135000,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
try {
|
|
613
|
+
const search = new URLSearchParams();
|
|
614
|
+
if (parsed.data.wallet_id)
|
|
615
|
+
search.set('wallet_id', parsed.data.wallet_id);
|
|
616
|
+
if (parsed.data.agent_id)
|
|
617
|
+
search.set('agent_id', parsed.data.agent_id);
|
|
618
|
+
return serialize(await apiRequest('GET', `/api/v2/treasury/multi-balance?${search.toString()}`));
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
return { content: [{ type: 'text', text: `Failed to fetch multi-balance: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
sardis_offramp_usdc_to_fiat: async (args) => {
|
|
625
|
+
const parsed = z.object({
|
|
626
|
+
amount: z.union([z.string(), z.number()]).optional(),
|
|
627
|
+
amount_minor: z.number().int().positive().optional(),
|
|
628
|
+
source_chain: z.string().optional(),
|
|
629
|
+
destination_currency: z.string().optional(),
|
|
630
|
+
external_bank_account_token: z.string().optional(),
|
|
631
|
+
wallet_id: z.string().optional(),
|
|
632
|
+
}).safeParse(args);
|
|
633
|
+
if (!parsed.success)
|
|
634
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
635
|
+
const amountMinor = parsed.data.amount_minor ?? toMinorUnits(parsed.data.amount);
|
|
636
|
+
if (amountMinor <= 0) {
|
|
637
|
+
return { content: [{ type: 'text', text: 'Invalid request: amount or amount_minor is required' }], isError: true };
|
|
638
|
+
}
|
|
639
|
+
const config = getConfig();
|
|
640
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
641
|
+
return serialize({
|
|
642
|
+
offramp_id: `offramp_${Date.now().toString(36)}`,
|
|
643
|
+
status: 'PENDING',
|
|
644
|
+
source_chain: parsed.data.source_chain || 'base',
|
|
645
|
+
source_token: 'USDC',
|
|
646
|
+
source_amount_minor: amountMinor,
|
|
647
|
+
destination_currency: parsed.data.destination_currency || 'USD',
|
|
648
|
+
destination_amount_minor: Math.round(amountMinor * 0.998),
|
|
649
|
+
fee_minor: Math.round(amountMinor * 0.002),
|
|
650
|
+
exchange_rate: 1.0,
|
|
651
|
+
estimated_settlement: '1-2 business days',
|
|
652
|
+
provider: 'bridge-xyz',
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
return serialize(await apiRequest('POST', '/api/v2/treasury/offramp', {
|
|
657
|
+
amount_minor: amountMinor,
|
|
658
|
+
source_chain: parsed.data.source_chain || 'base',
|
|
659
|
+
destination_currency: parsed.data.destination_currency || 'USD',
|
|
660
|
+
external_bank_account_token: parsed.data.external_bank_account_token,
|
|
661
|
+
wallet_id: parsed.data.wallet_id,
|
|
662
|
+
}));
|
|
663
|
+
}
|
|
664
|
+
catch (error) {
|
|
665
|
+
return { content: [{ type: 'text', text: `Failed to execute offramp: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
sardis_onramp_fiat_to_usdc: async (args) => {
|
|
669
|
+
const parsed = z.object({
|
|
670
|
+
amount: z.union([z.string(), z.number()]).optional(),
|
|
671
|
+
amount_minor: z.number().int().positive().optional(),
|
|
672
|
+
source_currency: z.string().optional(),
|
|
673
|
+
destination_chain: z.string().optional(),
|
|
674
|
+
external_bank_account_token: z.string().optional(),
|
|
675
|
+
wallet_id: z.string().optional(),
|
|
676
|
+
}).safeParse(args);
|
|
677
|
+
if (!parsed.success)
|
|
678
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
679
|
+
const amountMinor = parsed.data.amount_minor ?? toMinorUnits(parsed.data.amount);
|
|
680
|
+
if (amountMinor <= 0) {
|
|
681
|
+
return { content: [{ type: 'text', text: 'Invalid request: amount or amount_minor is required' }], isError: true };
|
|
682
|
+
}
|
|
683
|
+
const config = getConfig();
|
|
684
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
685
|
+
return serialize({
|
|
686
|
+
onramp_id: `onramp_${Date.now().toString(36)}`,
|
|
687
|
+
status: 'PENDING',
|
|
688
|
+
source_currency: parsed.data.source_currency || 'USD',
|
|
689
|
+
source_amount_minor: amountMinor,
|
|
690
|
+
destination_chain: parsed.data.destination_chain || 'base',
|
|
691
|
+
destination_token: 'USDC',
|
|
692
|
+
destination_amount_minor: Math.round(amountMinor * 0.997),
|
|
693
|
+
fee_minor: Math.round(amountMinor * 0.003),
|
|
694
|
+
exchange_rate: 1.0,
|
|
695
|
+
estimated_settlement: '10-30 minutes',
|
|
696
|
+
provider: 'bridge-xyz',
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
try {
|
|
700
|
+
return serialize(await apiRequest('POST', '/api/v2/treasury/onramp', {
|
|
701
|
+
amount_minor: amountMinor,
|
|
702
|
+
source_currency: parsed.data.source_currency || 'USD',
|
|
703
|
+
destination_chain: parsed.data.destination_chain || 'base',
|
|
704
|
+
external_bank_account_token: parsed.data.external_bank_account_token,
|
|
705
|
+
wallet_id: parsed.data.wallet_id,
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
return { content: [{ type: 'text', text: `Failed to execute onramp: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
710
|
+
}
|
|
711
|
+
},
|
|
712
|
+
sardis_get_ramp_quote: async (args) => {
|
|
713
|
+
const parsed = z.object({
|
|
714
|
+
direction: z.enum(['onramp', 'offramp']).optional(),
|
|
715
|
+
amount: z.union([z.string(), z.number()]).optional(),
|
|
716
|
+
amount_minor: z.number().int().positive().optional(),
|
|
717
|
+
source_currency: z.string().optional(),
|
|
718
|
+
destination_currency: z.string().optional(),
|
|
719
|
+
chain: z.string().optional(),
|
|
720
|
+
}).safeParse(args);
|
|
721
|
+
if (!parsed.success)
|
|
722
|
+
return { content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }], isError: true };
|
|
723
|
+
const amountMinor = parsed.data.amount_minor ?? toMinorUnits(parsed.data.amount);
|
|
724
|
+
if (amountMinor <= 0) {
|
|
725
|
+
return { content: [{ type: 'text', text: 'Invalid request: amount or amount_minor is required' }], isError: true };
|
|
726
|
+
}
|
|
727
|
+
const config = getConfig();
|
|
728
|
+
if (!config.apiKey || config.mode === 'simulated') {
|
|
729
|
+
const direction = parsed.data.direction || 'offramp';
|
|
730
|
+
const fee = direction === 'offramp' ? 0.002 : 0.003;
|
|
731
|
+
return serialize({
|
|
732
|
+
quotes: [
|
|
733
|
+
{
|
|
734
|
+
provider: 'bridge-xyz',
|
|
735
|
+
direction,
|
|
736
|
+
source_currency: parsed.data.source_currency || (direction === 'onramp' ? 'USD' : 'USDC'),
|
|
737
|
+
destination_currency: parsed.data.destination_currency || (direction === 'onramp' ? 'USDC' : 'USD'),
|
|
738
|
+
source_amount_minor: amountMinor,
|
|
739
|
+
destination_amount_minor: Math.round(amountMinor * (1 - fee)),
|
|
740
|
+
fee_minor: Math.round(amountMinor * fee),
|
|
741
|
+
exchange_rate: 1.0,
|
|
742
|
+
estimated_time: direction === 'onramp' ? '10-30 minutes' : '1-2 business days',
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
provider: 'circle',
|
|
746
|
+
direction,
|
|
747
|
+
source_currency: parsed.data.source_currency || (direction === 'onramp' ? 'USD' : 'USDC'),
|
|
748
|
+
destination_currency: parsed.data.destination_currency || (direction === 'onramp' ? 'USDC' : 'USD'),
|
|
749
|
+
source_amount_minor: amountMinor,
|
|
750
|
+
destination_amount_minor: Math.round(amountMinor * (1 - fee - 0.001)),
|
|
751
|
+
fee_minor: Math.round(amountMinor * (fee + 0.001)),
|
|
752
|
+
exchange_rate: 1.0,
|
|
753
|
+
estimated_time: direction === 'onramp' ? '5-15 minutes' : '1-3 business days',
|
|
754
|
+
},
|
|
755
|
+
],
|
|
756
|
+
best_quote: 'bridge-xyz',
|
|
757
|
+
});
|
|
373
758
|
}
|
|
374
759
|
try {
|
|
375
|
-
const
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
760
|
+
const search = new URLSearchParams();
|
|
761
|
+
if (parsed.data.direction)
|
|
762
|
+
search.set('direction', parsed.data.direction);
|
|
763
|
+
search.set('amount_minor', String(amountMinor));
|
|
764
|
+
if (parsed.data.source_currency)
|
|
765
|
+
search.set('source_currency', parsed.data.source_currency);
|
|
766
|
+
if (parsed.data.destination_currency)
|
|
767
|
+
search.set('destination_currency', parsed.data.destination_currency);
|
|
768
|
+
if (parsed.data.chain)
|
|
769
|
+
search.set('chain', parsed.data.chain);
|
|
770
|
+
return serialize(await apiRequest('GET', `/api/v2/treasury/ramp-quote?${search.toString()}`));
|
|
382
771
|
}
|
|
383
772
|
catch (error) {
|
|
384
|
-
return {
|
|
385
|
-
content: [{
|
|
386
|
-
type: 'text',
|
|
387
|
-
text: `Failed to list transactions: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
388
|
-
}],
|
|
389
|
-
isError: true,
|
|
390
|
-
};
|
|
773
|
+
return { content: [{ type: 'text', text: `Failed to get ramp quote: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true };
|
|
391
774
|
}
|
|
392
775
|
},
|
|
393
776
|
};
|