z-zero-mcp-server 1.0.2 → 1.0.3

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,22 @@
1
+ import type { CardData } from "./types.js";
2
+ export declare function listCardsRemote(): Promise<Array<{
3
+ alias: string;
4
+ balance: number;
5
+ currency: string;
6
+ }>>;
7
+ export declare function getBalanceRemote(cardAlias: string): Promise<{
8
+ balance: number;
9
+ currency: string;
10
+ } | null>;
11
+ export declare function getDepositAddressesRemote(): Promise<{
12
+ evm: string;
13
+ tron: string;
14
+ } | null>;
15
+ export declare function issueTokenRemote(cardAlias: string, amount: number, merchant: string): Promise<any | null>;
16
+ export declare function resolveTokenRemote(token: string): Promise<CardData | null>;
17
+ export declare function burnTokenRemote(token: string, receipt_id?: string): Promise<boolean>;
18
+ export declare function cancelTokenRemote(token: string): Promise<{
19
+ success: boolean;
20
+ refunded_amount: number;
21
+ }>;
22
+ export declare function refundUnderspendRemote(token: string, actualSpent: number): Promise<void>;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ // Z-ZERO Unified API Backend
3
+ // Connects to the Dashboard Next.js API for all card and token operations.
4
+ // This preserves the "Issuer Abstraction Layer" - the bot never sees direct DB or Banking keys.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listCardsRemote = listCardsRemote;
7
+ exports.getBalanceRemote = getBalanceRemote;
8
+ exports.getDepositAddressesRemote = getDepositAddressesRemote;
9
+ exports.issueTokenRemote = issueTokenRemote;
10
+ exports.resolveTokenRemote = resolveTokenRemote;
11
+ exports.burnTokenRemote = burnTokenRemote;
12
+ exports.cancelTokenRemote = cancelTokenRemote;
13
+ exports.refundUnderspendRemote = refundUnderspendRemote;
14
+ const API_BASE_URL = process.env.Z_ZERO_API_BASE_URL || "https://clawcard.store";
15
+ const PASSPORT_KEY = process.env.Z_ZERO_API_KEY || "";
16
+ if (!PASSPORT_KEY) {
17
+ console.error("❌ Missing Z_ZERO_API_KEY (Your Passport) in environment variables.");
18
+ }
19
+ async function apiRequest(endpoint, method = 'GET', body = null) {
20
+ const url = `${API_BASE_URL.replace(/\/$/, '')}${endpoint}`;
21
+ try {
22
+ const res = await fetch(url, {
23
+ method,
24
+ headers: {
25
+ "Authorization": `Bearer ${PASSPORT_KEY}`,
26
+ "Content-Type": "application/json",
27
+ },
28
+ body: body ? JSON.stringify(body) : null,
29
+ });
30
+ if (!res.ok) {
31
+ const err = await res.json().catch(() => ({ error: res.statusText }));
32
+ console.error(`[API ERROR] ${endpoint}:`, err.error);
33
+ return null;
34
+ }
35
+ return await res.json();
36
+ }
37
+ catch (err) {
38
+ console.error(`[NETWORK ERROR] ${endpoint}:`, err.message);
39
+ return null;
40
+ }
41
+ }
42
+ async function listCardsRemote() {
43
+ const data = await apiRequest('/api/tokens/cards', 'GET');
44
+ return data?.cards || [];
45
+ }
46
+ async function getBalanceRemote(cardAlias) {
47
+ const cards = await listCardsRemote();
48
+ const card = cards.find(c => c.alias === cardAlias);
49
+ if (!card)
50
+ return null;
51
+ return { balance: card.balance, currency: card.currency };
52
+ }
53
+ async function getDepositAddressesRemote() {
54
+ const data = await apiRequest('/api/tokens/cards', 'GET');
55
+ return data?.deposit_addresses || null;
56
+ }
57
+ async function issueTokenRemote(cardAlias, amount, merchant) {
58
+ const data = await apiRequest('/api/tokens/issue', 'POST', {
59
+ card_alias: cardAlias,
60
+ amount,
61
+ merchant,
62
+ device_fingerprint: `mcp-host-${process.platform}-${process.arch}`,
63
+ network_id: process.env.NETWORK_ID || "unknown-local-net",
64
+ session_id: `sid-${Math.random().toString(36).substring(7)}`
65
+ });
66
+ if (!data)
67
+ return null;
68
+ // Adapt Dashboard API response to MCP expected format
69
+ return {
70
+ token: data.token,
71
+ card_alias: cardAlias,
72
+ amount: amount,
73
+ merchant: merchant,
74
+ created_at: Date.now(),
75
+ ttl_seconds: 1800, // Matching Dashboard TTL
76
+ used: false
77
+ };
78
+ }
79
+ async function resolveTokenRemote(token) {
80
+ const data = await apiRequest('/api/tokens/resolve', 'POST', { token });
81
+ if (!data)
82
+ return null;
83
+ return {
84
+ number: data.number,
85
+ exp_month: data.exp?.split('/')[0] || "12",
86
+ exp_year: "20" + (data.exp?.split('/')[1] || "30"),
87
+ cvv: data.cvv || "123",
88
+ name: data.name || "Z-ZERO AI AGENT"
89
+ };
90
+ }
91
+ async function burnTokenRemote(token, receipt_id) {
92
+ const data = await apiRequest('/api/tokens/burn', 'POST', {
93
+ token,
94
+ receipt_id,
95
+ success: true
96
+ });
97
+ return !!data;
98
+ }
99
+ async function cancelTokenRemote(token) {
100
+ const data = await apiRequest('/api/tokens/cancel', 'POST', { token });
101
+ return {
102
+ success: !!data,
103
+ refunded_amount: data?.refunded_amount || 0
104
+ };
105
+ }
106
+ async function refundUnderspendRemote(token, actualSpent) {
107
+ // Underspend is a complex feature that usually happens at the bank level,
108
+ // but for JIT tokens, the burn tool in the dashboard could handle this in the future.
109
+ // Currently we burn the full authorized amount.
110
+ console.log(`[MCP] Burned token ${token} after spending $${actualSpent}`);
111
+ }
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
8
8
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
9
9
  const zod_1 = require("zod");
