globodai-mcp-payment-manager 1.0.1

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 (88) hide show
  1. package/.env.example +23 -0
  2. package/.github/workflows/ci.yml +26 -0
  3. package/.github/workflows/release.yml +82 -0
  4. package/LICENSE +21 -0
  5. package/README.md +362 -0
  6. package/dist/index.d.ts +31 -0
  7. package/dist/index.js +122 -0
  8. package/dist/lib/blockchain.d.ts +50 -0
  9. package/dist/lib/blockchain.js +287 -0
  10. package/dist/lib/cards.d.ts +83 -0
  11. package/dist/lib/cards.js +276 -0
  12. package/dist/lib/cli-runner.d.ts +31 -0
  13. package/dist/lib/cli-runner.js +77 -0
  14. package/dist/lib/crypto.d.ts +39 -0
  15. package/dist/lib/crypto.js +228 -0
  16. package/dist/lib/cvv-crypto.d.ts +23 -0
  17. package/dist/lib/cvv-crypto.js +67 -0
  18. package/dist/lib/mcp-core.d.ts +46 -0
  19. package/dist/lib/mcp-core.js +86 -0
  20. package/dist/lib/pin-manager.d.ts +69 -0
  21. package/dist/lib/pin-manager.js +199 -0
  22. package/dist/lib/wallets.d.ts +91 -0
  23. package/dist/lib/wallets.js +227 -0
  24. package/dist/tools/add-card.d.ts +65 -0
  25. package/dist/tools/add-card.js +97 -0
  26. package/dist/tools/add-wallet.d.ts +65 -0
  27. package/dist/tools/add-wallet.js +104 -0
  28. package/dist/tools/card-status.d.ts +20 -0
  29. package/dist/tools/card-status.js +26 -0
  30. package/dist/tools/confirm-payment.d.ts +44 -0
  31. package/dist/tools/confirm-payment.js +88 -0
  32. package/dist/tools/get-total-balance.d.ts +41 -0
  33. package/dist/tools/get-total-balance.js +98 -0
  34. package/dist/tools/get-transactions.d.ts +39 -0
  35. package/dist/tools/get-transactions.js +40 -0
  36. package/dist/tools/get-wallet-balance.d.ts +43 -0
  37. package/dist/tools/get-wallet-balance.js +69 -0
  38. package/dist/tools/list-cards.d.ts +36 -0
  39. package/dist/tools/list-cards.js +39 -0
  40. package/dist/tools/list-wallet-transactions.d.ts +63 -0
  41. package/dist/tools/list-wallet-transactions.js +76 -0
  42. package/dist/tools/list-wallets.d.ts +41 -0
  43. package/dist/tools/list-wallets.js +50 -0
  44. package/dist/tools/lock-cards.d.ts +16 -0
  45. package/dist/tools/lock-cards.js +23 -0
  46. package/dist/tools/prepare-crypto-tx.d.ts +69 -0
  47. package/dist/tools/prepare-crypto-tx.js +93 -0
  48. package/dist/tools/prepare-payment.d.ts +57 -0
  49. package/dist/tools/prepare-payment.js +93 -0
  50. package/dist/tools/remove-card.d.ts +25 -0
  51. package/dist/tools/remove-card.js +39 -0
  52. package/dist/tools/remove-wallet.d.ts +27 -0
  53. package/dist/tools/remove-wallet.js +40 -0
  54. package/dist/tools/setup-pin.d.ts +26 -0
  55. package/dist/tools/setup-pin.js +33 -0
  56. package/dist/tools/sign-crypto-tx.d.ts +42 -0
  57. package/dist/tools/sign-crypto-tx.js +75 -0
  58. package/dist/tools/unlock-cards.d.ts +35 -0
  59. package/dist/tools/unlock-cards.js +41 -0
  60. package/package.json +50 -0
  61. package/src/index.ts +139 -0
  62. package/src/lib/blockchain.ts +375 -0
  63. package/src/lib/cards.ts +372 -0
  64. package/src/lib/cli-runner.ts +113 -0
  65. package/src/lib/crypto.ts +284 -0
  66. package/src/lib/cvv-crypto.ts +81 -0
  67. package/src/lib/mcp-core.ts +127 -0
  68. package/src/lib/pin-manager.ts +252 -0
  69. package/src/lib/wallets.ts +331 -0
  70. package/src/tools/add-card.ts +108 -0
  71. package/src/tools/add-wallet.ts +114 -0
  72. package/src/tools/card-status.ts +32 -0
  73. package/src/tools/confirm-payment.ts +103 -0
  74. package/src/tools/get-total-balance.ts +123 -0
  75. package/src/tools/get-transactions.ts +49 -0
  76. package/src/tools/get-wallet-balance.ts +75 -0
  77. package/src/tools/list-cards.ts +52 -0
  78. package/src/tools/list-wallet-transactions.ts +83 -0
  79. package/src/tools/list-wallets.ts +63 -0
  80. package/src/tools/lock-cards.ts +31 -0
  81. package/src/tools/prepare-crypto-tx.ts +108 -0
  82. package/src/tools/prepare-payment.ts +108 -0
  83. package/src/tools/remove-card.ts +46 -0
  84. package/src/tools/remove-wallet.ts +47 -0
  85. package/src/tools/setup-pin.ts +39 -0
  86. package/src/tools/sign-crypto-tx.ts +90 -0
  87. package/src/tools/unlock-cards.ts +48 -0
  88. package/tsconfig.json +19 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Add a new crypto wallet
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { randomUUID } from "crypto";
7
+ import { addWallet } from "../lib/wallets";
8
+ import type { WalletType } from "../lib/wallets";
9
+
10
+ export const name = "add_wallet";
11
+
12
+ export const description = `Add a new crypto wallet. Supports hot wallets (with private key - encrypted), watch-only (address only), and hardware wallets (address + device info).
13
+
14
+ For hot wallets: private key will be encrypted at rest.
15
+ For watch-only: only the address is stored, no spending capability.
16
+ For hardware: address is stored, transactions require hardware signing.`;
17
+
18
+ export const parameters = z.object({
19
+ nickname: z.string().describe("Friendly name for the wallet (e.g., 'ETH principal', 'Trading wallet')"),
20
+ address: z.string().describe("Public wallet address"),
21
+ chain: z.enum(["ethereum", "polygon", "arbitrum", "optimism", "base", "avalanche", "bsc", "bitcoin", "solana", "starknet"])
22
+ .describe("Blockchain network"),
23
+ type: z.enum(["hot", "watch-only", "hardware"]).describe("Wallet type"),
24
+ private_key: z.string().optional().describe("Private key (for hot wallets only - will be encrypted)"),
25
+ mnemonic: z.string().optional().describe("Seed phrase (for hot wallets only - will be encrypted)"),
26
+ hardware_type: z.enum(["ledger", "trezor", "other"]).optional().describe("Hardware wallet type"),
27
+ derivation_path: z.string().optional().describe("Derivation path (e.g., m/44'/60'/0'/0/0)"),
28
+ allowed_operations: z.array(z.enum(["send", "swap", "approve", "sign"])).optional()
29
+ .describe("Allowed operations (default: all for hot, none for watch-only)"),
30
+ per_transaction_limit: z.number().optional().describe("Max amount per transaction"),
31
+ daily_limit: z.number().optional().describe("Max daily spending"),
32
+ limit_token: z.string().optional().describe("Token symbol for limits (e.g., 'ETH', 'USDC')"),
33
+ });
34
+
35
+ export async function execute(args: z.infer<typeof parameters>) {
36
+ try {
37
+ // Validate based on type
38
+ if (args.type === "hot" && !args.private_key && !args.mnemonic) {
39
+ return {
40
+ success: false,
41
+ error: "Hot wallets require either a private_key or mnemonic",
42
+ };
43
+ }
44
+
45
+ if (args.type === "watch-only" && (args.private_key || args.mnemonic)) {
46
+ return {
47
+ success: false,
48
+ error: "Watch-only wallets cannot have private keys",
49
+ };
50
+ }
51
+
52
+ if (args.type === "hardware" && !args.hardware_type) {
53
+ return {
54
+ success: false,
55
+ error: "Hardware wallets must specify hardware_type",
56
+ };
57
+ }
58
+
59
+ const walletId = randomUUID();
60
+
61
+ // Default operations based on type
62
+ let operations = args.allowed_operations;
63
+ if (!operations) {
64
+ if (args.type === "hot") {
65
+ operations = ["send", "swap", "approve", "sign"];
66
+ } else if (args.type === "hardware") {
67
+ operations = ["send", "swap", "approve", "sign"]; // Requires hardware confirmation
68
+ } else {
69
+ operations = []; // Watch-only can't do anything
70
+ }
71
+ }
72
+
73
+ await addWallet({
74
+ id: walletId,
75
+ nickname: args.nickname,
76
+ address: args.address,
77
+ chain: args.chain,
78
+ type: args.type as WalletType,
79
+ privateKey: args.private_key,
80
+ mnemonic: args.mnemonic,
81
+ hardwareType: args.hardware_type,
82
+ derivationPath: args.derivation_path,
83
+ enabled: true,
84
+ allowedOperations: operations,
85
+ limits: args.per_transaction_limit || args.daily_limit ? {
86
+ perTransaction: args.per_transaction_limit,
87
+ daily: args.daily_limit,
88
+ tokenSymbol: args.limit_token ?? "ETH",
89
+ } : undefined,
90
+ addedAt: new Date().toISOString(),
91
+ });
92
+
93
+ const typeInfo = {
94
+ hot: "Private key encrypted and stored securely",
95
+ "watch-only": "Address only - no spending capability",
96
+ hardware: `${args.hardware_type} wallet - requires device for signing`,
97
+ };
98
+
99
+ return {
100
+ success: true,
101
+ message: `Wallet "${args.nickname}" added successfully`,
102
+ wallet_id: walletId,
103
+ type: args.type,
104
+ chain: args.chain,
105
+ address: `${args.address.slice(0, 10)}...${args.address.slice(-8)}`,
106
+ security_note: typeInfo[args.type as WalletType],
107
+ };
108
+ } catch (error) {
109
+ return {
110
+ success: false,
111
+ error: error instanceof Error ? error.message : "Failed to add wallet",
112
+ };
113
+ }
114
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Get card lock status
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { getStatus, isPinConfigured } from "../lib/pin-manager";
7
+ import { getCardsSafe } from "../lib/cards";
8
+
9
+ export const name = "card_status";
10
+
11
+ export const description = "Check if cards are locked or unlocked, and how many cards are saved.";
12
+
13
+ export const parameters = z.object({});
14
+
15
+ export async function execute(_args: z.infer<typeof parameters>) {
16
+ const pinStatus = getStatus();
17
+ const cards = await getCardsSafe();
18
+
19
+ return {
20
+ success: true,
21
+ pin_configured: pinStatus.configured,
22
+ cards_unlocked: pinStatus.unlocked,
23
+ remaining_minutes: pinStatus.remainingMinutes ?? null,
24
+ saved_cards: cards.length,
25
+ cards: cards.map((c) => ({
26
+ nickname: c.nickname,
27
+ type: c.cardType.toUpperCase(),
28
+ lastFour: `****${c.lastFourDigits}`,
29
+ enabled: c.enabled,
30
+ })),
31
+ };
32
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Confirm and execute a prepared payment
3
+ *
4
+ * This is where the actual charge would happen.
5
+ * CVV is retrieved from encrypted storage (requires PIN unlock).
6
+ */
7
+
8
+ import { z } from "zod";
9
+ import { getCard, getCardCvv, getTransactions, updateTransaction, updateCardUsage } from "../lib/cards";
10
+ import { isUnlocked } from "../lib/pin-manager";
11
+
12
+ export const name = "confirm_payment";
13
+
14
+ export const description = "Confirm and execute a prepared payment. Cards must be unlocked with your PIN. CVV is retrieved from secure storage.";
15
+
16
+ export const parameters = z.object({
17
+ transaction_id: z.string().describe("ID of the pending transaction to confirm"),
18
+ confirm: z.boolean().describe("Must be true to confirm you want to proceed with the charge"),
19
+ });
20
+
21
+ export async function execute(args: z.infer<typeof parameters>) {
22
+ if (!args.confirm) {
23
+ return {
24
+ success: false,
25
+ error: "You must set confirm=true to proceed with the payment",
26
+ };
27
+ }
28
+
29
+ // Check cards are unlocked
30
+ if (!isUnlocked()) {
31
+ return {
32
+ success: false,
33
+ error: "Cards are locked. Use unlock_cards with your PIN first.",
34
+ };
35
+ }
36
+
37
+ // Find the pending transaction
38
+ const transactions = await getTransactions(100);
39
+ const tx = transactions.find((t) => t.id === args.transaction_id);
40
+
41
+ if (!tx) {
42
+ return {
43
+ success: false,
44
+ error: "Transaction not found",
45
+ };
46
+ }
47
+
48
+ if (tx.status !== "pending") {
49
+ return {
50
+ success: false,
51
+ error: `Transaction is already ${tx.status}`,
52
+ };
53
+ }
54
+
55
+ // Get the card
56
+ const card = await getCard(tx.cardId);
57
+ if (!card) {
58
+ return {
59
+ success: false,
60
+ error: "Card no longer exists",
61
+ };
62
+ }
63
+
64
+ // Get CVV from encrypted storage
65
+ const cvv = await getCardCvv(tx.cardId);
66
+ if (!cvv) {
67
+ return {
68
+ success: false,
69
+ error: "Could not retrieve CVV. Make sure cards are unlocked.",
70
+ };
71
+ }
72
+
73
+ // Here we would integrate with actual payment providers
74
+ // CVV is available in `cvv` variable for the API call
75
+ // For now, we mark as confirmed and return what would be needed
76
+
77
+ await updateTransaction(tx.id, {
78
+ status: "confirmed",
79
+ confirmedAt: new Date().toISOString(),
80
+ });
81
+
82
+ await updateCardUsage(card.id);
83
+
84
+ return {
85
+ success: true,
86
+ status: "confirmed",
87
+ transaction_id: tx.id,
88
+ message: `Payment of ${tx.amount} ${tx.currency} confirmed`,
89
+ summary: {
90
+ card: `${card.nickname} (****${card.lastFourDigits})`,
91
+ amount: `${tx.amount} ${tx.currency}`,
92
+ type: tx.type,
93
+ description: tx.description,
94
+ provider: tx.provider,
95
+ },
96
+ // In production, this would return actual booking reference
97
+ note: "Integration with payment provider needed. Transaction marked as confirmed.",
98
+ next_steps: [
99
+ "Integrate with Stripe/payment processor for actual charge",
100
+ "Integrate with booking APIs (Amadeus, Trainline, etc.) for reservations",
101
+ ],
102
+ };
103
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Get consolidated view of all balances
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { getCardsSafe } from "../lib/cards";
7
+ import { getWalletsSafe, type Chain } from "../lib/wallets";
8
+ import { getWalletBalance, getUsdPrice } from "../lib/blockchain";
9
+
10
+ export const name = "get_total_balance";
11
+
12
+ export const description = "Get a consolidated view of all financial accounts: cards and crypto wallets. Shows individual and total balances.";
13
+
14
+ export const parameters = z.object({
15
+ include_disabled: z.boolean().optional().describe("Include disabled accounts (default: false)"),
16
+ crypto_only: z.boolean().optional().describe("Only show crypto wallets"),
17
+ cards_only: z.boolean().optional().describe("Only show payment cards"),
18
+ });
19
+
20
+ interface BalanceItem {
21
+ id: string;
22
+ type: "card" | "wallet";
23
+ name: string;
24
+ chain?: string;
25
+ balance?: string;
26
+ currency?: string;
27
+ usdValue?: number;
28
+ status: "active" | "disabled" | "error";
29
+ error?: string;
30
+ }
31
+
32
+ export async function execute(args: z.infer<typeof parameters>) {
33
+ const includeDisabled = args.include_disabled ?? false;
34
+ const items: BalanceItem[] = [];
35
+ let totalUsdValue = 0;
36
+
37
+ // Get cards (if not crypto_only)
38
+ if (!args.crypto_only) {
39
+ const cards = await getCardsSafe();
40
+ const filteredCards = includeDisabled ? cards : cards.filter(c => c.enabled);
41
+
42
+ for (const card of filteredCards) {
43
+ items.push({
44
+ id: card.id,
45
+ type: "card",
46
+ name: `${card.nickname} (****${card.lastFourDigits})`,
47
+ currency: card.limits?.currency || "EUR",
48
+ balance: "N/A", // Requires bank integration
49
+ status: card.enabled ? "active" : "disabled",
50
+ });
51
+ }
52
+ }
53
+
54
+ // Get wallets (if not cards_only)
55
+ if (!args.cards_only) {
56
+ const wallets = await getWalletsSafe();
57
+ const filteredWallets = includeDisabled ? wallets : wallets.filter(w => w.enabled);
58
+
59
+ // Fetch balances in parallel
60
+ const balancePromises = filteredWallets.map(async (wallet) => {
61
+ try {
62
+ const balance = await getWalletBalance(wallet.address, wallet.chain as Chain);
63
+ const price = await getUsdPrice(balance.nativeCurrency);
64
+ const usdValue = price ? parseFloat(balance.nativeBalanceFormatted) * price : undefined;
65
+
66
+ if (usdValue) {
67
+ totalUsdValue += usdValue;
68
+ }
69
+
70
+ return {
71
+ id: wallet.id,
72
+ type: "wallet" as const,
73
+ name: wallet.nickname,
74
+ chain: wallet.chain,
75
+ balance: balance.nativeBalanceFormatted,
76
+ currency: balance.nativeCurrency,
77
+ usdValue,
78
+ status: wallet.enabled ? "active" as const : "disabled" as const,
79
+ };
80
+ } catch (err) {
81
+ return {
82
+ id: wallet.id,
83
+ type: "wallet" as const,
84
+ name: wallet.nickname,
85
+ chain: wallet.chain,
86
+ status: "error" as const,
87
+ error: err instanceof Error ? err.message : "Failed to fetch balance",
88
+ };
89
+ }
90
+ });
91
+
92
+ const walletResults = await Promise.all(balancePromises);
93
+ items.push(...walletResults);
94
+ }
95
+
96
+ // Separate by type for summary
97
+ const cardItems = items.filter(i => i.type === "card");
98
+ const walletItems = items.filter(i => i.type === "wallet");
99
+ const errorItems = items.filter(i => i.status === "error");
100
+
101
+ return {
102
+ success: true,
103
+ summary: {
104
+ totalAccounts: items.length,
105
+ cards: cardItems.length,
106
+ wallets: walletItems.length,
107
+ errors: errorItems.length,
108
+ totalCryptoUsd: totalUsdValue > 0 ? `$${totalUsdValue.toFixed(2)}` : null,
109
+ note: cardItems.length > 0 ? "Card balances require bank integration (Plaid/Tink)" : undefined,
110
+ },
111
+ accounts: items.map(item => ({
112
+ id: item.id,
113
+ type: item.type,
114
+ name: item.name,
115
+ chain: item.chain,
116
+ balance: item.balance || "N/A",
117
+ currency: item.currency,
118
+ usdValue: item.usdValue ? `$${item.usdValue.toFixed(2)}` : null,
119
+ status: item.status,
120
+ ...(item.error && { error: item.error }),
121
+ })),
122
+ };
123
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Get transaction history
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { getTransactions } from "../lib/cards";
7
+
8
+ export const name = "get_transactions";
9
+
10
+ export const description = "Get payment transaction history. Shows all bookings and payments made through the system.";
11
+
12
+ export const parameters = z.object({
13
+ limit: z.number().optional().describe("Number of transactions to return (default: 20)"),
14
+ status: z.enum(["pending", "confirmed", "completed", "failed", "refunded"]).optional()
15
+ .describe("Filter by transaction status"),
16
+ type: z.enum(["flight", "train", "hotel", "other"]).optional()
17
+ .describe("Filter by transaction type"),
18
+ });
19
+
20
+ export async function execute(args: z.infer<typeof parameters>) {
21
+ let transactions = await getTransactions(args.limit ?? 20);
22
+
23
+ if (args.status) {
24
+ transactions = transactions.filter((t) => t.status === args.status);
25
+ }
26
+
27
+ if (args.type) {
28
+ transactions = transactions.filter((t) => t.type === args.type);
29
+ }
30
+
31
+ if (transactions.length === 0) {
32
+ return { success: true, transactions: [], message: "No transactions found" };
33
+ }
34
+
35
+ return {
36
+ success: true,
37
+ count: transactions.length,
38
+ transactions: transactions.map((t) => ({
39
+ id: t.id,
40
+ type: t.type,
41
+ amount: `${t.amount} ${t.currency}`,
42
+ description: t.description,
43
+ provider: t.provider,
44
+ status: t.status,
45
+ reference: t.reference ?? "N/A",
46
+ date: t.createdAt,
47
+ })),
48
+ };
49
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Get wallet balance via blockchain API
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { getWallet, getWalletsSafe, type Chain } from "../lib/wallets";
7
+ import { getWalletBalance, getUsdPrice } from "../lib/blockchain";
8
+
9
+ export const name = "get_wallet_balance";
10
+
11
+ export const description = "Get the current balance of a crypto wallet. Fetches live data from the blockchain.";
12
+
13
+ export const parameters = z.object({
14
+ wallet_id: z.string().optional().describe("Wallet ID (use list_wallets to see available wallets)"),
15
+ address: z.string().optional().describe("Wallet address (alternative to wallet_id)"),
16
+ chain: z.enum(["ethereum", "polygon", "arbitrum", "optimism", "base", "avalanche", "bsc", "bitcoin", "solana", "starknet"]).optional()
17
+ .describe("Blockchain (required if using address)"),
18
+ include_usd: z.boolean().optional().describe("Include USD value conversion (default: true)"),
19
+ });
20
+
21
+ export async function execute(args: z.infer<typeof parameters>) {
22
+ let address: string;
23
+ let chain: Chain;
24
+
25
+ // Get wallet by ID or use provided address
26
+ if (args.wallet_id) {
27
+ const wallet = await getWallet(args.wallet_id);
28
+ if (!wallet) {
29
+ return { success: false, error: `Wallet not found: ${args.wallet_id}` };
30
+ }
31
+ address = wallet.address;
32
+ chain = wallet.chain;
33
+ } else if (args.address && args.chain) {
34
+ address = args.address;
35
+ chain = args.chain;
36
+ } else {
37
+ return {
38
+ success: false,
39
+ error: "Provide either wallet_id or both address and chain"
40
+ };
41
+ }
42
+
43
+ try {
44
+ const balance = await getWalletBalance(address, chain);
45
+
46
+ // Get USD price if requested
47
+ let usdValue: number | null = null;
48
+ if (args.include_usd !== false) {
49
+ const price = await getUsdPrice(balance.nativeCurrency);
50
+ if (price) {
51
+ usdValue = parseFloat(balance.nativeBalanceFormatted) * price;
52
+ }
53
+ }
54
+
55
+ return {
56
+ success: true,
57
+ wallet: {
58
+ address: `${address.slice(0, 10)}...${address.slice(-8)}`,
59
+ fullAddress: address,
60
+ chain,
61
+ },
62
+ balance: {
63
+ native: balance.nativeBalanceFormatted,
64
+ currency: balance.nativeCurrency,
65
+ raw: balance.nativeBalance,
66
+ usdValue: usdValue ? `$${usdValue.toFixed(2)}` : null,
67
+ },
68
+ };
69
+ } catch (err) {
70
+ return {
71
+ success: false,
72
+ error: `Failed to fetch balance: ${err instanceof Error ? err.message : err}`,
73
+ };
74
+ }
75
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * List saved payment cards (safe view - no full card numbers)
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { getCardsSafe } from "../lib/cards";
7
+
8
+ export const name = "list_cards";
9
+
10
+ export const description = "List all saved payment cards. Shows card type, last 4 digits, nickname, and allowed usage. Does NOT show full card numbers.";
11
+
12
+ export const parameters = z.object({
13
+ enabled_only: z.boolean().optional().describe("Only show enabled cards"),
14
+ usage_type: z.enum(["flight", "train", "hotel", "general", "all"]).optional().describe("Filter by allowed usage type"),
15
+ });
16
+
17
+ export async function execute(args: z.infer<typeof parameters>) {
18
+ const cards = await getCardsSafe();
19
+
20
+ let filtered = cards;
21
+
22
+ if (args.enabled_only) {
23
+ filtered = filtered.filter((c) => c.enabled);
24
+ }
25
+
26
+ if (args.usage_type) {
27
+ filtered = filtered.filter((c) =>
28
+ c.allowedUsage.includes(args.usage_type!) || c.allowedUsage.includes("all")
29
+ );
30
+ }
31
+
32
+ if (filtered.length === 0) {
33
+ return { success: true, cards: [], message: "No cards found" };
34
+ }
35
+
36
+ const summary = filtered.map((c) => ({
37
+ id: c.id,
38
+ nickname: c.nickname,
39
+ type: c.cardType.toUpperCase(),
40
+ lastFour: `****${c.lastFourDigits}`,
41
+ cardholder: c.cardholderName,
42
+ usage: c.allowedUsage.join(", "),
43
+ enabled: c.enabled,
44
+ lastUsed: c.lastUsedAt ?? "Never",
45
+ }));
46
+
47
+ return {
48
+ success: true,
49
+ count: summary.length,
50
+ cards: summary,
51
+ };
52
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * List wallet transactions from blockchain
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { getWallet, type Chain } from "../lib/wallets";
7
+ import { getWalletTransactions } from "../lib/blockchain";
8
+
9
+ export const name = "list_wallet_transactions";
10
+
11
+ export const description = "List recent transactions for a crypto wallet. Fetches live data from blockchain explorers.";
12
+
13
+ export const parameters = z.object({
14
+ wallet_id: z.string().optional().describe("Wallet ID (use list_wallets to see available wallets)"),
15
+ address: z.string().optional().describe("Wallet address (alternative to wallet_id)"),
16
+ chain: z.enum(["ethereum", "polygon", "arbitrum", "optimism", "base", "avalanche", "bsc", "bitcoin", "solana", "starknet"]).optional()
17
+ .describe("Blockchain (required if using address)"),
18
+ limit: z.number().optional().describe("Number of transactions to return (default: 20, max: 50)"),
19
+ });
20
+
21
+ export async function execute(args: z.infer<typeof parameters>) {
22
+ let address: string;
23
+ let chain: Chain;
24
+
25
+ // Get wallet by ID or use provided address
26
+ if (args.wallet_id) {
27
+ const wallet = await getWallet(args.wallet_id);
28
+ if (!wallet) {
29
+ return { success: false, error: `Wallet not found: ${args.wallet_id}` };
30
+ }
31
+ address = wallet.address;
32
+ chain = wallet.chain;
33
+ } else if (args.address && args.chain) {
34
+ address = args.address;
35
+ chain = args.chain;
36
+ } else {
37
+ return {
38
+ success: false,
39
+ error: "Provide either wallet_id or both address and chain"
40
+ };
41
+ }
42
+
43
+ const limit = Math.min(args.limit ?? 20, 50);
44
+
45
+ try {
46
+ const transactions = await getWalletTransactions(address, chain, limit);
47
+
48
+ if (transactions.length === 0) {
49
+ return {
50
+ success: true,
51
+ wallet: { address: `${address.slice(0, 10)}...${address.slice(-8)}`, chain },
52
+ transactions: [],
53
+ message: "No transactions found",
54
+ };
55
+ }
56
+
57
+ return {
58
+ success: true,
59
+ wallet: {
60
+ address: `${address.slice(0, 10)}...${address.slice(-8)}`,
61
+ fullAddress: address,
62
+ chain,
63
+ },
64
+ count: transactions.length,
65
+ transactions: transactions.map((tx) => ({
66
+ hash: `${tx.hash.slice(0, 10)}...${tx.hash.slice(-8)}`,
67
+ fullHash: tx.hash,
68
+ type: tx.isIncoming ? "RECEIVED" : "SENT",
69
+ amount: tx.valueFormatted,
70
+ from: tx.from ? `${tx.from.slice(0, 8)}...` : "N/A",
71
+ to: tx.to ? `${tx.to.slice(0, 8)}...` : "N/A",
72
+ status: tx.status,
73
+ date: new Date(tx.timestamp).toISOString(),
74
+ fee: tx.fee || null,
75
+ })),
76
+ };
77
+ } catch (err) {
78
+ return {
79
+ success: false,
80
+ error: `Failed to fetch transactions: ${err instanceof Error ? err.message : err}`,
81
+ };
82
+ }
83
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * List saved crypto wallets (safe view - no private keys)
3
+ */
4
+
5
+ import { z } from "zod";
6
+ import { getWalletsSafe, CHAIN_CONFIG } from "../lib/wallets";
7
+ import type { Chain } from "../lib/wallets";
8
+
9
+ export const name = "list_wallets";
10
+
11
+ export const description = "List all saved crypto wallets. Shows address, chain, type, and allowed operations. Does NOT show private keys.";
12
+
13
+ export const parameters = z.object({
14
+ chain: z.enum(["ethereum", "polygon", "arbitrum", "optimism", "base", "avalanche", "bsc", "bitcoin", "solana", "starknet"]).optional()
15
+ .describe("Filter by blockchain"),
16
+ type: z.enum(["hot", "watch-only", "hardware"]).optional()
17
+ .describe("Filter by wallet type"),
18
+ enabled_only: z.boolean().optional().describe("Only show enabled wallets"),
19
+ });
20
+
21
+ export async function execute(args: z.infer<typeof parameters>) {
22
+ const wallets = await getWalletsSafe();
23
+
24
+ let filtered = wallets;
25
+
26
+ if (args.chain) {
27
+ filtered = filtered.filter((w) => w.chain === args.chain);
28
+ }
29
+
30
+ if (args.type) {
31
+ filtered = filtered.filter((w) => w.type === args.type);
32
+ }
33
+
34
+ if (args.enabled_only) {
35
+ filtered = filtered.filter((w) => w.enabled);
36
+ }
37
+
38
+ if (filtered.length === 0) {
39
+ return { success: true, wallets: [], message: "No wallets found" };
40
+ }
41
+
42
+ const summary = filtered.map((w) => {
43
+ const chainInfo = CHAIN_CONFIG[w.chain as Chain];
44
+ return {
45
+ id: w.id,
46
+ nickname: w.nickname,
47
+ chain: chainInfo?.name ?? w.chain,
48
+ nativeCurrency: chainInfo?.nativeCurrency ?? "?",
49
+ address: `${w.address.slice(0, 10)}...${w.address.slice(-8)}`,
50
+ fullAddress: w.address,
51
+ type: w.type,
52
+ operations: w.allowedOperations.join(", "),
53
+ enabled: w.enabled,
54
+ lastUsed: w.lastUsedAt ?? "Never",
55
+ };
56
+ });
57
+
58
+ return {
59
+ success: true,
60
+ count: summary.length,
61
+ wallets: summary,
62
+ };
63
+ }