@sardis/mcp-server 0.2.7 → 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.
Files changed (50) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/CLAUDE_DESKTOP_CONFIG.md +10 -19
  3. package/README.md +179 -221
  4. package/dist/api.d.ts +1 -1
  5. package/dist/api.d.ts.map +1 -1
  6. package/dist/api.js +4 -2
  7. package/dist/api.js.map +1 -1
  8. package/dist/cli.js +185 -20
  9. package/dist/cli.js.map +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/tools/events.d.ts +7 -0
  15. package/dist/tools/events.d.ts.map +1 -0
  16. package/dist/tools/events.js +413 -0
  17. package/dist/tools/events.js.map +1 -0
  18. package/dist/tools/fiat.d.ts +2 -2
  19. package/dist/tools/fiat.d.ts.map +1 -1
  20. package/dist/tools/fiat.js +649 -266
  21. package/dist/tools/fiat.js.map +1 -1
  22. package/dist/tools/guardrails.d.ts +7 -0
  23. package/dist/tools/guardrails.d.ts.map +1 -0
  24. package/dist/tools/guardrails.js +379 -0
  25. package/dist/tools/guardrails.js.map +1 -0
  26. package/dist/tools/index.d.ts +26 -8
  27. package/dist/tools/index.d.ts.map +1 -1
  28. package/dist/tools/index.js +80 -5
  29. package/dist/tools/index.js.map +1 -1
  30. package/dist/tools/payments.d.ts +2 -0
  31. package/dist/tools/payments.d.ts.map +1 -1
  32. package/dist/tools/payments.js +2 -0
  33. package/dist/tools/payments.js.map +1 -1
  34. package/dist/tools/policy.d.ts.map +1 -1
  35. package/dist/tools/policy.js +32 -4
  36. package/dist/tools/policy.js.map +1 -1
  37. package/dist/tools/sandbox.js +1 -1
  38. package/dist/tools/types.d.ts +51 -0
  39. package/dist/tools/types.d.ts.map +1 -1
  40. package/dist/tools/types.js +20 -0
  41. package/dist/tools/types.js.map +1 -1
  42. package/dist/tools/wallet-management.d.ts.map +1 -1
  43. package/dist/tools/wallet-management.js +55 -45
  44. package/dist/tools/wallet-management.js.map +1 -1
  45. package/dist/version.d.ts +2 -0
  46. package/dist/version.d.ts.map +1 -0
  47. package/dist/version.js +2 -0
  48. package/dist/version.js.map +1 -0
  49. package/mcp.json +139 -53
  50. package/package.json +39 -24
