z-zero-mcp-server 1.0.7 → 1.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,228 @@
1
+ "use strict";
2
+ // Z-ZERO WDK Backend — Non-Custodial Module
3
+ // Drop-in replacement for api_backend.ts when Z_ZERO_WALLET_MODE=wdk
4
+ // Exports IDENTICAL function signatures so index.ts can swap backends without any changes.
5
+ //
6
+ // Key differences from custodial api_backend.ts:
7
+ // - getBalanceRemote() → reads USDT balance on-chain (not from Supabase wallets table)
8
+ // - getDepositAddressesRemote() → returns WDK wallet address (not HD custodial address)
9
+ // - issueTokenRemote() → sends USDT on-chain first, THEN issues JIT card
10
+ // - cancelTokenRemote() → triggers on-chain USDT refund back to user WDK wallet
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.listCardsRemote = listCardsRemote;
13
+ exports.getBalanceRemote = getBalanceRemote;
14
+ exports.getDepositAddressesRemote = getDepositAddressesRemote;
15
+ exports.issueTokenRemote = issueTokenRemote;
16
+ exports.resolveTokenRemote = resolveTokenRemote;
17
+ exports.burnTokenRemote = burnTokenRemote;
18
+ exports.cancelTokenRemote = cancelTokenRemote;
19
+ exports.refundUnderspendRemote = refundUnderspendRemote;
20
+ const key_store_js_1 = require("./lib/key-store.js");
21
+ const API_BASE_URL = process.env.Z_ZERO_API_BASE_URL || "https://www.clawcard.store";
22
+ const INTERNAL_SECRET = process.env.Z_ZERO_INTERNAL_SECRET || "";
23
+ if (!(0, key_store_js_1.hasPassportKey)()) {
24
+ console.error("❌ ERROR: Z_ZERO_API_KEY (Passport Key) is missing!");
25
+ console.error("🔐 Get your key: https://www.clawcard.store/dashboard/agents");
26
+ console.error("🛠️ Or call the set_api_key MCP tool to set it without restarting.");
27
+ }
28
+ // ──────────────────────────────────────────────────────────────────────────────
29
+ // HTTP helpers (same as api_backend.ts to call Dashboard API)
30
+ // ──────────────────────────────────────────────────────────────────────────────
31
+ async function apiRequest(endpoint, method = 'GET', body = null) {
32
+ const PASSPORT_KEY = (0, key_store_js_1.getPassportKey)(); // ✅ Hot-swap: read key dynamically each request
33
+ if (!PASSPORT_KEY) {
34
+ return { error: "AUTH_REQUIRED", message: "Z_ZERO_API_KEY is missing." };
35
+ }
36
+ const url = `${API_BASE_URL.replace(/\/$/, '')}${endpoint}`;
37
+ try {
38
+ const res = await fetch(url, {
39
+ method,
40
+ headers: {
41
+ "Authorization": `Bearer ${PASSPORT_KEY}`,
42
+ "Content-Type": "application/json",
43
+ },
44
+ body: body ? JSON.stringify(body) : null,
45
+ });
46
+ if (!res.ok) {
47
+ const err = await res.json().catch(() => ({ error: res.statusText }));
48
+ return { error: "API_ERROR", message: err.error || res.statusText };
49
+ }
50
+ return await res.json();
51
+ }
52
+ catch (err) {
53
+ return { error: "NETWORK_ERROR", message: err.message };
54
+ }
55
+ }
56
+ async function internalApiRequest(endpoint, method, body) {
57
+ if (!INTERNAL_SECRET) {
58
+ return { error: "CONFIG_ERROR", message: "INTERNAL_SECRET not configured" };
59
+ }
60
+ const url = `${API_BASE_URL.replace(/\/$/, '')}${endpoint}`;
61
+ try {
62
+ const res = await fetch(url, {
63
+ method,
64
+ headers: {
65
+ "x-internal-secret": INTERNAL_SECRET,
66
+ "Content-Type": "application/json",
67
+ },
68
+ body: body ? JSON.stringify(body) : null,
69
+ });
70
+ if (!res.ok) {
71
+ const err = await res.json().catch(() => ({ error: res.statusText }));
72
+ return { error: "API_ERROR", message: err.error || res.statusText };
73
+ }
74
+ return await res.json();
75
+ }
76
+ catch (err) {
77
+ return { error: "NETWORK_ERROR", message: err.message };
78
+ }
79
+ }
80
+ // ──────────────────────────────────────────────────────────────────────────────
81
+ // Core API: Same as custodial (cards, tokens still managed by Dashboard)
82
+ // ──────────────────────────────────────────────────────────────────────────────
83
+ async function listCardsRemote() {
84
+ // Calls /api/tokens/cards — but that route returns balance from Supabase.
85
+ // For WDK mode, the Dashboard API must read wallet_mode and return on-chain balance.
86
+ // This is handled server-side in the updated /api/tokens/cards route.
87
+ return await apiRequest('/api/tokens/cards', 'GET');
88
+ }
89
+ // ──────────────────────────────────────────────────────────────────────────────
90
+ // Balance: On-chain USDT via Dashboard WDK API
91
+ // ──────────────────────────────────────────────────────────────────────────────
92
+ async function getBalanceRemote(cardAlias) {
93
+ // Call Dashboard to get WDK wallet balance (Dashboard resolves user from passport key,
94
+ // finds connected WDK wallet, queries on-chain balance)
95
+ const data = await apiRequest('/api/wdk/balance', 'GET');
96
+ if (data?.error) {
97
+ // Fallback: try custodial balance if WDK not set up yet
98
+ const custodialData = await apiRequest('/api/tokens/cards', 'GET');
99
+ if (custodialData?.cards?.[0]) {
100
+ return {
101
+ wallet_balance: custodialData.cards[0].balance,
102
+ currency: 'USD',
103
+ mode: 'custodial_fallback',
104
+ note: 'WDK wallet not connected yet. Showing custodial balance.'
105
+ };
106
+ }
107
+ return data;
108
+ }
109
+ return {
110
+ wallet_balance: data.balance_usdt,
111
+ currency: 'USDT',
112
+ chain: data.chain || 'polygon',
113
+ address: data.address,
114
+ mode: 'wdk_onchain',
115
+ note: `Non-custodial WDK wallet balance (live on-chain). Address: ${data.address}`
116
+ };
117
+ }
118
+ // ──────────────────────────────────────────────────────────────────────────────
119
+ // Deposit Addresses: WDK wallet address (not custodial HD addresses)
120
+ // ──────────────────────────────────────────────────────────────────────────────
121
+ async function getDepositAddressesRemote() {
122
+ const data = await apiRequest('/api/wdk/balance', 'GET');
123
+ if (data?.error) {
124
+ // Fallback to custodial addresses
125
+ const custodial = await apiRequest('/api/tokens/cards', 'GET');
126
+ return {
127
+ ...custodial,
128
+ note: 'WDK wallet not connected. Showing custodial deposit addresses as fallback.'
129
+ };
130
+ }
131
+ return {
132
+ cards: [{ alias: 'wdk-wallet', balance: data.balance_usdt, currency: 'USDT' }],
133
+ deposit_addresses: {
134
+ polygon: data.address, // Primary: USDT on Polygon (ERC-20)
135
+ note: 'Send USDT (Polygon/ERC-20) to fund your Agent wallet. Gasless payments via ERC-4337.'
136
+ },
137
+ wdk_wallet: {
138
+ address: data.address,
139
+ chain: data.chain || 'polygon',
140
+ balance_usdt: data.balance_usdt
141
+ }
142
+ };
143
+ }
144
+ // ──────────────────────────────────────────────────────────────────────────────
145
+ // Issue Token: On-chain USDT payment → JIT card
146
+ // ──────────────────────────────────────────────────────────────────────────────
147
+ async function issueTokenRemote(cardAlias, amount, merchant) {
148
+ // WDK Flow (reversed from custodial):
149
+ // 1. Create Airwallex card first (reservation)
150
+ // 2. Send USDT on-chain from WDK wallet → system wallet
151
+ // 3. Dashboard verifies on-chain tx, activates token
152
+ // This is safe: if on-chain tx fails, Dashboard auto-cancels the card reservation.
153
+ const data = await apiRequest('/api/tokens/issue', 'POST', {
154
+ card_alias: cardAlias,
155
+ amount,
156
+ merchant,
157
+ device_fingerprint: `mcp-wdk-${process.platform}-${process.arch}`,
158
+ network_id: process.env.NETWORK_ID || "polygon-wdk",
159
+ session_id: `wdk-${Math.random().toString(36).substring(7)}`,
160
+ wallet_mode: 'wdk', // Signals Dashboard to use on-chain payment path
161
+ });
162
+ if (!data)
163
+ return null;
164
+ if (data.error)
165
+ return data;
166
+ return {
167
+ token: data.token,
168
+ card_alias: cardAlias,
169
+ amount,
170
+ merchant,
171
+ created_at: Date.now(),
172
+ ttl_seconds: 1800,
173
+ used: false,
174
+ tx_hash: data.tx_hash, // On-chain tx hash (for transparency)
175
+ mode: 'wdk_noncustodial'
176
+ };
177
+ }
178
+ // ──────────────────────────────────────────────────────────────────────────────
179
+ // Resolve Token: Same as custodial (returns PAN/CVV for Playwright injection)
180
+ // ──────────────────────────────────────────────────────────────────────────────
181
+ async function resolveTokenRemote(token) {
182
+ const data = await internalApiRequest('/api/tokens/resolve', 'POST', { token });
183
+ if (!data || data.error)
184
+ return data;
185
+ return {
186
+ number: data.number,
187
+ exp_month: data.exp?.split('/')[0] || "12",
188
+ exp_year: "20" + (data.exp?.split('/')[1] || "30"),
189
+ cvv: data.cvv || "123",
190
+ name: data.name || "Z-ZERO AI AGENT",
191
+ authorized_amount: data.authorized_amount ? Number(data.authorized_amount) : undefined,
192
+ };
193
+ }
194
+ // ──────────────────────────────────────────────────────────────────────────────
195
+ // Burn Token: Mark used + on-chain refund of underspend (if any)
196
+ // ──────────────────────────────────────────────────────────────────────────────
197
+ async function burnTokenRemote(token, receipt_id) {
198
+ const data = await internalApiRequest('/api/tokens/burn', 'POST', {
199
+ token,
200
+ receipt_id,
201
+ success: true,
202
+ wallet_mode: 'wdk', // Signals Dashboard to refund underspend on-chain
203
+ });
204
+ return !!data && !data.error;
205
+ }
206
+ // ──────────────────────────────────────────────────────────────────────────────
207
+ // Cancel Token: Refund USDT back to user's WDK wallet on-chain
208
+ // ──────────────────────────────────────────────────────────────────────────────
209
+ async function cancelTokenRemote(token) {
210
+ const data = await apiRequest('/api/tokens/cancel', 'POST', {
211
+ token,
212
+ wallet_mode: 'wdk', // Triggers on-chain USDT refund
213
+ });
214
+ if (data?.error)
215
+ return data;
216
+ return {
217
+ success: !!data,
218
+ refunded_amount: data?.refunded_amount || 0,
219
+ tx_hash: data?.tx_hash || null, // On-chain refund tx hash
220
+ note: 'USDT refunded on-chain to your WDK wallet.'
221
+ };
222
+ }
223
+ // ──────────────────────────────────────────────────────────────────────────────
224
+ // Refund Underspend: Logged only (full refund logic handled in burnTokenRemote)
225
+ // ──────────────────────────────────────────────────────────────────────────────
226
+ async function refundUnderspendRemote(token, actualSpent) {
227
+ console.log(`[WDK MCP] Token ${token} burned. Actual spent: $${actualSpent}. On-chain refund handled by Dashboard.`);
228
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "z-zero-mcp-server",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "description": "Z-ZERO MCP Server — Secure JIT Virtual Card Payment tools for AI Agents (Claude, Cursor, AutoGPT) — Real Single-Use Visa Cards",
5
5
  "main": "dist/index.js",
6
6
  "bin": "dist/index.js",
@@ -38,4 +38,4 @@
38
38
  "tsx": "^4.19.4",
39
39
  "typescript": "^5.8.3"
40
40
  }
41
- }
41
+ }