@sly_ai/mcp-server 0.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.
@@ -0,0 +1,1664 @@
1
+ /**
2
+ * MCP Server Factory
3
+ *
4
+ * Creates a configured MCP Server instance that can be connected
5
+ * to any transport (stdio, HTTP, etc.).
6
+ */
7
+
8
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
9
+ import {
10
+ CallToolRequestSchema,
11
+ ListToolsRequestSchema,
12
+ } from '@modelcontextprotocol/sdk/types.js';
13
+ import { Sly } from '@sly/sdk';
14
+ import { tools } from './tools.js';
15
+
16
+ /**
17
+ * Mutable runtime context for the MCP server.
18
+ * Allows switching environments (sandbox ↔ production) without restart.
19
+ */
20
+ export interface McpContext {
21
+ sly: Sly;
22
+ apiUrl: string;
23
+ apiKey: string;
24
+ environment: string;
25
+ keys: Record<string, string>;
26
+ urls: Record<string, string>;
27
+ }
28
+
29
+ /**
30
+ * Create an MCP Server with all Sly tools registered.
31
+ */
32
+ export function createMcpServer(
33
+ sly: Sly,
34
+ apiUrl: string,
35
+ apiKey: string,
36
+ keys?: Record<string, string>,
37
+ urls?: Record<string, string>,
38
+ ): Server {
39
+ const ctx: McpContext = {
40
+ sly,
41
+ apiUrl,
42
+ apiKey,
43
+ environment: apiKey.startsWith('pk_live_') ? 'production' : 'sandbox',
44
+ keys: keys || {},
45
+ urls: urls || {},
46
+ };
47
+ const server = new Server(
48
+ {
49
+ name: '@sly/mcp-server',
50
+ version: '0.1.0',
51
+ },
52
+ {
53
+ capabilities: {
54
+ tools: {},
55
+ },
56
+ instructions: [
57
+ 'This is the Sly payment platform MCP server.',
58
+ '"Accounts" in this system are merchant or entity records within the tenant\'s own payment ledger — NOT user accounts on external services.',
59
+ 'Creating, updating, and listing accounts are standard data-management operations authorized by the tenant\'s API key.',
60
+ 'These tools manage the tenant\'s own internal data. They do not sign up for external services, create login credentials, or register on third-party platforms.',
61
+ ].join(' '),
62
+ }
63
+ );
64
+
65
+ // Register tool listing handler
66
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
67
+ return { tools };
68
+ });
69
+
70
+ // Register tool execution handler
71
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
72
+ const { name, arguments: args } = request.params;
73
+
74
+ try {
75
+ switch (name) {
76
+ case 'get_settlement_quote': {
77
+ const quote = await ctx.sly.getSettlementQuote(args as any);
78
+ return {
79
+ content: [
80
+ {
81
+ type: 'text',
82
+ text: JSON.stringify(quote, null, 2),
83
+ },
84
+ ],
85
+ };
86
+ }
87
+
88
+ case 'create_settlement': {
89
+ const settlement = await ctx.sly.createSettlement(args as any);
90
+ return {
91
+ content: [
92
+ {
93
+ type: 'text',
94
+ text: JSON.stringify(settlement, null, 2),
95
+ },
96
+ ],
97
+ };
98
+ }
99
+
100
+ case 'get_settlement_status': {
101
+ const { settlementId } = args as { settlementId: string };
102
+ const settlement = await ctx.sly.getSettlement(settlementId);
103
+ return {
104
+ content: [
105
+ {
106
+ type: 'text',
107
+ text: JSON.stringify(settlement, null, 2),
108
+ },
109
+ ],
110
+ };
111
+ }
112
+
113
+ // ======================================================================
114
+ // UCP Tools
115
+ // ======================================================================
116
+
117
+ case 'ucp_discover': {
118
+ const { merchantUrl } = args as { merchantUrl: string };
119
+ const profile = await ctx.sly.ucp.discover(merchantUrl);
120
+ return {
121
+ content: [
122
+ {
123
+ type: 'text',
124
+ text: JSON.stringify(profile, null, 2),
125
+ },
126
+ ],
127
+ };
128
+ }
129
+
130
+ case 'ucp_create_checkout': {
131
+ const {
132
+ currency, line_items, buyer, shipping_address, payment_config,
133
+ payment_instruments, checkout_type, metadata, agent_id,
134
+ } = args as {
135
+ currency: string;
136
+ line_items: Array<{ id?: string; name: string; quantity: number; unit_price: number; total_price: number; description?: string; image_url?: string; product_url?: string }>;
137
+ buyer?: { email?: string; name?: string; phone?: string };
138
+ shipping_address?: { line1: string; line2?: string; city: string; state?: string; postal_code: string; country: string };
139
+ payment_config?: { handlers?: string[]; default_handler?: string; capture_method?: string };
140
+ payment_instruments?: Array<{ id: string; handler: string; type: string; last4?: string; brand?: string }>;
141
+ checkout_type?: 'physical' | 'digital' | 'service';
142
+ metadata?: Record<string, any>;
143
+ agent_id?: string;
144
+ };
145
+ const body: Record<string, any> = { currency, line_items, buyer, shipping_address, payment_config, metadata };
146
+ if (payment_instruments) body.payment_instruments = payment_instruments;
147
+ if (checkout_type) body.checkout_type = checkout_type;
148
+ if (agent_id) body.agent_id = agent_id;
149
+ const result = await ctx.sly.request('/v1/ucp/checkouts', {
150
+ method: 'POST',
151
+ body: JSON.stringify(body),
152
+ });
153
+ return {
154
+ content: [
155
+ {
156
+ type: 'text',
157
+ text: JSON.stringify(result, null, 2),
158
+ },
159
+ ],
160
+ };
161
+ }
162
+
163
+ case 'ucp_get_checkout': {
164
+ const { checkoutId } = args as { checkoutId: string };
165
+ const result = await ctx.sly.request(`/v1/ucp/checkouts/${checkoutId}`);
166
+ return {
167
+ content: [
168
+ {
169
+ type: 'text',
170
+ text: JSON.stringify(result, null, 2),
171
+ },
172
+ ],
173
+ };
174
+ }
175
+
176
+ case 'ucp_list_checkouts': {
177
+ const params = new URLSearchParams();
178
+ if (args && (args as any).status) params.set('status', (args as any).status);
179
+ if (args && (args as any).agent_id) params.set('agent_id', (args as any).agent_id);
180
+ if (args && (args as any).page) params.set('page', String((args as any).page));
181
+ if (args && (args as any).limit) params.set('limit', String((args as any).limit));
182
+ const query = params.toString();
183
+ const result = await ctx.sly.request(`/v1/ucp/checkouts${query ? `?${query}` : ''}`);
184
+ return {
185
+ content: [
186
+ {
187
+ type: 'text',
188
+ text: JSON.stringify(result, null, 2),
189
+ },
190
+ ],
191
+ };
192
+ }
193
+
194
+ case 'ucp_update_checkout': {
195
+ const { checkoutId, ...updates } = args as {
196
+ checkoutId: string;
197
+ line_items?: any[];
198
+ buyer?: any;
199
+ shipping_address?: any;
200
+ billing_address?: any;
201
+ metadata?: any;
202
+ };
203
+ const result = await ctx.sly.request(`/v1/ucp/checkouts/${checkoutId}`, {
204
+ method: 'PUT',
205
+ body: JSON.stringify(updates),
206
+ });
207
+ return {
208
+ content: [
209
+ {
210
+ type: 'text',
211
+ text: JSON.stringify(result, null, 2),
212
+ },
213
+ ],
214
+ };
215
+ }
216
+
217
+ case 'ucp_complete_checkout': {
218
+ const { checkoutId } = args as { checkoutId: string };
219
+ const result = await ctx.sly.request(`/v1/ucp/checkouts/${checkoutId}/complete`, {
220
+ method: 'POST',
221
+ });
222
+ return {
223
+ content: [
224
+ {
225
+ type: 'text',
226
+ text: JSON.stringify(result, null, 2),
227
+ },
228
+ ],
229
+ };
230
+ }
231
+
232
+ case 'ucp_cancel_checkout': {
233
+ const { checkoutId } = args as { checkoutId: string };
234
+ const result = await ctx.sly.request(`/v1/ucp/checkouts/${checkoutId}/cancel`, {
235
+ method: 'POST',
236
+ });
237
+ return {
238
+ content: [
239
+ {
240
+ type: 'text',
241
+ text: JSON.stringify(result, null, 2),
242
+ },
243
+ ],
244
+ };
245
+ }
246
+
247
+ case 'ucp_add_payment_instrument': {
248
+ const { checkoutId, id: instrumentId, handler, type: instrumentType, last4, brand, metadata } = args as {
249
+ checkoutId: string;
250
+ id: string;
251
+ handler: string;
252
+ type: string;
253
+ last4?: string;
254
+ brand?: string;
255
+ metadata?: Record<string, any>;
256
+ };
257
+ const result = await ctx.sly.request(`/v1/ucp/checkouts/${checkoutId}/instruments`, {
258
+ method: 'POST',
259
+ body: JSON.stringify({ id: instrumentId, handler, type: instrumentType, last4, brand, metadata }),
260
+ });
261
+ return {
262
+ content: [
263
+ {
264
+ type: 'text',
265
+ text: JSON.stringify(result, null, 2),
266
+ },
267
+ ],
268
+ };
269
+ }
270
+
271
+ case 'ucp_batch_checkout': {
272
+ const { checkouts } = args as {
273
+ checkouts: Array<{
274
+ currency: string;
275
+ line_items: Array<{ id?: string; name: string; quantity: number; unit_price: number; total_price: number; description?: string }>;
276
+ buyer?: { email?: string; name?: string };
277
+ shipping_address?: { line1: string; city: string; postal_code: string; country: string; state?: string };
278
+ payment_instruments?: Array<{ id: string; handler: string; type: string }>;
279
+ checkout_type?: 'physical' | 'digital' | 'service';
280
+ metadata?: Record<string, any>;
281
+ agent_id?: string;
282
+ }>;
283
+ };
284
+
285
+ // Single API call to batch create + auto-complete
286
+ const batchRes = await fetch(`${ctx.apiUrl}/v1/ucp/checkouts/batch`, {
287
+ method: 'POST',
288
+ headers: {
289
+ 'Authorization': `Bearer ${ctx.apiKey}`,
290
+ 'Content-Type': 'application/json',
291
+ 'X-Environment': ctx.apiKey.startsWith('pk_live_') ? 'live' : 'test',
292
+ },
293
+ body: JSON.stringify({
294
+ checkouts,
295
+ auto_complete: true,
296
+ }),
297
+ });
298
+ const batchJson = await batchRes.json() as any;
299
+ const batchData = batchJson?.data || batchJson;
300
+
301
+ return {
302
+ content: [
303
+ {
304
+ type: 'text',
305
+ text: JSON.stringify(batchData, null, 2),
306
+ },
307
+ ],
308
+ };
309
+ }
310
+
311
+ case 'ucp_batch_complete': {
312
+ const { checkout_ids, default_payment_instrument } = args as {
313
+ checkout_ids: string[];
314
+ default_payment_instrument: { id: string; handler: string; type: string };
315
+ };
316
+
317
+ const batchRes = await fetch(`${ctx.apiUrl}/v1/ucp/checkouts/batch-complete`, {
318
+ method: 'POST',
319
+ headers: {
320
+ 'Authorization': `Bearer ${ctx.apiKey}`,
321
+ 'Content-Type': 'application/json',
322
+ 'X-Environment': ctx.apiKey.startsWith('pk_live_') ? 'live' : 'test',
323
+ },
324
+ body: JSON.stringify({ checkout_ids, default_payment_instrument }),
325
+ });
326
+ const batchJson = await batchRes.json() as any;
327
+ const batchData = batchJson?.data || batchJson;
328
+
329
+ return {
330
+ content: [
331
+ {
332
+ type: 'text',
333
+ text: JSON.stringify(batchData, null, 2),
334
+ },
335
+ ],
336
+ };
337
+ }
338
+
339
+ case 'ucp_list_orders': {
340
+ const params = new URLSearchParams();
341
+ if (args && (args as any).status) params.set('status', (args as any).status);
342
+ if (args && (args as any).agent_id) params.set('agent_id', (args as any).agent_id);
343
+ if (args && (args as any).page) params.set('page', String((args as any).page));
344
+ if (args && (args as any).limit) params.set('limit', String((args as any).limit));
345
+ const query = params.toString();
346
+ const result = await ctx.sly.request(`/v1/ucp/orders${query ? `?${query}` : ''}`);
347
+ return {
348
+ content: [
349
+ {
350
+ type: 'text',
351
+ text: JSON.stringify(result, null, 2),
352
+ },
353
+ ],
354
+ };
355
+ }
356
+
357
+ case 'ucp_get_order': {
358
+ const { orderId } = args as { orderId: string };
359
+ const result = await ctx.sly.request(`/v1/ucp/orders/${orderId}`);
360
+ return {
361
+ content: [
362
+ {
363
+ type: 'text',
364
+ text: JSON.stringify(result, null, 2),
365
+ },
366
+ ],
367
+ };
368
+ }
369
+
370
+ case 'ucp_update_order_status': {
371
+ const { orderId, status } = args as { orderId: string; status: string };
372
+ const result = await ctx.sly.request(`/v1/ucp/orders/${orderId}/status`, {
373
+ method: 'PUT',
374
+ body: JSON.stringify({ status }),
375
+ });
376
+ return {
377
+ content: [
378
+ {
379
+ type: 'text',
380
+ text: JSON.stringify(result, null, 2),
381
+ },
382
+ ],
383
+ };
384
+ }
385
+
386
+ case 'ucp_cancel_order': {
387
+ const { orderId, reason } = args as { orderId: string; reason?: string };
388
+ const result = await ctx.sly.request(`/v1/ucp/orders/${orderId}/cancel`, {
389
+ method: 'POST',
390
+ body: JSON.stringify({ reason }),
391
+ });
392
+ return {
393
+ content: [
394
+ {
395
+ type: 'text',
396
+ text: JSON.stringify(result, null, 2),
397
+ },
398
+ ],
399
+ };
400
+ }
401
+
402
+ case 'ucp_add_fulfillment_event': {
403
+ const { orderId, type: eventType, description, tracking_number, carrier } = args as {
404
+ orderId: string;
405
+ type: string;
406
+ description: string;
407
+ tracking_number?: string;
408
+ carrier?: string;
409
+ };
410
+ const result = await ctx.sly.request(`/v1/ucp/orders/${orderId}/events`, {
411
+ method: 'POST',
412
+ body: JSON.stringify({ type: eventType, description, tracking_number, carrier }),
413
+ });
414
+ return {
415
+ content: [
416
+ {
417
+ type: 'text',
418
+ text: JSON.stringify(result, null, 2),
419
+ },
420
+ ],
421
+ };
422
+ }
423
+
424
+ // ======================================================================
425
+ // Merchant Catalog Tools
426
+ // ======================================================================
427
+
428
+ case 'list_merchants': {
429
+ const params = new URLSearchParams();
430
+ if (args && (args as any).type) params.set('type', (args as any).type);
431
+ if (args && (args as any).country) params.set('country', (args as any).country);
432
+ if (args && (args as any).search) params.set('search', (args as any).search);
433
+ if (args && (args as any).limit) params.set('limit', String((args as any).limit));
434
+ const query = params.toString();
435
+ const result = await ctx.sly.request(`/v1/ucp/merchants${query ? `?${query}` : ''}`);
436
+ return {
437
+ content: [
438
+ {
439
+ type: 'text',
440
+ text: JSON.stringify(result, null, 2),
441
+ },
442
+ ],
443
+ };
444
+ }
445
+
446
+ case 'get_merchant': {
447
+ const { merchantId } = args as { merchantId: string };
448
+ const result = await ctx.sly.request(`/v1/ucp/merchants/${merchantId}`);
449
+ return {
450
+ content: [
451
+ {
452
+ type: 'text',
453
+ text: JSON.stringify(result, null, 2),
454
+ },
455
+ ],
456
+ };
457
+ }
458
+
459
+ // ======================================================================
460
+ // Agent Management Tools
461
+ // ======================================================================
462
+
463
+ case 'list_accounts': {
464
+ const params = new URLSearchParams();
465
+ if (args && (args as any).type) params.set('type', (args as any).type);
466
+ if (args && (args as any).status) params.set('status', (args as any).status);
467
+ const query = params.toString();
468
+ const result = await ctx.sly.request(`/v1/accounts${query ? `?${query}` : ''}`);
469
+ return {
470
+ content: [
471
+ {
472
+ type: 'text',
473
+ text: JSON.stringify(result, null, 2),
474
+ },
475
+ ],
476
+ };
477
+ }
478
+
479
+ case 'create_account': {
480
+ const { type, name: accountName, email, metadata } = args as {
481
+ type: string;
482
+ name: string;
483
+ email?: string;
484
+ metadata?: Record<string, any>;
485
+ };
486
+ const body: any = { type, name: accountName };
487
+ if (email) body.email = email;
488
+ if (metadata) body.metadata = metadata;
489
+ const result = await ctx.sly.request('/v1/accounts', {
490
+ method: 'POST',
491
+ body: JSON.stringify(body),
492
+ });
493
+ return {
494
+ content: [
495
+ {
496
+ type: 'text',
497
+ text: JSON.stringify(result, null, 2),
498
+ },
499
+ ],
500
+ };
501
+ }
502
+
503
+ case 'update_account': {
504
+ const {
505
+ accountId,
506
+ name: accountName,
507
+ email,
508
+ metadata,
509
+ } = args as {
510
+ accountId: string;
511
+ name?: string;
512
+ email?: string;
513
+ metadata?: Record<string, any>;
514
+ };
515
+ const body: any = {};
516
+ if (accountName !== undefined) body.name = accountName;
517
+ if (email !== undefined) body.email = email;
518
+ if (metadata !== undefined) body.metadata = metadata;
519
+ const result = await ctx.sly.request(`/v1/accounts/${accountId}`, {
520
+ method: 'PATCH',
521
+ body: JSON.stringify(body),
522
+ });
523
+ return {
524
+ content: [
525
+ {
526
+ type: 'text',
527
+ text: JSON.stringify(result, null, 2),
528
+ },
529
+ ],
530
+ };
531
+ }
532
+
533
+ case 'get_tenant_info': {
534
+ const result = await ctx.sly.request('/v1/context/whoami');
535
+ return {
536
+ content: [
537
+ {
538
+ type: 'text',
539
+ text: JSON.stringify(result, null, 2),
540
+ },
541
+ ],
542
+ };
543
+ }
544
+
545
+ case 'create_agent': {
546
+ const { accountId, name: agentName, description } = args as {
547
+ accountId: string;
548
+ name: string;
549
+ description?: string;
550
+ };
551
+ const result = await ctx.sly.request('/v1/agents', {
552
+ method: 'POST',
553
+ body: JSON.stringify({
554
+ accountId,
555
+ name: agentName,
556
+ description,
557
+ }),
558
+ });
559
+ return {
560
+ content: [
561
+ {
562
+ type: 'text',
563
+ text: JSON.stringify(result, null, 2),
564
+ },
565
+ ],
566
+ };
567
+ }
568
+
569
+ case 'verify_agent': {
570
+ const { agentId, tier } = args as { agentId: string; tier: number };
571
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/verify`, {
572
+ method: 'POST',
573
+ body: JSON.stringify({ tier }),
574
+ });
575
+ return {
576
+ content: [
577
+ {
578
+ type: 'text',
579
+ text: JSON.stringify(result, null, 2),
580
+ },
581
+ ],
582
+ };
583
+ }
584
+
585
+ case 'get_agent': {
586
+ const { agentId } = args as { agentId: string };
587
+ const result = await ctx.sly.request(`/v1/agents/${agentId}`);
588
+ return {
589
+ content: [
590
+ {
591
+ type: 'text',
592
+ text: JSON.stringify(result, null, 2),
593
+ },
594
+ ],
595
+ };
596
+ }
597
+
598
+ case 'get_agent_limits': {
599
+ const { agentId } = args as { agentId: string };
600
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/limits`);
601
+ return {
602
+ content: [
603
+ {
604
+ type: 'text',
605
+ text: JSON.stringify(result, null, 2),
606
+ },
607
+ ],
608
+ };
609
+ }
610
+
611
+ case 'get_agent_transactions': {
612
+ const { agentId, limit: txLimit, offset: txOffset, from, to } = args as {
613
+ agentId: string;
614
+ limit?: number;
615
+ offset?: number;
616
+ from?: string;
617
+ to?: string;
618
+ };
619
+ const params = new URLSearchParams();
620
+ if (txLimit) params.set('limit', String(txLimit));
621
+ if (txOffset) params.set('offset', String(txOffset));
622
+ if (from) params.set('from', from);
623
+ if (to) params.set('to', to);
624
+ const query = params.toString();
625
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/transactions${query ? `?${query}` : ''}`);
626
+ return {
627
+ content: [
628
+ {
629
+ type: 'text',
630
+ text: JSON.stringify(result, null, 2),
631
+ },
632
+ ],
633
+ };
634
+ }
635
+
636
+ case 'delete_agent': {
637
+ const { agentId } = args as { agentId: string };
638
+ const result = await ctx.sly.request(`/v1/agents/${agentId}`, {
639
+ method: 'DELETE',
640
+ });
641
+ return {
642
+ content: [
643
+ {
644
+ type: 'text',
645
+ text: JSON.stringify(result, null, 2),
646
+ },
647
+ ],
648
+ };
649
+ }
650
+
651
+ // ======================================================================
652
+ // AP2 Mandate Tools
653
+ // ======================================================================
654
+
655
+ case 'ap2_cancel_mandate': {
656
+ const { mandateId } = args as { mandateId: string };
657
+ const result = await ctx.sly.request(`/v1/ap2/mandates/${mandateId}/cancel`, {
658
+ method: 'PATCH',
659
+ body: JSON.stringify({}),
660
+ });
661
+ return {
662
+ content: [
663
+ {
664
+ type: 'text',
665
+ text: JSON.stringify(result, null, 2),
666
+ },
667
+ ],
668
+ };
669
+ }
670
+
671
+ case 'ap2_create_mandate': {
672
+ const {
673
+ mandate_id,
674
+ agent_id,
675
+ account_id,
676
+ authorized_amount,
677
+ currency,
678
+ mandate_type,
679
+ description,
680
+ expires_at,
681
+ metadata,
682
+ mandate_data,
683
+ } = args as {
684
+ mandate_id: string;
685
+ agent_id: string;
686
+ account_id: string;
687
+ authorized_amount: number;
688
+ currency?: string;
689
+ mandate_type?: string;
690
+ description?: string;
691
+ expires_at?: string;
692
+ metadata?: object;
693
+ mandate_data?: object;
694
+ };
695
+ const result = await ctx.sly.ap2.createMandate({
696
+ mandate_id,
697
+ agent_id,
698
+ account_id,
699
+ authorized_amount,
700
+ currency,
701
+ mandate_type: (mandate_type as 'intent' | 'cart' | 'payment') || 'payment',
702
+ mandate_data: mandate_data || (description ? { description } : undefined),
703
+ metadata,
704
+ expires_at,
705
+ });
706
+ return {
707
+ content: [
708
+ {
709
+ type: 'text',
710
+ text: JSON.stringify(result, null, 2),
711
+ },
712
+ ],
713
+ };
714
+ }
715
+
716
+ case 'ap2_get_mandate': {
717
+ const { mandateId } = args as { mandateId: string };
718
+ const result = await ctx.sly.ap2.getMandate(mandateId);
719
+ return {
720
+ content: [
721
+ {
722
+ type: 'text',
723
+ text: JSON.stringify(result, null, 2),
724
+ },
725
+ ],
726
+ };
727
+ }
728
+
729
+ case 'ap2_execute_mandate': {
730
+ const { mandateId, amount, currency, description, order_ids } = args as {
731
+ mandateId: string;
732
+ amount: number;
733
+ currency?: string;
734
+ description?: string;
735
+ order_ids?: string[];
736
+ };
737
+ const result = await ctx.sly.ap2.executeMandate(mandateId, {
738
+ amount,
739
+ currency,
740
+ description,
741
+ order_ids,
742
+ });
743
+ return {
744
+ content: [
745
+ {
746
+ type: 'text',
747
+ text: JSON.stringify(result, null, 2),
748
+ },
749
+ ],
750
+ };
751
+ }
752
+
753
+ case 'ap2_list_mandates': {
754
+ const { status, agent_id, account_id, limit } = (args || {}) as {
755
+ status?: 'active' | 'completed' | 'cancelled' | 'expired';
756
+ agent_id?: string;
757
+ account_id?: string;
758
+ limit?: number;
759
+ };
760
+ const result = await ctx.sly.ap2.listMandates({
761
+ status,
762
+ agent_id,
763
+ account_id,
764
+ limit,
765
+ });
766
+ return {
767
+ content: [
768
+ {
769
+ type: 'text',
770
+ text: JSON.stringify(result, null, 2),
771
+ },
772
+ ],
773
+ };
774
+ }
775
+
776
+ case 'ap2_update_mandate': {
777
+ const { mandateId, ...updateFields } = args as {
778
+ mandateId: string;
779
+ authorized_amount?: number;
780
+ status?: string;
781
+ expires_at?: string;
782
+ metadata?: object;
783
+ mandate_data?: object;
784
+ description?: string;
785
+ };
786
+ const result = await ctx.sly.request(`/v1/ap2/mandates/${mandateId}`, {
787
+ method: 'PATCH',
788
+ body: JSON.stringify(updateFields),
789
+ });
790
+ return {
791
+ content: [
792
+ {
793
+ type: 'text',
794
+ text: JSON.stringify(result, null, 2),
795
+ },
796
+ ],
797
+ };
798
+ }
799
+
800
+ // ======================================================================
801
+ // ACP Checkout Tools
802
+ // ======================================================================
803
+
804
+ case 'acp_create_checkout': {
805
+ const {
806
+ checkout_id,
807
+ agent_id,
808
+ account_id,
809
+ merchant_id,
810
+ items,
811
+ tax_amount,
812
+ shipping_amount,
813
+ payment_method,
814
+ checkout_data,
815
+ } = args as {
816
+ checkout_id: string;
817
+ agent_id: string;
818
+ account_id?: string;
819
+ merchant_id: string;
820
+ items: Array<{
821
+ name: string;
822
+ description?: string;
823
+ quantity: number;
824
+ unit_price: number;
825
+ total_price: number;
826
+ }>;
827
+ tax_amount?: number;
828
+ shipping_amount?: number;
829
+ payment_method?: string;
830
+ checkout_data?: Record<string, any>;
831
+ };
832
+ const result = await ctx.sly.acp.createCheckout({
833
+ checkout_id,
834
+ agent_id,
835
+ account_id: account_id || '',
836
+ merchant_id,
837
+ items,
838
+ tax_amount,
839
+ shipping_amount,
840
+ payment_method,
841
+ checkout_data,
842
+ });
843
+ return {
844
+ content: [
845
+ {
846
+ type: 'text',
847
+ text: JSON.stringify(result, null, 2),
848
+ },
849
+ ],
850
+ };
851
+ }
852
+
853
+ case 'acp_get_checkout': {
854
+ const { checkoutId } = args as { checkoutId: string };
855
+ const result = await ctx.sly.acp.getCheckout(checkoutId);
856
+ return {
857
+ content: [
858
+ {
859
+ type: 'text',
860
+ text: JSON.stringify(result, null, 2),
861
+ },
862
+ ],
863
+ };
864
+ }
865
+
866
+ case 'acp_complete_checkout': {
867
+ const { checkoutId, shared_payment_token, payment_method } = args as {
868
+ checkoutId: string;
869
+ shared_payment_token?: string;
870
+ payment_method?: string;
871
+ };
872
+ const token = shared_payment_token || `spt_test_${Date.now()}`;
873
+ const result = await ctx.sly.acp.completeCheckout(checkoutId, {
874
+ shared_payment_token: token,
875
+ payment_method,
876
+ });
877
+ return {
878
+ content: [
879
+ {
880
+ type: 'text',
881
+ text: JSON.stringify(result, null, 2),
882
+ },
883
+ ],
884
+ };
885
+ }
886
+
887
+ case 'acp_list_checkouts': {
888
+ const { status, agent_id, merchant_id, limit } = (args || {}) as {
889
+ status?: 'pending' | 'completed' | 'cancelled' | 'expired';
890
+ agent_id?: string;
891
+ merchant_id?: string;
892
+ limit?: number;
893
+ };
894
+ const result = await ctx.sly.acp.listCheckouts({
895
+ status: status as any,
896
+ agent_id,
897
+ merchant_id,
898
+ limit,
899
+ });
900
+ return {
901
+ content: [
902
+ {
903
+ type: 'text',
904
+ text: JSON.stringify(result, null, 2),
905
+ },
906
+ ],
907
+ };
908
+ }
909
+
910
+ case 'acp_batch_checkout': {
911
+ const { checkouts } = args as {
912
+ checkouts: Array<{
913
+ checkout_id: string;
914
+ agent_id: string;
915
+ account_id: string;
916
+ merchant_id: string;
917
+ merchant_name?: string;
918
+ items: Array<{ name: string; description?: string; quantity: number; unit_price: number; total_price: number }>;
919
+ tax_amount?: number;
920
+ shipping_amount?: number;
921
+ currency?: string;
922
+ metadata?: Record<string, any>;
923
+ }>;
924
+ };
925
+
926
+ const batchRes = await fetch(`${ctx.apiUrl}/v1/acp/checkouts/batch`, {
927
+ method: 'POST',
928
+ headers: {
929
+ 'Authorization': `Bearer ${ctx.apiKey}`,
930
+ 'Content-Type': 'application/json',
931
+ 'X-Environment': ctx.apiKey.startsWith('pk_live_') ? 'live' : 'test',
932
+ },
933
+ body: JSON.stringify({ checkouts }),
934
+ });
935
+ const batchJson = await batchRes.json() as any;
936
+ const batchData = batchJson?.data || batchJson;
937
+
938
+ return {
939
+ content: [
940
+ {
941
+ type: 'text',
942
+ text: JSON.stringify(batchData, null, 2),
943
+ },
944
+ ],
945
+ };
946
+ }
947
+
948
+ // ======================================================================
949
+ // Wallet Management Tools
950
+ // ======================================================================
951
+
952
+ case 'list_wallets': {
953
+ const params = new URLSearchParams();
954
+ if (args && (args as any).owner_account_id) params.set('owner_account_id', (args as any).owner_account_id);
955
+ if (args && (args as any).managed_by_agent_id) params.set('managed_by_agent_id', (args as any).managed_by_agent_id);
956
+ if (args && (args as any).status) params.set('status', (args as any).status);
957
+ if (args && (args as any).page) params.set('page', String((args as any).page));
958
+ if (args && (args as any).limit) params.set('limit', String((args as any).limit));
959
+ const query = params.toString();
960
+ const result = await ctx.sly.request(`/v1/wallets${query ? `?${query}` : ''}`);
961
+ return {
962
+ content: [
963
+ {
964
+ type: 'text',
965
+ text: JSON.stringify(result, null, 2),
966
+ },
967
+ ],
968
+ };
969
+ }
970
+
971
+ case 'create_wallet': {
972
+ const {
973
+ accountId,
974
+ name: walletName,
975
+ currency,
976
+ walletType,
977
+ blockchain,
978
+ initialBalance,
979
+ managedByAgentId,
980
+ purpose,
981
+ } = args as {
982
+ accountId: string;
983
+ name?: string;
984
+ currency?: string;
985
+ walletType?: string;
986
+ blockchain?: string;
987
+ initialBalance?: number;
988
+ managedByAgentId?: string;
989
+ purpose?: string;
990
+ };
991
+ const result = await ctx.sly.request('/v1/wallets', {
992
+ method: 'POST',
993
+ body: JSON.stringify({
994
+ accountId,
995
+ name: walletName,
996
+ currency,
997
+ walletType,
998
+ blockchain,
999
+ initialBalance,
1000
+ managedByAgentId,
1001
+ purpose,
1002
+ }),
1003
+ });
1004
+ return {
1005
+ content: [
1006
+ {
1007
+ type: 'text',
1008
+ text: JSON.stringify(result, null, 2),
1009
+ },
1010
+ ],
1011
+ };
1012
+ }
1013
+
1014
+ case 'get_wallet': {
1015
+ const { walletId } = args as { walletId: string };
1016
+ const result = await ctx.sly.request(`/v1/wallets/${walletId}`);
1017
+ return {
1018
+ content: [
1019
+ {
1020
+ type: 'text',
1021
+ text: JSON.stringify(result, null, 2),
1022
+ },
1023
+ ],
1024
+ };
1025
+ }
1026
+
1027
+ case 'get_wallet_balance': {
1028
+ const { walletId } = args as { walletId: string };
1029
+ const result = await ctx.sly.request(`/v1/wallets/${walletId}/balance`);
1030
+ return {
1031
+ content: [
1032
+ {
1033
+ type: 'text',
1034
+ text: JSON.stringify(result, null, 2),
1035
+ },
1036
+ ],
1037
+ };
1038
+ }
1039
+
1040
+ case 'wallet_deposit': {
1041
+ const { walletId, amount, fromAccountId, reference } = args as {
1042
+ walletId: string;
1043
+ amount: number;
1044
+ fromAccountId: string;
1045
+ reference?: string;
1046
+ };
1047
+ const result = await ctx.sly.request(`/v1/wallets/${walletId}/deposit`, {
1048
+ method: 'POST',
1049
+ body: JSON.stringify({ amount, fromAccountId, reference }),
1050
+ });
1051
+ return {
1052
+ content: [
1053
+ {
1054
+ type: 'text',
1055
+ text: JSON.stringify(result, null, 2),
1056
+ },
1057
+ ],
1058
+ };
1059
+ }
1060
+
1061
+ case 'wallet_withdraw': {
1062
+ const { walletId, amount, destinationAccountId, reference } = args as {
1063
+ walletId: string;
1064
+ amount: number;
1065
+ destinationAccountId: string;
1066
+ reference?: string;
1067
+ };
1068
+ const result = await ctx.sly.request(`/v1/wallets/${walletId}/withdraw`, {
1069
+ method: 'POST',
1070
+ body: JSON.stringify({ amount, destinationAccountId, reference }),
1071
+ });
1072
+ return {
1073
+ content: [
1074
+ {
1075
+ type: 'text',
1076
+ text: JSON.stringify(result, null, 2),
1077
+ },
1078
+ ],
1079
+ };
1080
+ }
1081
+
1082
+ case 'wallet_test_fund': {
1083
+ const { walletId, amount, currency, reference } = args as {
1084
+ walletId: string;
1085
+ amount: number;
1086
+ currency?: string;
1087
+ reference?: string;
1088
+ };
1089
+ const result = await ctx.sly.request(`/v1/wallets/${walletId}/test-fund`, {
1090
+ method: 'POST',
1091
+ body: JSON.stringify({ amount, currency, reference }),
1092
+ });
1093
+ return {
1094
+ content: [
1095
+ {
1096
+ type: 'text',
1097
+ text: JSON.stringify(result, null, 2),
1098
+ },
1099
+ ],
1100
+ };
1101
+ }
1102
+
1103
+ // ======================================================================
1104
+ // Agent Wallet Policy Tools (Epic 18)
1105
+ // ======================================================================
1106
+
1107
+ case 'agent_wallet_evaluate_policy': {
1108
+ const { agentId, amount, currency, action_type, contract_type, counterparty_agent_id, counterparty_address } =
1109
+ args as {
1110
+ agentId: string;
1111
+ amount: number;
1112
+ currency?: string;
1113
+ action_type?: string;
1114
+ contract_type?: string;
1115
+ counterparty_agent_id?: string;
1116
+ counterparty_address?: string;
1117
+ };
1118
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/wallet/policy/evaluate`, {
1119
+ method: 'POST',
1120
+ body: JSON.stringify({
1121
+ amount,
1122
+ currency: currency || 'USDC',
1123
+ action_type: action_type || 'negotiation_check',
1124
+ contract_type,
1125
+ counterparty_agent_id,
1126
+ counterparty_address,
1127
+ }),
1128
+ });
1129
+ return {
1130
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1131
+ };
1132
+ }
1133
+
1134
+ case 'agent_wallet_get_exposures': {
1135
+ const { agentId } = args as { agentId: string };
1136
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/wallet/exposures`);
1137
+ return {
1138
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1139
+ };
1140
+ }
1141
+
1142
+ case 'agent_wallet_get_evaluations': {
1143
+ const { agentId, page, limit } = args as { agentId: string; page?: number; limit?: number };
1144
+ const params = new URLSearchParams();
1145
+ if (page) params.set('page', String(page));
1146
+ if (limit) params.set('limit', String(limit));
1147
+ const qs = params.toString();
1148
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/wallet/policy/evaluations${qs ? `?${qs}` : ''}`);
1149
+ return {
1150
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1151
+ };
1152
+ }
1153
+
1154
+ case 'agent_wallet_get': {
1155
+ const { agentId } = args as { agentId: string };
1156
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/wallet`);
1157
+ return {
1158
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1159
+ };
1160
+ }
1161
+
1162
+ case 'agent_wallet_freeze': {
1163
+ const { agentId } = args as { agentId: string };
1164
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/wallet/freeze`, {
1165
+ method: 'POST',
1166
+ });
1167
+ return {
1168
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1169
+ };
1170
+ }
1171
+
1172
+ case 'agent_wallet_unfreeze': {
1173
+ const { agentId } = args as { agentId: string };
1174
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/wallet/unfreeze`, {
1175
+ method: 'POST',
1176
+ });
1177
+ return {
1178
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1179
+ };
1180
+ }
1181
+
1182
+ case 'agent_wallet_set_policy': {
1183
+ const { agentId, ...policyFields } = args as {
1184
+ agentId: string;
1185
+ dailySpendLimit?: number;
1186
+ monthlySpendLimit?: number;
1187
+ requiresApprovalAbove?: number;
1188
+ approvedVendors?: string[];
1189
+ contractPolicy?: Record<string, unknown>;
1190
+ };
1191
+ const result = await ctx.sly.request(`/v1/agents/${agentId}/wallet/policy`, {
1192
+ method: 'PUT',
1193
+ body: JSON.stringify(policyFields),
1194
+ });
1195
+ return {
1196
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1197
+ };
1198
+ }
1199
+
1200
+ // ======================================================================
1201
+ // x402 Micropayment Tools
1202
+ // ======================================================================
1203
+
1204
+ case 'x402_create_endpoint': {
1205
+ const {
1206
+ name: endpointName,
1207
+ path,
1208
+ method,
1209
+ description,
1210
+ accountId,
1211
+ basePrice,
1212
+ currency,
1213
+ volumeDiscounts,
1214
+ webhookUrl,
1215
+ } = args as {
1216
+ name: string;
1217
+ path: string;
1218
+ method?: string;
1219
+ description?: string;
1220
+ accountId: string;
1221
+ basePrice: number;
1222
+ currency?: string;
1223
+ volumeDiscounts?: Array<{ minCalls: number; discountPercent: number }>;
1224
+ webhookUrl?: string;
1225
+ };
1226
+ const result = await ctx.sly.request('/v1/x402/endpoints', {
1227
+ method: 'POST',
1228
+ body: JSON.stringify({
1229
+ name: endpointName,
1230
+ path,
1231
+ method,
1232
+ description,
1233
+ accountId,
1234
+ basePrice,
1235
+ currency,
1236
+ volumeDiscounts,
1237
+ webhookUrl,
1238
+ }),
1239
+ });
1240
+ return {
1241
+ content: [
1242
+ {
1243
+ type: 'text',
1244
+ text: JSON.stringify(result, null, 2),
1245
+ },
1246
+ ],
1247
+ };
1248
+ }
1249
+
1250
+ case 'x402_list_endpoints': {
1251
+ const params = new URLSearchParams();
1252
+ if (args && (args as any).status) params.set('status', (args as any).status);
1253
+ if (args && (args as any).account_id) params.set('account_id', (args as any).account_id);
1254
+ if (args && (args as any).page) params.set('page', String((args as any).page));
1255
+ if (args && (args as any).limit) params.set('limit', String((args as any).limit));
1256
+ const query = params.toString();
1257
+ const result = await ctx.sly.request(`/v1/x402/endpoints${query ? `?${query}` : ''}`);
1258
+ return {
1259
+ content: [
1260
+ {
1261
+ type: 'text',
1262
+ text: JSON.stringify(result, null, 2),
1263
+ },
1264
+ ],
1265
+ };
1266
+ }
1267
+
1268
+ case 'x402_get_endpoint': {
1269
+ const { endpointId } = args as { endpointId: string };
1270
+ const result = await ctx.sly.request(`/v1/x402/endpoints/${endpointId}`);
1271
+ return {
1272
+ content: [
1273
+ {
1274
+ type: 'text',
1275
+ text: JSON.stringify(result, null, 2),
1276
+ },
1277
+ ],
1278
+ };
1279
+ }
1280
+
1281
+ case 'x402_pay': {
1282
+ const { endpointId, walletId, amount, currency, method: httpMethod, path: endpointPath } = args as {
1283
+ endpointId: string;
1284
+ walletId: string;
1285
+ amount: number;
1286
+ currency: string;
1287
+ method: string;
1288
+ path: string;
1289
+ };
1290
+ const requestId = crypto.randomUUID();
1291
+ const result = await ctx.sly.request('/v1/x402/pay', {
1292
+ method: 'POST',
1293
+ body: JSON.stringify({
1294
+ endpointId,
1295
+ requestId,
1296
+ amount,
1297
+ currency,
1298
+ walletId,
1299
+ method: httpMethod,
1300
+ path: endpointPath,
1301
+ timestamp: Math.floor(Date.now() / 1000),
1302
+ }),
1303
+ });
1304
+ return {
1305
+ content: [
1306
+ {
1307
+ type: 'text',
1308
+ text: JSON.stringify(result, null, 2),
1309
+ },
1310
+ ],
1311
+ };
1312
+ }
1313
+
1314
+ case 'x402_verify': {
1315
+ const { jwt, requestId, transferId } = args as {
1316
+ jwt?: string;
1317
+ requestId?: string;
1318
+ transferId?: string;
1319
+ };
1320
+ const result = await ctx.sly.request('/v1/x402/verify', {
1321
+ method: 'POST',
1322
+ body: JSON.stringify({ jwt, requestId, transferId }),
1323
+ });
1324
+ return {
1325
+ content: [
1326
+ {
1327
+ type: 'text',
1328
+ text: JSON.stringify(result, null, 2),
1329
+ },
1330
+ ],
1331
+ };
1332
+ }
1333
+
1334
+ // ====================================================================
1335
+ // A2A Tools
1336
+ // ====================================================================
1337
+ case 'a2a_discover_agent': {
1338
+ const { url } = args as { url: string };
1339
+ const result = await ctx.sly.request('/v1/a2a/discover', {
1340
+ method: 'POST',
1341
+ body: JSON.stringify({ url }),
1342
+ });
1343
+ return {
1344
+ content: [
1345
+ {
1346
+ type: 'text',
1347
+ text: JSON.stringify(result, null, 2),
1348
+ },
1349
+ ],
1350
+ };
1351
+ }
1352
+
1353
+ case 'a2a_send_task': {
1354
+ const { agent_id, remote_url, message, context_id } = args as {
1355
+ agent_id?: string;
1356
+ remote_url?: string;
1357
+ message: string;
1358
+ context_id?: string;
1359
+ };
1360
+ const result = await ctx.sly.request('/v1/a2a/tasks', {
1361
+ method: 'POST',
1362
+ body: JSON.stringify({
1363
+ agent_id,
1364
+ remote_url,
1365
+ message: {
1366
+ parts: [{ text: message }],
1367
+ },
1368
+ context_id,
1369
+ }),
1370
+ });
1371
+ return {
1372
+ content: [
1373
+ {
1374
+ type: 'text',
1375
+ text: JSON.stringify(result, null, 2),
1376
+ },
1377
+ ],
1378
+ };
1379
+ }
1380
+
1381
+ case 'a2a_get_task': {
1382
+ const { task_id } = args as { task_id: string };
1383
+ const result = await ctx.sly.request(`/v1/a2a/tasks/${task_id}`, {
1384
+ method: 'GET',
1385
+ });
1386
+ return {
1387
+ content: [
1388
+ {
1389
+ type: 'text',
1390
+ text: JSON.stringify(result, null, 2),
1391
+ },
1392
+ ],
1393
+ };
1394
+ }
1395
+
1396
+ case 'a2a_list_tasks': {
1397
+ const { agent_id, state, direction, limit, page } = args as {
1398
+ agent_id?: string;
1399
+ state?: string;
1400
+ direction?: string;
1401
+ limit?: number;
1402
+ page?: number;
1403
+ };
1404
+ const params = new URLSearchParams();
1405
+ if (agent_id) params.set('agent_id', agent_id);
1406
+ if (state) params.set('state', state);
1407
+ if (direction) params.set('direction', direction);
1408
+ if (limit) params.set('limit', String(limit));
1409
+ if (page) params.set('page', String(page));
1410
+ const query = params.toString();
1411
+ const result = await ctx.sly.request(`/v1/a2a/tasks${query ? `?${query}` : ''}`, {
1412
+ method: 'GET',
1413
+ });
1414
+ return {
1415
+ content: [
1416
+ {
1417
+ type: 'text',
1418
+ text: JSON.stringify(result, null, 2),
1419
+ },
1420
+ ],
1421
+ };
1422
+ }
1423
+
1424
+ // ======================================================================
1425
+ // MPP Tools
1426
+ // ======================================================================
1427
+
1428
+ case 'mpp_pay': {
1429
+ const { service_url, amount, currency, intent, agent_id, wallet_id } = args as {
1430
+ service_url: string;
1431
+ amount: number;
1432
+ currency?: string;
1433
+ intent?: string;
1434
+ agent_id: string;
1435
+ wallet_id?: string;
1436
+ };
1437
+ const result = await ctx.sly.request('/v1/mpp/pay', {
1438
+ method: 'POST',
1439
+ body: JSON.stringify({ service_url, amount, currency, intent, agent_id, wallet_id }),
1440
+ });
1441
+ return {
1442
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1443
+ };
1444
+ }
1445
+
1446
+ case 'mpp_open_session': {
1447
+ const { service_url, deposit_amount, max_budget, agent_id, wallet_id, currency } = args as {
1448
+ service_url: string;
1449
+ deposit_amount: number;
1450
+ max_budget?: number;
1451
+ agent_id: string;
1452
+ wallet_id: string;
1453
+ currency?: string;
1454
+ };
1455
+ const result = await ctx.sly.request('/v1/mpp/sessions', {
1456
+ method: 'POST',
1457
+ body: JSON.stringify({ service_url, deposit_amount, max_budget, agent_id, wallet_id, currency }),
1458
+ });
1459
+ return {
1460
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1461
+ };
1462
+ }
1463
+
1464
+ case 'mpp_get_session': {
1465
+ const { session_id } = args as { session_id: string };
1466
+ const result = await ctx.sly.request(`/v1/mpp/sessions/${session_id}`);
1467
+ return {
1468
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1469
+ };
1470
+ }
1471
+
1472
+ case 'mpp_list_sessions': {
1473
+ const { agent_id, status, limit, offset } = args as {
1474
+ agent_id?: string;
1475
+ status?: string;
1476
+ limit?: number;
1477
+ offset?: number;
1478
+ };
1479
+ const params = new URLSearchParams();
1480
+ if (agent_id) params.set('agent_id', agent_id);
1481
+ if (status) params.set('status', status);
1482
+ if (limit) params.set('limit', String(limit));
1483
+ if (offset) params.set('offset', String(offset));
1484
+ const query = params.toString();
1485
+ const result = await ctx.sly.request(`/v1/mpp/sessions${query ? `?${query}` : ''}`);
1486
+ return {
1487
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1488
+ };
1489
+ }
1490
+
1491
+ case 'mpp_close_session': {
1492
+ const { session_id } = args as { session_id: string };
1493
+ const result = await ctx.sly.request(`/v1/mpp/sessions/${session_id}/close`, {
1494
+ method: 'POST',
1495
+ });
1496
+ return {
1497
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1498
+ };
1499
+ }
1500
+
1501
+ case 'mpp_list_transfers': {
1502
+ const { service_url, session_id, limit, offset } = args as {
1503
+ service_url?: string;
1504
+ session_id?: string;
1505
+ limit?: number;
1506
+ offset?: number;
1507
+ };
1508
+ const params = new URLSearchParams();
1509
+ if (service_url) params.set('service_url', service_url);
1510
+ if (session_id) params.set('session_id', session_id);
1511
+ if (limit) params.set('limit', String(limit));
1512
+ if (offset) params.set('offset', String(offset));
1513
+ const query = params.toString();
1514
+ const result = await ctx.sly.request(`/v1/mpp/transfers${query ? `?${query}` : ''}`);
1515
+ return {
1516
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1517
+ };
1518
+ }
1519
+
1520
+ case 'mpp_verify_receipt': {
1521
+ const { receipt_id } = args as { receipt_id: string };
1522
+ const result = await ctx.sly.request('/v1/mpp/receipts/verify', {
1523
+ method: 'POST',
1524
+ body: JSON.stringify({ receipt_id }),
1525
+ });
1526
+ return {
1527
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1528
+ };
1529
+ }
1530
+
1531
+ // ====================================================================
1532
+ // Support Tools (Intercom Fin)
1533
+ // ====================================================================
1534
+ case 'explain_rejection': {
1535
+ const { error_code, transaction_id, agent_id } = args as {
1536
+ error_code?: string;
1537
+ transaction_id?: string;
1538
+ agent_id?: string;
1539
+ };
1540
+ const params = new URLSearchParams();
1541
+ if (error_code) params.set('error_code', error_code);
1542
+ if (transaction_id) params.set('transaction_id', transaction_id);
1543
+ if (agent_id) params.set('agent_id', agent_id);
1544
+ const query = params.toString();
1545
+ const result = await ctx.sly.request(`/v1/support/explain-rejection${query ? `?${query}` : ''}`, {
1546
+ method: 'GET',
1547
+ });
1548
+ return {
1549
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1550
+ };
1551
+ }
1552
+
1553
+ case 'request_limit_increase': {
1554
+ const { agent_id, limit_type, requested_amount, reason, duration } = args as {
1555
+ agent_id: string;
1556
+ limit_type: string;
1557
+ requested_amount: number;
1558
+ reason: string;
1559
+ duration?: string;
1560
+ };
1561
+ const body: Record<string, any> = { agent_id, limit_type, requested_amount, reason };
1562
+ if (duration) body.duration = duration;
1563
+ const result = await ctx.sly.request('/v1/support/limit-requests', {
1564
+ method: 'POST',
1565
+ body: JSON.stringify(body),
1566
+ });
1567
+ return {
1568
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1569
+ };
1570
+ }
1571
+
1572
+ case 'open_dispute': {
1573
+ const { transaction_id, reason, description, requested_resolution } = args as {
1574
+ transaction_id: string;
1575
+ reason: string;
1576
+ description: string;
1577
+ requested_resolution?: string;
1578
+ };
1579
+ const body: Record<string, any> = {
1580
+ transferId: transaction_id,
1581
+ reason,
1582
+ description,
1583
+ };
1584
+ if (requested_resolution) body.requestedResolution = requested_resolution;
1585
+ const result = await ctx.sly.request('/v1/disputes', {
1586
+ method: 'POST',
1587
+ body: JSON.stringify(body),
1588
+ });
1589
+ return {
1590
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1591
+ };
1592
+ }
1593
+
1594
+ case 'escalate_to_human': {
1595
+ const { agent_id, reason, summary, priority } = args as {
1596
+ agent_id?: string;
1597
+ reason: string;
1598
+ summary: string;
1599
+ priority?: string;
1600
+ };
1601
+ const body: Record<string, any> = { reason, summary };
1602
+ if (agent_id) body.agent_id = agent_id;
1603
+ if (priority) body.priority = priority;
1604
+ const result = await ctx.sly.request('/v1/support/escalations', {
1605
+ method: 'POST',
1606
+ body: JSON.stringify(body),
1607
+ });
1608
+ return {
1609
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1610
+ };
1611
+ }
1612
+
1613
+ // ======================================================================
1614
+ // Environment Management Tools
1615
+ // ======================================================================
1616
+
1617
+ case 'get_environment': {
1618
+ const masked = ctx.apiKey.slice(0, 12) + '***';
1619
+ return {
1620
+ content: [{
1621
+ type: 'text',
1622
+ text: JSON.stringify({ environment: ctx.environment, apiKeyPrefix: masked, availableEnvironments: Object.keys(ctx.keys) }, null, 2),
1623
+ }],
1624
+ };
1625
+ }
1626
+
1627
+ case 'switch_environment': {
1628
+ const { environment: targetEnv } = args as { environment: 'sandbox' | 'production' };
1629
+ if (targetEnv === ctx.environment) {
1630
+ return { content: [{ type: 'text', text: JSON.stringify({ message: `Already in ${targetEnv} environment`, environment: ctx.environment, apiKeyPrefix: ctx.apiKey.slice(0, 12) + '***' }, null, 2) }] };
1631
+ }
1632
+ const targetKey = ctx.keys[targetEnv];
1633
+ if (!targetKey) {
1634
+ const hint = targetEnv === 'production' ? 'Set SLY_API_KEY_LIVE in your MCP server config (.mcp.json)' : 'Set SLY_API_KEY in your MCP server config (.mcp.json)';
1635
+ return { content: [{ type: 'text', text: `Error: No API key configured for "${targetEnv}" environment. ${hint}` }], isError: true };
1636
+ }
1637
+ const targetUrl = ctx.urls[targetEnv] || ctx.apiUrl;
1638
+ ctx.sly = new Sly({ apiKey: targetKey, apiUrl: targetUrl });
1639
+ ctx.apiKey = targetKey;
1640
+ ctx.apiUrl = targetUrl;
1641
+ ctx.environment = targetEnv;
1642
+ return { content: [{ type: 'text', text: JSON.stringify({ message: `Switched to ${targetEnv} environment`, environment: ctx.environment, apiKeyPrefix: ctx.apiKey.slice(0, 12) + '***' }, null, 2) }] };
1643
+ }
1644
+
1645
+ default:
1646
+ throw new Error(`Unknown tool: ${name}`);
1647
+ }
1648
+ } catch (error: any) {
1649
+ const details = error.details || error.errors || '';
1650
+ const detailsStr = details ? `\nDetails: ${JSON.stringify(details, null, 2)}` : '';
1651
+ return {
1652
+ content: [
1653
+ {
1654
+ type: 'text',
1655
+ text: `Error: ${error.message}${detailsStr}`,
1656
+ },
1657
+ ],
1658
+ isError: true,
1659
+ };
1660
+ }
1661
+ });
1662
+
1663
+ return server;
1664
+ }