@@ -1,393 +1,776 @@
1
1
  /**
2
- * Fiat Rails tools for MCP server
2
+ * Fiat/Treasury tools for MCP server
3
3
  *
4
- * Tools for funding wallets from bank accounts and withdrawing to fiat.
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
- // Schemas
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('Wallet to fund'),
12
- amount: z.union([z.string(), z.number()]).describe('Amount to fund in USD'),
13
- source: z.enum(['ach', 'wire', 'card', 'bank_account']).optional().describe('Funding source type'),
14
- currency: z.string().optional().describe('Currency code'),
15
- source_id: z.string().optional().describe('Saved payment method ID'),
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().describe('Wallet to withdraw from'),
19
- amount: z.string().describe('Amount to withdraw in USD'),
20
- destination_id: z.string().describe('Bank account ID to withdraw to'),
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
- transfer_id: z.string().optional().describe('Transfer ID to check'),
24
- funding_id: z.string().optional().describe('Funding ID to check (alias for transfer_id)'),
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
- // Tool definitions
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: 'Fund a wallet from a bank account, wire transfer, or card. Use this to add funds to a wallet. USD is automatically converted to USDC.',
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: 'Amount to fund in USD' },
35
- wallet_id: { type: 'string', description: 'Wallet to fund' },
36
- source: {
37
- type: 'string',
38
- enum: ['ach', 'wire', 'card', 'bank_account'],
39
- description: 'Funding source type. ACH is free but slower, wire is faster, card has fees.',
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: ['amount'],
177
+ required: [],
45
178
  },
46
179
  },
47
180
  {
48
181
  name: 'sardis_withdraw_to_bank',
49
- description: 'Withdraw funds from wallet to a bank account. USDC is converted to USD.',
182
+ description: 'Withdraw treasury funds to an external bank account via ACH payment.',
50
183
  inputSchema: {
51
184
  type: 'object',
52
185
  properties: {
53
- wallet_id: { type: 'string', description: 'Wallet to withdraw from' },
54
- amount: { type: 'string', description: 'Amount to withdraw in USD' },
55
- destination_id: { type: 'string', description: 'Bank account ID to withdraw to' },
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: ['wallet_id', 'amount', 'destination_id'],
193
+ required: [],
58
194
  },
59
195
  },
60
196
  {
61
197
  name: 'sardis_withdraw',
62
- description: 'Withdraw funds from wallet to a bank account (alias for sardis_withdraw_to_bank).',
198
+ description: 'Alias for sardis_withdraw_to_bank.',
63
199
  inputSchema: {
64
200
  type: 'object',
65
201
  properties: {
66
- amount: { type: 'number', description: 'Amount to withdraw' },
67
- destination: { type: 'string', description: 'Destination type (bank_account, wire)' },
68
- account_id: { type: 'string', description: 'Bank account ID to withdraw to' },
69
- wallet_id: { type: 'string', description: 'Wallet to withdraw from' },
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: ['amount'],
208
+ required: [],
72
209
  },
73
210
  },
74
211
  {
75
212
  name: 'sardis_get_funding_status',
76
- description: 'Check the status of a funding transfer.',
213
+ description: 'Get status of a treasury payment token.',
77
214
  inputSchema: {
78
215
  type: 'object',
79
216
  properties: {
80
- transfer_id: { type: 'string', description: 'Funding transfer ID' },
217
+ payment_token: { type: 'string' },
218
+ transfer_id: { type: 'string' },
219
+ funding_id: { type: 'string' },
81
220
  },
82
- required: ['transfer_id'],
221
+ required: [],
83
222
  },
84
223
  },
85
224
  {
86
225
  name: 'sardis_get_withdrawal_status',
87
- description: 'Check the status of a withdrawal to bank.',
226
+ description: 'Get withdrawal status by treasury payment token.',
88
227
  inputSchema: {
89
228
  type: 'object',
90
229
  properties: {
91
- transfer_id: { type: 'string', description: 'Withdrawal transfer ID' },
230
+ payment_token: { type: 'string' },
231
+ transfer_id: { type: 'string' },
92
232
  },
93
- required: ['transfer_id'],
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 all funding and withdrawal transactions.',
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
- type: { type: 'string', enum: ['deposit', 'withdrawal', 'all'], description: 'Filter by transaction type' },
103
- limit: { type: 'number', description: 'Maximum number of results' },
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
- content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }],
116
- isError: true,
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 transferId = `fund_${Date.now().toString(36)}`;
126
- const source = parsed.data.source || 'ach';
127
- const eta = source === 'wire' ? '1 hour' : source === 'card' ? '5 minutes' : '2-3 business days';
128
- return {
129
- content: [{
130
- type: 'text',
131
- text: JSON.stringify({
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/fiat/fund', parsed.data);
147
- return {
148
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
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
- content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }],
166
- isError: true,
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 transferId = `wd_${Date.now().toString(36)}`;
172
- return {
173
- content: [{
174
- type: 'text',
175
- text: JSON.stringify({
176
- id: transferId,
177
- wallet_id: parsed.data.wallet_id,
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/fiat/withdraw', parsed.data);
190
- return {
191
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
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
- const transferId = parsed.success && (parsed.data.transfer_id || parsed.data.funding_id);
207
- if (!transferId) {
208
- return {
209
- content: [{ type: 'text', text: 'Invalid request: transfer_id or funding_id is required' }],
210
- isError: true,
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/fiat/status/${transferId}`);
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
- content: [{ type: 'text', text: `Invalid request: ${parsed.error.message}` }],
250
- isError: true,
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/fiat/status/${parsed.data.transfer_id}`);
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
- sardis_withdraw: async (args) => {
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
- const transferId = `wd_${Date.now().toString(36)}`;
303
- return {
304
- content: [{
305
- type: 'text',
306
- text: JSON.stringify({
307
- withdrawal_id: transferId,
308
- wallet_id: walletId,
309
- amount: amount.toString(),
310
- destination_bank: '****1234',
311
- account_id: accountId,
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
- const result = await apiRequest('POST', '/api/v2/fiat/withdraw', {
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
- content: [{
333
- type: 'text',
334
- text: `Failed to withdraw: ${error instanceof Error ? error.message : 'Unknown error'}`,
335
- }],
336
- isError: true,
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 = typeof args === 'object' && args !== null && 'type' in args
343
- ? args.type
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
- id: 'fund_001',
349
- type: 'deposit',
350
- amount: '1000.00',
351
- status: 'completed',
352
- source: 'bank_account',
353
- created_at: new Date(Date.now() - 86400000).toISOString(),
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
- id: 'wd_001',
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
- const filtered = typeFilter === 'all'
365
- ? mockTransactions
366
- : mockTransactions.filter(t => t.type === typeFilter);
367
- return {
368
- content: [{
369
- type: 'text',
370
- text: JSON.stringify(filtered, null, 2),
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 params = new URLSearchParams();
376
- if (typeFilter && typeFilter !== 'all')
377
- params.append('type', typeFilter);
378
- const result = await apiRequest('GET', `/api/v2/fiat/transactions?${params}`);
379
- return {
380
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
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
  };