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.
- package/.env.example +23 -0
- package/.github/workflows/ci.yml +26 -0
- package/.github/workflows/release.yml +82 -0
- package/LICENSE +21 -0
- package/README.md +362 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +122 -0
- package/dist/lib/blockchain.d.ts +50 -0
- package/dist/lib/blockchain.js +287 -0
- package/dist/lib/cards.d.ts +83 -0
- package/dist/lib/cards.js +276 -0
- package/dist/lib/cli-runner.d.ts +31 -0
- package/dist/lib/cli-runner.js +77 -0
- package/dist/lib/crypto.d.ts +39 -0
- package/dist/lib/crypto.js +228 -0
- package/dist/lib/cvv-crypto.d.ts +23 -0
- package/dist/lib/cvv-crypto.js +67 -0
- package/dist/lib/mcp-core.d.ts +46 -0
- package/dist/lib/mcp-core.js +86 -0
- package/dist/lib/pin-manager.d.ts +69 -0
- package/dist/lib/pin-manager.js +199 -0
- package/dist/lib/wallets.d.ts +91 -0
- package/dist/lib/wallets.js +227 -0
- package/dist/tools/add-card.d.ts +65 -0
- package/dist/tools/add-card.js +97 -0
- package/dist/tools/add-wallet.d.ts +65 -0
- package/dist/tools/add-wallet.js +104 -0
- package/dist/tools/card-status.d.ts +20 -0
- package/dist/tools/card-status.js +26 -0
- package/dist/tools/confirm-payment.d.ts +44 -0
- package/dist/tools/confirm-payment.js +88 -0
- package/dist/tools/get-total-balance.d.ts +41 -0
- package/dist/tools/get-total-balance.js +98 -0
- package/dist/tools/get-transactions.d.ts +39 -0
- package/dist/tools/get-transactions.js +40 -0
- package/dist/tools/get-wallet-balance.d.ts +43 -0
- package/dist/tools/get-wallet-balance.js +69 -0
- package/dist/tools/list-cards.d.ts +36 -0
- package/dist/tools/list-cards.js +39 -0
- package/dist/tools/list-wallet-transactions.d.ts +63 -0
- package/dist/tools/list-wallet-transactions.js +76 -0
- package/dist/tools/list-wallets.d.ts +41 -0
- package/dist/tools/list-wallets.js +50 -0
- package/dist/tools/lock-cards.d.ts +16 -0
- package/dist/tools/lock-cards.js +23 -0
- package/dist/tools/prepare-crypto-tx.d.ts +69 -0
- package/dist/tools/prepare-crypto-tx.js +93 -0
- package/dist/tools/prepare-payment.d.ts +57 -0
- package/dist/tools/prepare-payment.js +93 -0
- package/dist/tools/remove-card.d.ts +25 -0
- package/dist/tools/remove-card.js +39 -0
- package/dist/tools/remove-wallet.d.ts +27 -0
- package/dist/tools/remove-wallet.js +40 -0
- package/dist/tools/setup-pin.d.ts +26 -0
- package/dist/tools/setup-pin.js +33 -0
- package/dist/tools/sign-crypto-tx.d.ts +42 -0
- package/dist/tools/sign-crypto-tx.js +75 -0
- package/dist/tools/unlock-cards.d.ts +35 -0
- package/dist/tools/unlock-cards.js +41 -0
- package/package.json +50 -0
- package/src/index.ts +139 -0
- package/src/lib/blockchain.ts +375 -0
- package/src/lib/cards.ts +372 -0
- package/src/lib/cli-runner.ts +113 -0
- package/src/lib/crypto.ts +284 -0
- package/src/lib/cvv-crypto.ts +81 -0
- package/src/lib/mcp-core.ts +127 -0
- package/src/lib/pin-manager.ts +252 -0
- package/src/lib/wallets.ts +331 -0
- package/src/tools/add-card.ts +108 -0
- package/src/tools/add-wallet.ts +114 -0
- package/src/tools/card-status.ts +32 -0
- package/src/tools/confirm-payment.ts +103 -0
- package/src/tools/get-total-balance.ts +123 -0
- package/src/tools/get-transactions.ts +49 -0
- package/src/tools/get-wallet-balance.ts +75 -0
- package/src/tools/list-cards.ts +52 -0
- package/src/tools/list-wallet-transactions.ts +83 -0
- package/src/tools/list-wallets.ts +63 -0
- package/src/tools/lock-cards.ts +31 -0
- package/src/tools/prepare-crypto-tx.ts +108 -0
- package/src/tools/prepare-payment.ts +108 -0
- package/src/tools/remove-card.ts +46 -0
- package/src/tools/remove-wallet.ts +47 -0
- package/src/tools/setup-pin.ts +39 -0
- package/src/tools/sign-crypto-tx.ts +90 -0
- package/src/tools/unlock-cards.ts +48 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prepare a payment for confirmation
|
|
3
|
+
*
|
|
4
|
+
* This creates a pending transaction that must be confirmed before execution.
|
|
5
|
+
* Security: User must explicitly confirm with CVV before any charge.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { randomUUID } from "crypto";
|
|
9
|
+
import { getCard, logTransaction } from "../lib/cards";
|
|
10
|
+
import { isUnlocked, isPinConfigured } from "../lib/pin-manager";
|
|
11
|
+
export const name = "prepare_payment";
|
|
12
|
+
export const description = "Prepare a payment for a booking (flight, train, hotel, etc.). This creates a PENDING transaction that must be confirmed with confirm_payment before any charge is made.";
|
|
13
|
+
export const parameters = z.object({
|
|
14
|
+
card_id: z.string().describe("ID of the card to use"),
|
|
15
|
+
amount: z.number().describe("Amount to charge"),
|
|
16
|
+
currency: z.string().describe("Currency code (EUR, USD, etc.)"),
|
|
17
|
+
type: z.enum(["flight", "train", "hotel", "general"]).describe("Type of purchase"),
|
|
18
|
+
description: z.string().describe("What is being purchased (e.g., 'Paris-London Eurostar 15 Feb')"),
|
|
19
|
+
provider: z.string().describe("Booking provider (e.g., 'Trainline', 'Air France', 'Booking.com')"),
|
|
20
|
+
details: z.record(z.unknown()).optional().describe("Additional booking details"),
|
|
21
|
+
});
|
|
22
|
+
export async function execute(args) {
|
|
23
|
+
// Check cards are unlocked
|
|
24
|
+
if (!isPinConfigured()) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: "Master PIN not configured. Use setup_pin first.",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (!isUnlocked()) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: "Cards are locked. Use unlock_cards with your PIN first.",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// Verify card exists and is enabled
|
|
37
|
+
const card = await getCard(args.card_id);
|
|
38
|
+
if (!card) {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: "Card not found",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (!card.enabled) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: "Card is disabled",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Check usage restrictions
|
|
51
|
+
if (!card.allowedUsage.includes("all") && !card.allowedUsage.includes(args.type)) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: `Card "${card.nickname}" is not allowed for ${args.type} purchases. Allowed: ${card.allowedUsage.join(", ")}`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Check limits
|
|
58
|
+
if (card.limits?.perTransaction && args.amount > card.limits.perTransaction) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: `Amount exceeds per-transaction limit of ${card.limits.perTransaction} ${card.limits.currency}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Create pending transaction
|
|
65
|
+
const txId = randomUUID();
|
|
66
|
+
const transaction = {
|
|
67
|
+
id: txId,
|
|
68
|
+
cardId: card.id,
|
|
69
|
+
type: args.type,
|
|
70
|
+
amount: args.amount,
|
|
71
|
+
currency: args.currency,
|
|
72
|
+
description: args.description,
|
|
73
|
+
provider: args.provider,
|
|
74
|
+
status: "pending",
|
|
75
|
+
createdAt: new Date().toISOString(),
|
|
76
|
+
details: args.details,
|
|
77
|
+
};
|
|
78
|
+
await logTransaction(transaction);
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
transaction_id: txId,
|
|
82
|
+
status: "pending",
|
|
83
|
+
summary: {
|
|
84
|
+
card: `${card.nickname} (****${card.lastFourDigits})`,
|
|
85
|
+
amount: `${args.amount} ${args.currency}`,
|
|
86
|
+
type: args.type,
|
|
87
|
+
description: args.description,
|
|
88
|
+
provider: args.provider,
|
|
89
|
+
},
|
|
90
|
+
next_step: "Call confirm_payment with this transaction_id and the card CVV to complete the payment",
|
|
91
|
+
warning: "NO CHARGE has been made yet. You must confirm to proceed.",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove a payment card
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
export declare const name = "remove_card";
|
|
6
|
+
export declare const description = "Remove a saved payment card. This action is irreversible.";
|
|
7
|
+
export declare const parameters: z.ZodObject<{
|
|
8
|
+
card_id: z.ZodString;
|
|
9
|
+
confirm: z.ZodBoolean;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
card_id: string;
|
|
12
|
+
confirm: boolean;
|
|
13
|
+
}, {
|
|
14
|
+
card_id: string;
|
|
15
|
+
confirm: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function execute(args: z.infer<typeof parameters>): Promise<{
|
|
18
|
+
success: boolean;
|
|
19
|
+
error: string;
|
|
20
|
+
message?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
success: boolean;
|
|
23
|
+
message: string;
|
|
24
|
+
error?: undefined;
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove a payment card
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { removeCard, getCard } from "../lib/cards";
|
|
6
|
+
export const name = "remove_card";
|
|
7
|
+
export const description = "Remove a saved payment card. This action is irreversible.";
|
|
8
|
+
export const parameters = z.object({
|
|
9
|
+
card_id: z.string().describe("ID of the card to remove"),
|
|
10
|
+
confirm: z.boolean().describe("Must be true to confirm deletion"),
|
|
11
|
+
});
|
|
12
|
+
export async function execute(args) {
|
|
13
|
+
if (!args.confirm) {
|
|
14
|
+
return {
|
|
15
|
+
success: false,
|
|
16
|
+
error: "You must set confirm=true to delete a card",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const card = await getCard(args.card_id);
|
|
20
|
+
if (!card) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: "Card not found",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const removed = await removeCard(args.card_id);
|
|
27
|
+
if (removed) {
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
message: `Card "${card.nickname}" (****${card.lastFourDigits}) has been removed`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
error: "Failed to remove card",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove a crypto wallet
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
export declare const name = "remove_wallet";
|
|
6
|
+
export declare const description = "Remove a saved crypto wallet. This deletes the stored keys (if any). This action is irreversible.";
|
|
7
|
+
export declare const parameters: z.ZodObject<{
|
|
8
|
+
wallet_id: z.ZodString;
|
|
9
|
+
confirm: z.ZodBoolean;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
confirm: boolean;
|
|
12
|
+
wallet_id: string;
|
|
13
|
+
}, {
|
|
14
|
+
confirm: boolean;
|
|
15
|
+
wallet_id: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function execute(args: z.infer<typeof parameters>): Promise<{
|
|
18
|
+
success: boolean;
|
|
19
|
+
error: string;
|
|
20
|
+
message?: undefined;
|
|
21
|
+
warning?: undefined;
|
|
22
|
+
} | {
|
|
23
|
+
success: boolean;
|
|
24
|
+
message: string;
|
|
25
|
+
warning: string | undefined;
|
|
26
|
+
error?: undefined;
|
|
27
|
+
}>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove a crypto wallet
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { removeWallet, getWallet } from "../lib/wallets";
|
|
6
|
+
export const name = "remove_wallet";
|
|
7
|
+
export const description = "Remove a saved crypto wallet. This deletes the stored keys (if any). This action is irreversible.";
|
|
8
|
+
export const parameters = z.object({
|
|
9
|
+
wallet_id: z.string().describe("ID of the wallet to remove"),
|
|
10
|
+
confirm: z.boolean().describe("Must be true to confirm deletion"),
|
|
11
|
+
});
|
|
12
|
+
export async function execute(args) {
|
|
13
|
+
if (!args.confirm) {
|
|
14
|
+
return {
|
|
15
|
+
success: false,
|
|
16
|
+
error: "You must set confirm=true to delete a wallet",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const wallet = await getWallet(args.wallet_id);
|
|
20
|
+
if (!wallet) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: "Wallet not found",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const removed = await removeWallet(args.wallet_id);
|
|
27
|
+
if (removed) {
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
message: `Wallet "${wallet.nickname}" (${wallet.address.slice(0, 10)}...) has been removed`,
|
|
31
|
+
warning: wallet.type === "hot" ? "Private key has been deleted" : undefined,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
error: "Failed to remove wallet",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set up master PIN for card access
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
export declare const name = "setup_pin";
|
|
6
|
+
export declare const description = "Set up a master PIN (4-8 characters) for card access. This PIN encrypts your CVVs and must be entered to unlock cards for payments. Choose something you'll remember!";
|
|
7
|
+
export declare const parameters: z.ZodObject<{
|
|
8
|
+
pin: z.ZodString;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
pin: string;
|
|
11
|
+
}, {
|
|
12
|
+
pin: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function execute(args: z.infer<typeof parameters>): Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
note: string;
|
|
18
|
+
session_timeout: string;
|
|
19
|
+
error?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
success: boolean;
|
|
22
|
+
error: string | undefined;
|
|
23
|
+
message?: undefined;
|
|
24
|
+
note?: undefined;
|
|
25
|
+
session_timeout?: undefined;
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set up master PIN for card access
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { setupPin, isPinConfigured } from "../lib/pin-manager";
|
|
6
|
+
export const name = "setup_pin";
|
|
7
|
+
export const description = "Set up a master PIN (4-8 characters) for card access. This PIN encrypts your CVVs and must be entered to unlock cards for payments. Choose something you'll remember!";
|
|
8
|
+
export const parameters = z.object({
|
|
9
|
+
pin: z.string().describe("Your master PIN (4-8 characters). This will encrypt your CVVs."),
|
|
10
|
+
});
|
|
11
|
+
export async function execute(args) {
|
|
12
|
+
if (isPinConfigured()) {
|
|
13
|
+
return {
|
|
14
|
+
success: false,
|
|
15
|
+
error: "PIN already configured. Use change_pin to modify it.",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const result = setupPin(args.pin);
|
|
19
|
+
if (result.success) {
|
|
20
|
+
return {
|
|
21
|
+
success: true,
|
|
22
|
+
message: "Master PIN configured! Cards are now unlocked.",
|
|
23
|
+
note: "Your PIN encrypts all CVVs. Without it, cards cannot be used.",
|
|
24
|
+
session_timeout: "30 minutes of inactivity will lock cards automatically",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
error: result.error,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign and broadcast a prepared crypto transaction
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
export declare const name = "sign_crypto_tx";
|
|
6
|
+
export declare const description = "Sign and broadcast a prepared crypto transaction.\n\nFor hot wallets: signs with the stored private key.\nFor hardware wallets: requires the device to be connected (integration needed).\nFor watch-only: not allowed.";
|
|
7
|
+
export declare const parameters: z.ZodObject<{
|
|
8
|
+
transaction_id: z.ZodString;
|
|
9
|
+
confirm: z.ZodBoolean;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
confirm: boolean;
|
|
12
|
+
transaction_id: string;
|
|
13
|
+
}, {
|
|
14
|
+
confirm: boolean;
|
|
15
|
+
transaction_id: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function execute(args: z.infer<typeof parameters>): Promise<{
|
|
18
|
+
success: boolean;
|
|
19
|
+
error: string;
|
|
20
|
+
status?: undefined;
|
|
21
|
+
transaction_id?: undefined;
|
|
22
|
+
message?: undefined;
|
|
23
|
+
summary?: undefined;
|
|
24
|
+
note?: undefined;
|
|
25
|
+
next_steps?: undefined;
|
|
26
|
+
explorer?: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
success: boolean;
|
|
29
|
+
status: string;
|
|
30
|
+
transaction_id: string;
|
|
31
|
+
message: string;
|
|
32
|
+
summary: {
|
|
33
|
+
from: string;
|
|
34
|
+
to: string;
|
|
35
|
+
amount: string;
|
|
36
|
+
chain: string;
|
|
37
|
+
};
|
|
38
|
+
note: string;
|
|
39
|
+
next_steps: string[];
|
|
40
|
+
explorer: string;
|
|
41
|
+
error?: undefined;
|
|
42
|
+
}>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign and broadcast a prepared crypto transaction
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { getWallet, getCryptoTransactions, updateCryptoTransaction, CHAIN_CONFIG } from "../lib/wallets";
|
|
6
|
+
export const name = "sign_crypto_tx";
|
|
7
|
+
export const description = `Sign and broadcast a prepared crypto transaction.
|
|
8
|
+
|
|
9
|
+
For hot wallets: signs with the stored private key.
|
|
10
|
+
For hardware wallets: requires the device to be connected (integration needed).
|
|
11
|
+
For watch-only: not allowed.`;
|
|
12
|
+
export const parameters = z.object({
|
|
13
|
+
transaction_id: z.string().describe("ID of the pending transaction to sign"),
|
|
14
|
+
confirm: z.boolean().describe("Must be true to confirm you want to sign and broadcast"),
|
|
15
|
+
});
|
|
16
|
+
export async function execute(args) {
|
|
17
|
+
if (!args.confirm) {
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
error: "You must set confirm=true to sign and broadcast the transaction",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Find the pending transaction
|
|
24
|
+
const transactions = await getCryptoTransactions(100);
|
|
25
|
+
const tx = transactions.find((t) => t.id === args.transaction_id);
|
|
26
|
+
if (!tx) {
|
|
27
|
+
return { success: false, error: "Transaction not found" };
|
|
28
|
+
}
|
|
29
|
+
if (tx.status !== "pending") {
|
|
30
|
+
return { success: false, error: `Transaction is already ${tx.status}` };
|
|
31
|
+
}
|
|
32
|
+
// Get the wallet
|
|
33
|
+
const wallet = await getWallet(tx.walletId);
|
|
34
|
+
if (!wallet) {
|
|
35
|
+
return { success: false, error: "Wallet no longer exists" };
|
|
36
|
+
}
|
|
37
|
+
if (wallet.type === "watch-only") {
|
|
38
|
+
return { success: false, error: "Watch-only wallets cannot sign transactions" };
|
|
39
|
+
}
|
|
40
|
+
const chainConfig = CHAIN_CONFIG[wallet.chain];
|
|
41
|
+
// Here we would:
|
|
42
|
+
// 1. For hot wallets: sign with ethers.js/viem using the private key
|
|
43
|
+
// 2. For hardware wallets: prompt for Ledger/Trezor connection
|
|
44
|
+
// 3. Broadcast to the network
|
|
45
|
+
// For now, mark as signed and return what would be needed
|
|
46
|
+
await updateCryptoTransaction(tx.id, {
|
|
47
|
+
status: "signed",
|
|
48
|
+
signedAt: new Date().toISOString(),
|
|
49
|
+
});
|
|
50
|
+
const amountDisplay = tx.tokenSymbol
|
|
51
|
+
? `${tx.value} ${tx.tokenSymbol}`
|
|
52
|
+
: `${tx.value} ${chainConfig?.nativeCurrency ?? "tokens"}`;
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
status: "signed",
|
|
56
|
+
transaction_id: tx.id,
|
|
57
|
+
message: wallet.type === "hardware"
|
|
58
|
+
? "Transaction prepared for hardware signing"
|
|
59
|
+
: "Transaction signed (ready for broadcast)",
|
|
60
|
+
summary: {
|
|
61
|
+
from: wallet.nickname,
|
|
62
|
+
to: tx.to,
|
|
63
|
+
amount: amountDisplay,
|
|
64
|
+
chain: chainConfig?.name ?? wallet.chain,
|
|
65
|
+
},
|
|
66
|
+
// In production, this would return the actual tx hash
|
|
67
|
+
note: "Integration with ethers.js/viem needed for actual signing and broadcast",
|
|
68
|
+
next_steps: [
|
|
69
|
+
"Integrate with ethers.js or viem for transaction signing",
|
|
70
|
+
"Add RPC endpoint configuration for each chain",
|
|
71
|
+
"For hardware wallets, integrate with @ledgerhq/hw-transport or similar",
|
|
72
|
+
],
|
|
73
|
+
explorer: chainConfig?.explorerUrl,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unlock cards with master PIN
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
export declare const name = "unlock_cards";
|
|
6
|
+
export declare const description = "Unlock your cards by entering your master PIN. Required before making any payment. Cards stay unlocked for 30 minutes of activity.";
|
|
7
|
+
export declare const parameters: z.ZodObject<{
|
|
8
|
+
pin: z.ZodString;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
pin: string;
|
|
11
|
+
}, {
|
|
12
|
+
pin: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function execute(args: z.infer<typeof parameters>): Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
remaining_minutes: number | undefined;
|
|
18
|
+
session_expires_in?: undefined;
|
|
19
|
+
tip?: undefined;
|
|
20
|
+
error?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
success: boolean;
|
|
23
|
+
message: string;
|
|
24
|
+
session_expires_in: string;
|
|
25
|
+
tip: string;
|
|
26
|
+
remaining_minutes?: undefined;
|
|
27
|
+
error?: undefined;
|
|
28
|
+
} | {
|
|
29
|
+
success: boolean;
|
|
30
|
+
error: string | undefined;
|
|
31
|
+
message?: undefined;
|
|
32
|
+
remaining_minutes?: undefined;
|
|
33
|
+
session_expires_in?: undefined;
|
|
34
|
+
tip?: undefined;
|
|
35
|
+
}>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unlock cards with master PIN
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { unlock, isPinConfigured, getStatus } from "../lib/pin-manager";
|
|
6
|
+
export const name = "unlock_cards";
|
|
7
|
+
export const description = "Unlock your cards by entering your master PIN. Required before making any payment. Cards stay unlocked for 30 minutes of activity.";
|
|
8
|
+
export const parameters = z.object({
|
|
9
|
+
pin: z.string().describe("Your master PIN"),
|
|
10
|
+
});
|
|
11
|
+
export async function execute(args) {
|
|
12
|
+
if (!isPinConfigured()) {
|
|
13
|
+
return {
|
|
14
|
+
success: false,
|
|
15
|
+
error: "No PIN configured. Use setup_pin first.",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const status = getStatus();
|
|
19
|
+
if (status.unlocked) {
|
|
20
|
+
return {
|
|
21
|
+
success: true,
|
|
22
|
+
message: "Cards already unlocked",
|
|
23
|
+
remaining_minutes: status.remainingMinutes,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const result = unlock(args.pin);
|
|
27
|
+
if (result.success) {
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
message: "Cards unlocked! You can now make payments.",
|
|
31
|
+
session_expires_in: `${result.expiresIn} minutes`,
|
|
32
|
+
tip: "Cards will auto-lock after 30 minutes of inactivity",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: result.error,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "globodai-mcp-payment-manager",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "MCP Server for Payment Management - Secure card storage and crypto wallet management with encryption",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-payment-manager": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"payment",
|
|
20
|
+
"crypto",
|
|
21
|
+
"wallet",
|
|
22
|
+
"blockchain",
|
|
23
|
+
"cards",
|
|
24
|
+
"finance",
|
|
25
|
+
"encryption",
|
|
26
|
+
"ai",
|
|
27
|
+
"llm",
|
|
28
|
+
"claude"
|
|
29
|
+
],
|
|
30
|
+
"author": "Kevin Valfin <kevin@globodai.com>",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/globodai-group/mcp-payment-manager.git"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
38
|
+
"@aws-sdk/client-kms": "^3.0.0",
|
|
39
|
+
"zod": "^3.25.67"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"typescript": "^5.8.3",
|
|
43
|
+
"tsx": "^4.0.0",
|
|
44
|
+
"@types/node": "^22.0.0"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/globodai-group/mcp-payment-manager#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/globodai-group/mcp-payment-manager/issues"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Payment Manager MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Comprehensive personal finance management with bank cards and crypto wallets:
|
|
7
|
+
*
|
|
8
|
+
* 🏦 CARDS (Fiat):
|
|
9
|
+
* - Encrypted card storage (AES-256-GCM + AWS KMS)
|
|
10
|
+
* - PIN-protected CVV access
|
|
11
|
+
* - Two-step payment flow (prepare → confirm)
|
|
12
|
+
* - Card status management (lock/unlock)
|
|
13
|
+
*
|
|
14
|
+
* 🪙 WALLETS (Crypto):
|
|
15
|
+
* - Multi-chain support (ETH, Polygon, Arbitrum, Base, Solana, Bitcoin)
|
|
16
|
+
* - Hot, watch-only, and hardware wallet types
|
|
17
|
+
* - Encrypted private key storage
|
|
18
|
+
* - Real-time balance and transaction fetching
|
|
19
|
+
*
|
|
20
|
+
* 🔐 SECURITY:
|
|
21
|
+
* - All sensitive data encrypted at rest
|
|
22
|
+
* - PIN-based access control
|
|
23
|
+
* - Complete audit logging
|
|
24
|
+
* - AWS KMS integration for enterprise security
|
|
25
|
+
*
|
|
26
|
+
* Environment Variables:
|
|
27
|
+
* - MCP_MASTER_KEY: Master encryption key (256-bit)
|
|
28
|
+
* - AWS_KMS_KEY_ID: AWS KMS key ARN
|
|
29
|
+
* - ETHERSCAN_API_KEY: For Ethereum data
|
|
30
|
+
* - [CHAIN]SCAN_API_KEY: For other chain data
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
34
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
35
|
+
|
|
36
|
+
// Card management tools
|
|
37
|
+
import * as addCard from "./tools/add-card.js";
|
|
38
|
+
import * as listCards from "./tools/list-cards.js";
|
|
39
|
+
import * as removeCard from "./tools/remove-card.js";
|
|
40
|
+
import * as cardStatus from "./tools/card-status.js";
|
|
41
|
+
import * as lockCards from "./tools/lock-cards.js";
|
|
42
|
+
import * as unlockCards from "./tools/unlock-cards.js";
|
|
43
|
+
|
|
44
|
+
// Wallet management tools
|
|
45
|
+
import * as addWallet from "./tools/add-wallet.js";
|
|
46
|
+
import * as listWallets from "./tools/list-wallets.js";
|
|
47
|
+
import * as removeWallet from "./tools/remove-wallet.js";
|
|
48
|
+
import * as getWalletBalance from "./tools/get-wallet-balance.js";
|
|
49
|
+
import * as getTotalBalance from "./tools/get-total-balance.js";
|
|
50
|
+
import * as listWalletTransactions from "./tools/list-wallet-transactions.js";
|
|
51
|
+
|
|
52
|
+
// Transaction tools
|
|
53
|
+
import * as getTransactions from "./tools/get-transactions.js";
|
|
54
|
+
import * as preparePayment from "./tools/prepare-payment.js";
|
|
55
|
+
import * as confirmPayment from "./tools/confirm-payment.js";
|
|
56
|
+
import * as prepareCryptoTx from "./tools/prepare-crypto-tx.js";
|
|
57
|
+
import * as signCryptoTx from "./tools/sign-crypto-tx.js";
|
|
58
|
+
|
|
59
|
+
// Security tools
|
|
60
|
+
import * as setupPin from "./tools/setup-pin.js";
|
|
61
|
+
|
|
62
|
+
const tools = [
|
|
63
|
+
// Card Management
|
|
64
|
+
addCard,
|
|
65
|
+
listCards,
|
|
66
|
+
removeCard,
|
|
67
|
+
cardStatus,
|
|
68
|
+
lockCards,
|
|
69
|
+
unlockCards,
|
|
70
|
+
// Wallet Management
|
|
71
|
+
addWallet,
|
|
72
|
+
listWallets,
|
|
73
|
+
removeWallet,
|
|
74
|
+
getWalletBalance,
|
|
75
|
+
getTotalBalance,
|
|
76
|
+
listWalletTransactions,
|
|
77
|
+
// Transactions
|
|
78
|
+
getTransactions,
|
|
79
|
+
preparePayment,
|
|
80
|
+
confirmPayment,
|
|
81
|
+
prepareCryptoTx,
|
|
82
|
+
signCryptoTx,
|
|
83
|
+
// Security
|
|
84
|
+
setupPin,
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
async function main() {
|
|
88
|
+
// Verify critical environment variables
|
|
89
|
+
const requiredEnvs = ['MCP_MASTER_KEY'];
|
|
90
|
+
const missing = requiredEnvs.filter(env => !process.env[env]);
|
|
91
|
+
if (missing.length > 0) {
|
|
92
|
+
console.error(`❌ Missing critical environment variables: ${missing.join(', ')}`);
|
|
93
|
+
console.error('⚠️ Payment Manager requires encryption keys for security!');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const server = new McpServer({
|
|
98
|
+
name: "mcp-payment-manager",
|
|
99
|
+
version: "1.0.0",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Register all tools
|
|
103
|
+
for (const tool of tools) {
|
|
104
|
+
server.tool(
|
|
105
|
+
tool.name,
|
|
106
|
+
tool.description,
|
|
107
|
+
tool.parameters.shape,
|
|
108
|
+
async (args: Record<string, unknown>) => {
|
|
109
|
+
try {
|
|
110
|
+
const result = await tool.execute(args as any);
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
113
|
+
};
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: "text" as const,
|
|
119
|
+
text: JSON.stringify({
|
|
120
|
+
success: false,
|
|
121
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
122
|
+
}),
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
isError: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Connect to stdio transport
|
|
133
|
+
const transport = new StdioServerTransport();
|
|
134
|
+
await server.connect(transport);
|
|
135
|
+
|
|
136
|
+
console.error("🔒 Payment Manager MCP Server started - All data encrypted at rest");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
main().catch(console.error);
|