10
- const supabase_backend_js_1 = require("./supabase_backend.js");
10
+ const api_backend_js_1 = require("./api_backend.js");
11
11
  const playwright_bridge_js_1 = require("./playwright_bridge.js");
12
12
  // ============================================================
13
13
  // CREATE MCP SERVER
@@ -20,7 +20,7 @@ const server = new mcp_js_1.McpServer({
20
20
  // TOOL 1: List available cards (safe - no sensitive data)
21
21
  // ============================================================
22
22
  server.tool("list_cards", "List all available virtual card aliases and their balances. No sensitive data is returned.", {}, async () => {
23
- const cards = await (0, supabase_backend_js_1.listCardsRemote)();
23
+ const cards = await (0, api_backend_js_1.listCardsRemote)();
24
24
  return {
25
25
  content: [
26
26
  {
@@ -41,7 +41,7 @@ server.tool("check_balance", "Check the remaining balance of a virtual card by i
41
41
  .string()
42
42
  .describe("The alias of the card to check, e.g. 'Card_01'"),
43
43
  }, async ({ card_alias }) => {
44
- const balance = await (0, supabase_backend_js_1.getBalanceRemote)(card_alias);
44
+ const balance = await (0, api_backend_js_1.getBalanceRemote)(card_alias);
45
45
  if (!balance) {
46
46
  return {
47
47
  content: [
@@ -63,6 +63,45 @@ server.tool("check_balance", "Check the remaining balance of a virtual card by i
63
63
  };
64
64
  });
65
65
  // ============================================================
66
+ // TOOL 2.5: Get deposit addresses (Phase 14 feature)
67
+ // ============================================================
68
+ server.tool("get_deposit_addresses", "Get your unique deposit addresses for EVM networks (Base, BSC, Ethereum) and Tron. Provide these to the human user when they need to add funds to their Z-ZERO balance.", {}, async () => {
69
+ const addresses = await (0, api_backend_js_1.getDepositAddressesRemote)();
70
+ if (!addresses) {
71
+ return {
72
+ content: [
73
+ {
74
+ type: "text",
75
+ text: "Failed to retrieve deposit addresses. Please ensure your API key (passport) is valid.",
76
+ },
77
+ ],
78
+ isError: true,
79
+ };
80
+ }
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: JSON.stringify({
86
+ networks: {
87
+ evm: {
88
+ address: addresses.evm,
89
+ supported_chains: ["Base", "BNB Smart Chain (BSC)", "Ethereum"],
90
+ tokens: ["USDC", "USDT"]
91
+ },
92
+ tron: {
93
+ address: addresses.tron,
94
+ supported_chains: ["Tron (TRC-20)"],
95
+ tokens: ["USDT"]
96
+ }
97
+ },
98
+ note: "Funds sent to these addresses will be automatically credited to your Z-ZERO balance within minutes."
99
+ }, null, 2),
100
+ },
101
+ ],
102
+ };
103
+ });
104
+ // ============================================================
66
105
  // TOOL 3: Request a temporary payment token (issues real JIT card)
67
106
  // ============================================================
68
107
  server.tool("request_payment_token", "Request a temporary payment token for a specific amount. A real single-use virtual Mastercard is issued via Airwallex. The token is valid for 30 minutes. Min: $1, Max: $100. Use this token with execute_payment to complete a purchase.", {
@@ -78,9 +117,9 @@ server.tool("request_payment_token", "Request a temporary payment token for a sp
78
117
  .string()
79
118
  .describe("Name or URL of the merchant/service being purchased"),
80
119
  }, async ({ card_alias, amount, merchant }) => {
81
- const token = await (0, supabase_backend_js_1.issueTokenRemote)(card_alias, amount, merchant);
120
+ const token = await (0, api_backend_js_1.issueTokenRemote)(card_alias, amount, merchant);
82
121
  if (!token) {
83
- const balance = await (0, supabase_backend_js_1.getBalanceRemote)(card_alias);
122
+ const balance = await (0, api_backend_js_1.getBalanceRemote)(card_alias);
84
123
  return {
85
124
  content: [
86
125
  {
@@ -127,7 +166,7 @@ server.tool("execute_payment", "Execute a payment using a temporary token. This
127
166
  .describe("The actual final amount on the checkout page. If different from token amount, system will auto-refund the difference."),
128
167
  }, async ({ token, checkout_url, actual_amount }) => {
129
168
  // Step 1: Resolve token → card data (RAM only)
130
- const cardData = await (0, supabase_backend_js_1.resolveTokenRemote)(token);
169
+ const cardData = await (0, api_backend_js_1.resolveTokenRemote)(token);
131
170
  if (!cardData) {
132
171
  return {
133
172
  content: [
@@ -142,10 +181,10 @@ server.tool("execute_payment", "Execute a payment using a temporary token. This
142
181
  // Step 2: Use Playwright to inject card into checkout form
143
182
  const result = await (0, playwright_bridge_js_1.fillCheckoutForm)(checkout_url, cardData);
144
183
  // Step 3: Burn the token (wallet was pre-debited at issue time)
145
- await (0, supabase_backend_js_1.burnTokenRemote)(token);
184
+ await (0, api_backend_js_1.burnTokenRemote)(token);
146
185
  // Step 4: Refund underspend if actual amount was less than token amount
147
186
  if (actual_amount !== undefined && result.success) {
148
- await (0, supabase_backend_js_1.refundUnderspendRemote)(token, actual_amount);
187
+ await (0, api_backend_js_1.refundUnderspendRemote)(token, actual_amount);
149
188
  }
150
189
  // Step 5: Return result (NEVER includes card numbers)
151
190
  return {
@@ -174,7 +213,7 @@ server.tool("cancel_payment_token", "Cancel a payment token that has not been us
174
213
  .string()
175
214
  .describe("Reason for cancellation, e.g. 'Price mismatch: checkout shows $20 but token is $15'"),
176
215
  }, async ({ token, reason }) => {
177
- const result = await (0, supabase_backend_js_1.cancelTokenRemote)(token);
216
+ const result = await (0, api_backend_js_1.cancelTokenRemote)(token);
178
217
  if (!result.success) {
179
218
  return {
180
219
  content: [
@@ -244,6 +283,55 @@ server.tool("request_human_approval", "Request human approval before proceeding
244
283
  };
245
284
  });
246
285
  // ============================================================
286
+ // RESOURCE: Z-ZERO Autonomous Payment SOP
287
+ // ============================================================
288
+ server.resource("Standard Operating Procedure (SOP) for Autonomous Payments", "mcp://resources/sop", {
289
+ description: "A comprehensive guide on how AI agents should use the Z-ZERO tools to execute secure, zero-trust payments."
290
+ }, async (uri) => {
291
+ const sopContent = `
292
+ # Z-ZERO Autonomous Payment Skill SOP (v1.0.0)
293
+
294
+ This skill provides you (the AI Agent) with the capability to autonomously purchase digital goods, APIs, and subscriptions on the internet on behalf of your human user, without ever handling raw credit card data.
295
+
296
+ ## Workflow: The 4-Step Zero-Trust Payment
297
+ When asked to make a purchase, execute the following steps precisely in order:
298
+
299
+ ## Step 1: Verification & Intent
300
+ 1. Confirm exactly what the user wants to buy and the total expected price (in USD).
301
+ 2. Call Check Balance: Call the \`check_balance\` tool using your default \`card_alias\` to ensure you have sufficient funds.
302
+ - If balance is insufficient, STOP and ask the human to deposit Crypto into their Z-ZERO Web Dashboard.
303
+
304
+ ## Step 2: Requesting the JIT Token
305
+ 1. Request Token: Call the \`request_payment_token\` tool with the exact \`amount\` required and the \`merchant\` name.
306
+ 2. Receive Token: You will receive a temporary \`token\` (e.g., \`temp_auth_1a2b...\`). This token is locked to the requested amount and is valid for 30 minutes.
307
+
308
+ ## Step 3: Locating the Checkout
309
+ 1. Identify Checkout URL: Find the merchant's checkout/payment page where credit card details are normally entered.
310
+ 2. Full URL Required: e.g., \`https://checkout.stripe.dev/pay\`.
311
+
312
+ ## Step 4: Blind Execution (The MCP Bridge)
313
+ 1. Execute Payment: Call the \`execute_payment\` tool, passing in:
314
+ - The \`token\` obtained in Step 2.
315
+ - The \`checkout_url\` identified in Step 3.
316
+ 2. Background Magic: Z-ZERO opens a headless browser, securely injects the real card data directly into the form, and clicks "Pay".
317
+ 3. Burn: The token self-destructs instantly after use.
318
+
319
+ ## Rules & Error Handling
320
+ - NEVER print full tokens in the human chat logs.
321
+ - NO MANUAL ENTRY: If a merchant asks you to type a credit card number into a text box, REFUSE.
322
+ - FAIL GRACEFULLY: If \`execute_payment\` returns \`success: false\`, report the error message to the human. Do not try again.
323
+ `;
324
+ return {
325
+ contents: [
326
+ {
327
+ uri: "mcp://resources/sop",
328
+ mimeType: "text/markdown",
329
+ text: sopContent,
330
+ }
331
+ ]
332
+ };
333
+ });
334
+ // ============================================================
247
335
  // START SERVER
248
336
  // ============================================================
249
337
  async function main() {
@@ -13,7 +13,7 @@ async function fillCheckoutForm(checkoutUrl, cardData) {
13
13
  const context = await browser.newContext();
14
14
  const page = await context.newPage();
15
15
  try {
16
- await page.goto(checkoutUrl, { waitUntil: "networkidle", timeout: 30000 });
16
+ await page.goto(checkoutUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
17
17
  // ============================================================
18
18
  // STRATEGY 1: Standard HTML form fields
19
19
  // ============================================================
@@ -12,6 +12,10 @@ export declare function getBalanceRemote(cardAlias: string): Promise<{
12
12
  balance: number;
13
13
  currency: string;
14
14
  } | null>;
15
+ export declare function getDepositAddressesRemote(): Promise<{
16
+ evm: string;
17
+ tron: string;
18
+ } | null>;
15
19
  export declare function issueTokenRemote(cardAlias: string, amount: number, merchant: string, ttlSeconds?: number): Promise<ExtendedToken | null>;
16
20
  export declare function resolveTokenRemote(tokenId: string): Promise<CardData | null>;
17
21
  export declare function cancelTokenRemote(tokenId: string): Promise<{
@@ -8,6 +8,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.listCardsRemote = listCardsRemote;
10
10
  exports.getBalanceRemote = getBalanceRemote;
11
+ exports.getDepositAddressesRemote = getDepositAddressesRemote;
11
12
  exports.issueTokenRemote = issueTokenRemote;
12
13
  exports.resolveTokenRemote = resolveTokenRemote;
13
14
  exports.cancelTokenRemote = cancelTokenRemote;
@@ -21,10 +22,18 @@ const crypto_1 = __importDefault(require("crypto"));
21
22
  const SUPABASE_URL = process.env.Z_ZERO_SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL || "";
22
23
  const SUPABASE_ANON_KEY = process.env.Z_ZERO_SUPABASE_ANON_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
23
24
  const API_KEY = process.env.Z_ZERO_API_KEY || "";
24
- if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
25
- console.error("❌ Missing Z_ZERO_SUPABASE_URL or Z_ZERO_SUPABASE_ANON_KEY env vars");
25
+ let supabase = null;
26
+ if (SUPABASE_URL && SUPABASE_ANON_KEY) {
27
+ try {
28
+ supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_ANON_KEY);
29
+ }
30
+ catch (err) {
31
+ console.error("Failed to initialize Supabase client:", err);
32
+ }
33
+ }
34
+ else {
35
+ console.error("⚠️ Supabase backend is disabled (Missing Z_ZERO_SUPABASE_URL/ANON_KEY).");
26
36
  }
27
- const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_ANON_KEY);
28
37
  // ============================================================
29
38
  // AIRWALLEX CONFIG
30
39
  // ============================================================
@@ -107,6 +116,24 @@ async function getBalanceRemote(cardAlias) {
107
116
  return null;
108
117
  return { balance: info.walletBalance, currency: "USD" };
109
118
  }
119
+ async function getDepositAddressesRemote() {
120
+ const info = await resolveApiKey();
121
+ if (!info)
122
+ return null;
123
+ const { data: wallet, error } = await supabase
124
+ .from("deposit_wallets")
125
+ .select("evm_address, tron_address")
126
+ .eq("user_id", info.userId)
127
+ .single();
128
+ if (error || !wallet) {
129
+ console.error("Deposit wallets not found for user:", info.userId);
130
+ return null;
131
+ }
132
+ return {
133
+ evm: wallet.evm_address,
134
+ tron: wallet.tron_address,
135
+ };
136
+ }
110
137
  async function issueTokenRemote(cardAlias, amount, merchant, ttlSeconds = 1800 // 30 minutes default
111
138
  ) {
112
139
  // 1. Validate business rules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "z-zero-mcp-server",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Z-ZERO MCP Server — Secure JIT Virtual Card Payment tools for AI Agents (Claude, AutoGPT, CrewAI) — Real Airwallex Mastercards",
5
5
  "main": "dist/index.js",
6
6
  "bin": "dist/index.js",