@mixrpay/agent-sdk 0.3.2 → 0.3.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.
- package/README.md +26 -450
- package/dist/index.cjs +221 -0
- package/dist/index.d.cts +123 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.js +221 -0
- package/package.json +2 -14
package/README.md
CHANGED
|
@@ -1,495 +1,71 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mixrpay/agent-sdk
|
|
2
2
|
|
|
3
|
-
Enable AI agents to make
|
|
3
|
+
Enable AI agents to make payments.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
|
-
|
|
8
|
-
## 🚀 Quick Start
|
|
9
|
-
|
|
10
|
-
```typescript
|
|
11
|
-
import { AgentWallet } from '@mixrpay/agent-sdk';
|
|
12
|
-
|
|
13
|
-
const wallet = new AgentWallet({
|
|
14
|
-
sessionKey: 'sk_live_...' // Get from wallet owner
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// For MixrPay merchants (recommended):
|
|
18
|
-
const response = await wallet.callMerchantApi({
|
|
19
|
-
url: 'https://api.merchant.com/generate',
|
|
20
|
-
merchantPublicKey: 'pk_live_abc123',
|
|
21
|
-
priceUsd: 0.05,
|
|
22
|
-
body: { prompt: 'Hello world' }
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// For external x402 APIs:
|
|
26
|
-
const response = await wallet.fetch('https://external-api.com/query');
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## 📦 Installation
|
|
5
|
+
## Installation
|
|
30
6
|
|
|
31
7
|
```bash
|
|
32
8
|
npm install @mixrpay/agent-sdk
|
|
33
|
-
# or
|
|
34
|
-
yarn add @mixrpay/agent-sdk
|
|
35
|
-
# or
|
|
36
|
-
pnpm add @mixrpay/agent-sdk
|
|
37
9
|
```
|
|
38
10
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
The Agent SDK supports **two payment patterns** depending on what API you're calling:
|
|
11
|
+
## Setup
|
|
42
12
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
const response = await wallet.callMerchantApi({
|
|
49
|
-
url: 'https://api.merchant.com/generate',
|
|
50
|
-
merchantPublicKey: 'pk_live_abc123', // Merchant's MixrPay public key
|
|
51
|
-
priceUsd: 0.05,
|
|
52
|
-
method: 'POST',
|
|
53
|
-
body: { prompt: 'Hello world' },
|
|
54
|
-
});
|
|
13
|
+
```bash
|
|
14
|
+
# .env
|
|
15
|
+
MIXRPAY_SESSION_KEY=sk_live_...
|
|
55
16
|
```
|
|
56
17
|
|
|
57
|
-
|
|
58
|
-
1. SDK creates/reuses a session authorization with the merchant
|
|
59
|
-
2. SDK charges against the session
|
|
60
|
-
3. Request is sent with `X-Mixr-Session` header
|
|
61
|
-
4. No 402 round-trip needed—faster and cheaper
|
|
62
|
-
|
|
63
|
-
**When to use:** APIs built with MixrPay, or any API that accepts `X-Mixr-Session` headers.
|
|
18
|
+
Get session keys from [mixrpay.com/wallet/sessions](https://www.mixrpay.com/wallet/sessions).
|
|
64
19
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Use `fetch()` when calling APIs that implement x402 but aren't MixrPay merchants:
|
|
20
|
+
## Usage
|
|
68
21
|
|
|
69
22
|
```typescript
|
|
70
|
-
|
|
71
|
-
method: 'POST',
|
|
72
|
-
body: JSON.stringify({ prompt: 'Hello' })
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**How it works:**
|
|
77
|
-
1. SDK makes initial request
|
|
78
|
-
2. API returns `402 Payment Required`
|
|
79
|
-
3. SDK signs payment authorization
|
|
80
|
-
4. SDK retries with `X-PAYMENT` header
|
|
81
|
-
|
|
82
|
-
**When to use:** External APIs that use standard x402 protocol.
|
|
83
|
-
|
|
84
|
-
## 🔑 Getting Session Keys
|
|
85
|
-
|
|
86
|
-
Session keys are created by wallet owners and grant spending permissions to agents:
|
|
87
|
-
|
|
88
|
-
1. **From a wallet owner**: They create a session key at `/wallet/sessions`
|
|
89
|
-
2. **Programmatically**: Via the MixrPay API
|
|
90
|
-
|
|
91
|
-
Session keys look like: `sk_live_abc123...` (mainnet) or `sk_test_abc123...` (testnet)
|
|
92
|
-
|
|
93
|
-
### Session Key Limits
|
|
94
|
-
|
|
95
|
-
Each session key has configurable spending limits:
|
|
96
|
-
- **Per-transaction**: Maximum amount per single request
|
|
97
|
-
- **Daily**: Maximum total per 24 hours
|
|
98
|
-
- **Total**: Maximum lifetime spend
|
|
99
|
-
- **Expiration**: When the key becomes invalid
|
|
100
|
-
|
|
101
|
-
## 📖 Session Management
|
|
102
|
-
|
|
103
|
-
### Automatic (Recommended)
|
|
23
|
+
import { AgentWallet } from '@mixrpay/agent-sdk';
|
|
104
24
|
|
|
105
|
-
|
|
25
|
+
const wallet = new AgentWallet({
|
|
26
|
+
sessionKey: process.env.MIXRPAY_SESSION_KEY!
|
|
27
|
+
});
|
|
106
28
|
|
|
107
|
-
```typescript
|
|
108
|
-
// Sessions are created and reused automatically
|
|
109
29
|
const response = await wallet.callMerchantApi({
|
|
110
30
|
url: 'https://api.merchant.com/generate',
|
|
111
|
-
merchantPublicKey: '
|
|
31
|
+
merchantPublicKey: 'pk_live_...',
|
|
112
32
|
priceUsd: 0.05,
|
|
113
|
-
body: { prompt: 'Hello' }
|
|
114
|
-
});
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Manual Control
|
|
118
|
-
|
|
119
|
-
For fine-grained control over sessions:
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
// Get or create a session
|
|
123
|
-
const session = await wallet.getOrCreateSession({
|
|
124
|
-
merchantPublicKey: 'pk_live_abc123',
|
|
125
|
-
spendingLimitUsd: 25, // Max total spending
|
|
126
|
-
durationDays: 7, // Session validity
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
console.log('Session ID:', session.id);
|
|
130
|
-
console.log('Remaining:', session.remainingLimitUsd);
|
|
131
|
-
console.log('Expires:', session.expiresAt);
|
|
132
|
-
|
|
133
|
-
// Charge manually
|
|
134
|
-
const charge = await wallet.chargeSession(session.id, 0.05, {
|
|
135
|
-
feature: 'generate',
|
|
136
|
-
idempotencyKey: `charge-${Date.now()}`,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Then make your API call with the session
|
|
140
|
-
const response = await fetch('https://api.merchant.com/generate', {
|
|
141
|
-
method: 'POST',
|
|
142
|
-
headers: {
|
|
143
|
-
'Content-Type': 'application/json',
|
|
144
|
-
'X-Mixr-Session': session.id,
|
|
145
|
-
'X-Mixr-Charged': 'true', // Tell merchant payment is done
|
|
146
|
-
},
|
|
147
|
-
body: JSON.stringify({ prompt: 'Hello' }),
|
|
148
|
-
});
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### List and Revoke Sessions
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
// List all active sessions
|
|
155
|
-
const sessions = await wallet.listSessions();
|
|
156
|
-
for (const session of sessions) {
|
|
157
|
-
console.log(`${session.merchantName}: $${session.remainingLimitUsd} remaining`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Revoke a session (e.g., when done with a merchant)
|
|
161
|
-
await wallet.revokeSession(session.id);
|
|
162
|
-
|
|
163
|
-
// Get session statistics
|
|
164
|
-
const stats = await wallet.getSessionStats();
|
|
165
|
-
console.log(`Active sessions: ${stats.activeCount}`);
|
|
166
|
-
console.log(`Total authorized: $${stats.totalAuthorizedUsd}`);
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## 💡 Usage Examples
|
|
170
|
-
|
|
171
|
-
### With Payment Tracking
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
import { AgentWallet, PaymentEvent } from '@mixrpay/agent-sdk';
|
|
175
|
-
|
|
176
|
-
const wallet = new AgentWallet({
|
|
177
|
-
sessionKey: 'sk_live_...',
|
|
178
|
-
onPayment: (payment: PaymentEvent) => {
|
|
179
|
-
console.log(`💸 Paid $${payment.amountUsd.toFixed(4)} to ${payment.recipient}`);
|
|
180
|
-
},
|
|
33
|
+
body: { prompt: 'Hello world' }
|
|
181
34
|
});
|
|
182
35
|
|
|
183
|
-
|
|
184
|
-
const response = await wallet.callMerchantApi({ ... });
|
|
185
|
-
|
|
186
|
-
// Check spending
|
|
187
|
-
const stats = await wallet.getSpendingStats();
|
|
188
|
-
console.log(`Total spent: $${stats.totalSpentUsd.toFixed(2)}`);
|
|
189
|
-
console.log(`Transactions: ${stats.txCount}`);
|
|
36
|
+
const data = await response.json();
|
|
190
37
|
```
|
|
191
38
|
|
|
192
|
-
|
|
39
|
+
## Error Handling
|
|
193
40
|
|
|
194
41
|
```typescript
|
|
195
|
-
import {
|
|
196
|
-
AgentWallet,
|
|
197
|
-
InsufficientBalanceError,
|
|
198
|
-
SpendingLimitExceededError,
|
|
199
|
-
SessionKeyExpiredError,
|
|
200
|
-
// Session Authorization Errors
|
|
201
|
-
SessionExpiredError,
|
|
202
|
-
SessionLimitExceededError,
|
|
203
|
-
SessionNotFoundError,
|
|
204
|
-
SessionRevokedError,
|
|
205
|
-
} from '@mixrpay/agent-sdk';
|
|
206
|
-
|
|
207
|
-
const wallet = new AgentWallet({ sessionKey: 'sk_live_...' });
|
|
42
|
+
import { AgentWallet, InsufficientBalanceError, SessionLimitExceededError } from '@mixrpay/agent-sdk';
|
|
208
43
|
|
|
209
44
|
try {
|
|
210
45
|
const response = await wallet.callMerchantApi({ ... });
|
|
211
46
|
} catch (error) {
|
|
212
47
|
if (error instanceof InsufficientBalanceError) {
|
|
213
|
-
console.log(
|
|
214
|
-
console.log(` Top up at: ${error.topUpUrl}`);
|
|
215
|
-
} else if (error instanceof SessionExpiredError) {
|
|
216
|
-
console.log(`❌ Session ${error.sessionId} expired`);
|
|
217
|
-
// SDK will auto-create on next callMerchantApi()
|
|
218
|
-
} else if (error instanceof SessionLimitExceededError) {
|
|
219
|
-
console.log(`❌ Session limit exceeded`);
|
|
220
|
-
console.log(` Limit: $${error.limit}, Requested: $${error.requested}, Remaining: $${error.remaining}`);
|
|
221
|
-
} else if (error instanceof SessionNotFoundError) {
|
|
222
|
-
console.log(`❌ Session ${error.sessionId} not found`);
|
|
223
|
-
} else if (error instanceof SessionRevokedError) {
|
|
224
|
-
console.log(`❌ Session ${error.sessionId} was revoked: ${error.reason || 'no reason given'}`);
|
|
225
|
-
} else if (error instanceof SpendingLimitExceededError) {
|
|
226
|
-
console.log(`❌ Session key limit exceeded: ${error.limitType}`);
|
|
227
|
-
} else if (error instanceof SessionKeyExpiredError) {
|
|
228
|
-
console.log(`❌ Session key expired at ${error.expiredAt}`);
|
|
229
|
-
} else {
|
|
230
|
-
throw error;
|
|
48
|
+
console.log(`Top up at: ${error.topUpUrl}`);
|
|
231
49
|
}
|
|
232
50
|
}
|
|
233
51
|
```
|
|
234
52
|
|
|
235
|
-
|
|
53
|
+
## Spending Controls
|
|
236
54
|
|
|
237
55
|
```typescript
|
|
238
56
|
const wallet = new AgentWallet({
|
|
239
57
|
sessionKey: 'sk_live_...',
|
|
240
|
-
maxPaymentUsd: 1.0,
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
// If an API tries to charge more than $1, the SDK will throw
|
|
244
|
-
// SpendingLimitExceededError with limitType: 'client_max'
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Debug Mode
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
// Enable logging during initialization
|
|
251
|
-
const wallet = new AgentWallet({
|
|
252
|
-
sessionKey: 'sk_live_...',
|
|
253
|
-
logLevel: 'debug', // 'debug' | 'info' | 'warn' | 'error' | 'none'
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Or toggle at runtime
|
|
257
|
-
wallet.setDebug(true);
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
## 🤖 AI Framework Integrations
|
|
261
|
-
|
|
262
|
-
### Vercel AI SDK
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
265
|
-
import { AgentWallet, SessionLimitExceededError } from '@mixrpay/agent-sdk';
|
|
266
|
-
import { generateText } from 'ai';
|
|
267
|
-
import { openai } from '@ai-sdk/openai';
|
|
268
|
-
import { z } from 'zod';
|
|
269
|
-
|
|
270
|
-
const wallet = new AgentWallet({
|
|
271
|
-
sessionKey: process.env.MIXRPAY_SESSION_KEY!,
|
|
272
|
-
maxPaymentUsd: 1.00,
|
|
58
|
+
maxPaymentUsd: 1.0,
|
|
59
|
+
onPayment: (payment) => console.log(`Paid $${payment.amountUsd}`),
|
|
273
60
|
});
|
|
274
61
|
|
|
275
|
-
const
|
|
276
|
-
model: openai('gpt-4'),
|
|
277
|
-
tools: {
|
|
278
|
-
paidSearch: {
|
|
279
|
-
description: 'Search using a premium paid API',
|
|
280
|
-
parameters: z.object({ query: z.string() }),
|
|
281
|
-
execute: async ({ query }) => {
|
|
282
|
-
try {
|
|
283
|
-
const response = await wallet.callMerchantApi({
|
|
284
|
-
url: 'https://api.search.com/query',
|
|
285
|
-
merchantPublicKey: 'pk_live_...',
|
|
286
|
-
priceUsd: 0.05,
|
|
287
|
-
body: { query }
|
|
288
|
-
});
|
|
289
|
-
return response.json();
|
|
290
|
-
} catch (error) {
|
|
291
|
-
if (error instanceof SessionLimitExceededError) {
|
|
292
|
-
return { error: 'Budget exceeded', limit: error.limitType };
|
|
293
|
-
}
|
|
294
|
-
throw error;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
prompt: 'Search for the latest AI news'
|
|
300
|
-
});
|
|
62
|
+
const balance = await wallet.getBalance();
|
|
301
63
|
```
|
|
302
64
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
import { AgentWallet, InsufficientBalanceError } from '@mixrpay/agent-sdk';
|
|
307
|
-
import { DynamicTool } from '@langchain/core/tools';
|
|
308
|
-
|
|
309
|
-
const wallet = new AgentWallet({
|
|
310
|
-
sessionKey: process.env.MIXRPAY_SESSION_KEY!,
|
|
311
|
-
maxPaymentUsd: 0.50,
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
const paidSearchTool = new DynamicTool({
|
|
315
|
-
name: 'paid_search',
|
|
316
|
-
description: 'Premium search API ($0.05/query)',
|
|
317
|
-
func: async (query: string) => {
|
|
318
|
-
try {
|
|
319
|
-
const response = await wallet.callMerchantApi({
|
|
320
|
-
url: 'https://api.search.com/query',
|
|
321
|
-
merchantPublicKey: 'pk_live_...',
|
|
322
|
-
priceUsd: 0.05,
|
|
323
|
-
body: { query }
|
|
324
|
-
});
|
|
325
|
-
const data = await response.json();
|
|
326
|
-
return JSON.stringify(data.results);
|
|
327
|
-
} catch (error) {
|
|
328
|
-
if (error instanceof InsufficientBalanceError) {
|
|
329
|
-
return `Error: Low balance. Top up: ${error.topUpUrl}`;
|
|
330
|
-
}
|
|
331
|
-
return `Error: ${error instanceof Error ? error.message : 'Unknown'}`;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
## 📚 API Reference
|
|
338
|
-
|
|
339
|
-
### AgentWallet
|
|
340
|
-
|
|
341
|
-
```typescript
|
|
342
|
-
new AgentWallet({
|
|
343
|
-
// Required
|
|
344
|
-
sessionKey: string; // Session key (sk_live_... or sk_test_...)
|
|
345
|
-
|
|
346
|
-
// Optional
|
|
347
|
-
walletAddress?: string; // Smart wallet address (auto-detected)
|
|
348
|
-
maxPaymentUsd?: number; // Client-side payment limit
|
|
349
|
-
onPayment?: (PaymentEvent) => void; // Payment callback
|
|
350
|
-
facilitatorUrl?: string; // x402 facilitator URL
|
|
351
|
-
baseUrl?: string; // MixrPay API base URL
|
|
352
|
-
timeout?: number; // Request timeout in ms (default: 30000)
|
|
353
|
-
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'none';
|
|
354
|
-
})
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### Methods
|
|
358
|
-
|
|
359
|
-
| Method | Description |
|
|
360
|
-
|--------|-------------|
|
|
361
|
-
| `callMerchantApi(options)` | Make request to MixrPay merchant (session-based) |
|
|
362
|
-
| `fetch(url, init?)` | Make request to x402 API (drop-in for native fetch) |
|
|
363
|
-
| `getOrCreateSession(options)` | Get or create session with merchant |
|
|
364
|
-
| `chargeSession(id, amount, options?)` | Charge against a session |
|
|
365
|
-
| `listSessions()` | List all active sessions |
|
|
366
|
-
| `revokeSession(id)` | Revoke a session |
|
|
367
|
-
| `getSessionStats()` | Get session statistics |
|
|
368
|
-
| `getBalance()` | Get USDC balance in USD |
|
|
369
|
-
| `getSpendingStats()` | Get spending statistics |
|
|
370
|
-
| `getSessionKeyInfo()` | Get session key details and limits |
|
|
371
|
-
| `getPaymentHistory()` | Get list of payments made |
|
|
372
|
-
| `getTotalSpent()` | Get total amount spent |
|
|
373
|
-
| `getWalletAddress()` | Get wallet address |
|
|
374
|
-
| `getNetwork()` | Get network info (Base or Base Sepolia) |
|
|
375
|
-
| `isTestnet()` | Check if using testnet |
|
|
376
|
-
| `runDiagnostics()` | Run health checks |
|
|
377
|
-
| `setDebug(enable)` | Toggle debug logging |
|
|
378
|
-
|
|
379
|
-
### Types
|
|
380
|
-
|
|
381
|
-
```typescript
|
|
382
|
-
interface SessionAuthorization {
|
|
383
|
-
id: string;
|
|
384
|
-
merchantId: string;
|
|
385
|
-
merchantName: string;
|
|
386
|
-
status: 'pending' | 'active' | 'expired' | 'revoked';
|
|
387
|
-
spendingLimitUsd: number;
|
|
388
|
-
amountUsedUsd: number;
|
|
389
|
-
remainingLimitUsd: number;
|
|
390
|
-
expiresAt: Date;
|
|
391
|
-
createdAt: Date;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
interface SessionStats {
|
|
395
|
-
activeCount: number;
|
|
396
|
-
expiredCount: number;
|
|
397
|
-
revokedCount: number;
|
|
398
|
-
totalAuthorizedUsd: number;
|
|
399
|
-
totalSpentUsd: number;
|
|
400
|
-
totalRemainingUsd: number;
|
|
401
|
-
activeSessions: Array<{
|
|
402
|
-
id: string;
|
|
403
|
-
merchantName: string;
|
|
404
|
-
merchantPublicKey: string;
|
|
405
|
-
spendingLimitUsd: number;
|
|
406
|
-
remainingUsd: number;
|
|
407
|
-
expiresAt: Date;
|
|
408
|
-
}>;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
interface ChargeResult {
|
|
412
|
-
success: boolean;
|
|
413
|
-
chargeId: string;
|
|
414
|
-
amountUsd: number;
|
|
415
|
-
txHash?: string;
|
|
416
|
-
remainingSessionBalanceUsd: number;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
interface PaymentEvent {
|
|
420
|
-
amountUsd: number;
|
|
421
|
-
recipient: string;
|
|
422
|
-
txHash: string | null;
|
|
423
|
-
timestamp: Date;
|
|
424
|
-
description?: string;
|
|
425
|
-
url?: string;
|
|
426
|
-
}
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### Exceptions
|
|
430
|
-
|
|
431
|
-
| Exception | When Thrown |
|
|
432
|
-
|-----------|-------------|
|
|
433
|
-
| `InsufficientBalanceError` | Wallet doesn't have enough USDC |
|
|
434
|
-
| `SessionKeyExpiredError` | Session key has expired |
|
|
435
|
-
| `SpendingLimitExceededError` | Would exceed session key limits |
|
|
436
|
-
| `SessionExpiredError` | Session authorization has expired |
|
|
437
|
-
| `SessionLimitExceededError` | Would exceed session spending limit |
|
|
438
|
-
| `SessionNotFoundError` | Session ID is invalid |
|
|
439
|
-
| `SessionRevokedError` | Session was revoked |
|
|
440
|
-
| `PaymentFailedError` | Payment transaction failed |
|
|
441
|
-
| `InvalidSessionKeyError` | Invalid session key format |
|
|
442
|
-
| `X402ProtocolError` | x402 protocol handling error |
|
|
443
|
-
|
|
444
|
-
## 🌐 Networks
|
|
445
|
-
|
|
446
|
-
| Network | Chain ID | Session Key Prefix | USDC Address |
|
|
447
|
-
|---------|----------|-------------------|--------------|
|
|
448
|
-
| Base Mainnet | 8453 | `sk_live_` | Production USDC |
|
|
449
|
-
| Base Sepolia | 84532 | `sk_test_` | Test USDC |
|
|
450
|
-
|
|
451
|
-
### Testing on Testnet
|
|
452
|
-
|
|
453
|
-
```typescript
|
|
454
|
-
// Test environment setup
|
|
455
|
-
const wallet = new AgentWallet({
|
|
456
|
-
sessionKey: 'sk_test_...', // Use test key
|
|
457
|
-
baseUrl: 'http://localhost:3000', // Local dev server
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
// The SDK automatically detects testnet from sk_test_ prefix
|
|
461
|
-
console.log(`Testnet: ${wallet.isTestnet()}`); // true
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
## 🔧 Environment Variables
|
|
465
|
-
|
|
466
|
-
| Variable | Required | Description |
|
|
467
|
-
|----------|----------|-------------|
|
|
468
|
-
| `MIXRPAY_SESSION_KEY` | Yes | Your session key (`sk_live_...` or `sk_test_...`) |
|
|
469
|
-
| `MIXRPAY_BASE_URL` | No | MixrPay server URL (default: `https://www.mixrpay.com`) |
|
|
470
|
-
|
|
471
|
-
## 🔒 Security Best Practices
|
|
472
|
-
|
|
473
|
-
1. **Store session keys securely** - Use environment variables, never commit to source
|
|
474
|
-
2. **Set appropriate limits** - Use `maxPaymentUsd` as a safety net
|
|
475
|
-
3. **Monitor spending** - Use `onPayment` callback and `getSpendingStats()`
|
|
476
|
-
4. **Use testnet first** - Test with `sk_test_` keys before production
|
|
477
|
-
5. **Configure base URL** - Always set `baseUrl` explicitly in production
|
|
478
|
-
|
|
479
|
-
## 📋 Changelog
|
|
480
|
-
|
|
481
|
-
### v0.2.0 (Current)
|
|
482
|
-
- Added session-based payments (`callMerchantApi()`)
|
|
483
|
-
- Added session management (`getOrCreateSession()`, `listSessions()`, `revokeSession()`)
|
|
484
|
-
- Added session-related error types
|
|
485
|
-
- Improved documentation
|
|
65
|
+
## Documentation
|
|
486
66
|
|
|
487
|
-
|
|
488
|
-
- Initial release
|
|
489
|
-
- x402 payment handling via `fetch()`
|
|
490
|
-
- Session key validation and spending limits
|
|
491
|
-
- Payment callbacks and statistics
|
|
67
|
+
[mixrpay.com/docs](https://www.mixrpay.com/docs)
|
|
492
68
|
|
|
493
|
-
##
|
|
69
|
+
## License
|
|
494
70
|
|
|
495
71
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1467,6 +1467,227 @@ var AgentWallet = class {
|
|
|
1467
1467
|
createdAt: new Date(data.createdAt || data.created_at)
|
|
1468
1468
|
};
|
|
1469
1469
|
}
|
|
1470
|
+
// ===========================================================================
|
|
1471
|
+
// MCP (Model Context Protocol) Methods
|
|
1472
|
+
// ===========================================================================
|
|
1473
|
+
/**
|
|
1474
|
+
* Get authentication headers for MCP wallet-based authentication.
|
|
1475
|
+
*
|
|
1476
|
+
* These headers prove wallet ownership without transmitting the private key.
|
|
1477
|
+
* Use for direct pay-per-call mode (no session needed).
|
|
1478
|
+
*
|
|
1479
|
+
* @returns Headers object with X-Mixr-Wallet, X-Mixr-Signature, X-Mixr-Timestamp
|
|
1480
|
+
*
|
|
1481
|
+
* @example
|
|
1482
|
+
* ```typescript
|
|
1483
|
+
* const headers = await wallet.getMCPAuthHeaders();
|
|
1484
|
+
* const response = await fetch('https://mixrpay.com/api/mcp', {
|
|
1485
|
+
* method: 'POST',
|
|
1486
|
+
* headers: {
|
|
1487
|
+
* 'Content-Type': 'application/json',
|
|
1488
|
+
* ...headers,
|
|
1489
|
+
* },
|
|
1490
|
+
* body: JSON.stringify({
|
|
1491
|
+
* jsonrpc: '2.0',
|
|
1492
|
+
* method: 'tools/list',
|
|
1493
|
+
* id: 1,
|
|
1494
|
+
* }),
|
|
1495
|
+
* });
|
|
1496
|
+
* ```
|
|
1497
|
+
*/
|
|
1498
|
+
async getMCPAuthHeaders() {
|
|
1499
|
+
const timestamp = Date.now().toString();
|
|
1500
|
+
const message = `MixrPay MCP Auth
|
|
1501
|
+
Wallet: ${this.walletAddress}
|
|
1502
|
+
Timestamp: ${timestamp}`;
|
|
1503
|
+
const signature = await this.sessionKey.signMessage(message);
|
|
1504
|
+
return {
|
|
1505
|
+
"X-Mixr-Wallet": this.walletAddress,
|
|
1506
|
+
"X-Mixr-Signature": signature,
|
|
1507
|
+
"X-Mixr-Timestamp": timestamp
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* List available MCP tools from the MixrPay gateway.
|
|
1512
|
+
*
|
|
1513
|
+
* Returns all tools exposed by MCP providers on the MixrPay marketplace.
|
|
1514
|
+
* Each tool includes pricing information.
|
|
1515
|
+
*
|
|
1516
|
+
* @returns Array of MCP tools with pricing and metadata
|
|
1517
|
+
*
|
|
1518
|
+
* @example
|
|
1519
|
+
* ```typescript
|
|
1520
|
+
* const tools = await wallet.listMCPTools();
|
|
1521
|
+
* for (const tool of tools) {
|
|
1522
|
+
* console.log(`${tool.name}: $${tool.priceUsd} - ${tool.description}`);
|
|
1523
|
+
* }
|
|
1524
|
+
* ```
|
|
1525
|
+
*/
|
|
1526
|
+
async listMCPTools() {
|
|
1527
|
+
this.logger.debug("listMCPTools");
|
|
1528
|
+
const response = await fetch(`${this.baseUrl}/api/mcp`, {
|
|
1529
|
+
method: "POST",
|
|
1530
|
+
headers: { "Content-Type": "application/json" },
|
|
1531
|
+
body: JSON.stringify({
|
|
1532
|
+
jsonrpc: "2.0",
|
|
1533
|
+
method: "tools/list",
|
|
1534
|
+
id: Date.now()
|
|
1535
|
+
})
|
|
1536
|
+
});
|
|
1537
|
+
if (!response.ok) {
|
|
1538
|
+
throw new MixrPayError(`Failed to list MCP tools: ${response.status}`);
|
|
1539
|
+
}
|
|
1540
|
+
const result = await response.json();
|
|
1541
|
+
if (result.error) {
|
|
1542
|
+
throw new MixrPayError(result.error.message || "Failed to list MCP tools");
|
|
1543
|
+
}
|
|
1544
|
+
return (result.result?.tools || []).map((tool) => ({
|
|
1545
|
+
name: tool.name,
|
|
1546
|
+
description: tool.description,
|
|
1547
|
+
inputSchema: tool.inputSchema,
|
|
1548
|
+
priceUsd: tool["x-mixrpay"]?.priceUsd || 0,
|
|
1549
|
+
merchantName: tool["x-mixrpay"]?.merchantName,
|
|
1550
|
+
merchantSlug: tool["x-mixrpay"]?.merchantSlug,
|
|
1551
|
+
verified: tool["x-mixrpay"]?.verified || false
|
|
1552
|
+
}));
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Call an MCP tool with wallet authentication (direct pay per call).
|
|
1556
|
+
*
|
|
1557
|
+
* This method signs a fresh auth message for each call, charging
|
|
1558
|
+
* directly from your wallet balance without needing a session.
|
|
1559
|
+
*
|
|
1560
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
1561
|
+
* @param args - Arguments to pass to the tool
|
|
1562
|
+
* @returns Tool execution result
|
|
1563
|
+
*
|
|
1564
|
+
* @example
|
|
1565
|
+
* ```typescript
|
|
1566
|
+
* const result = await wallet.callMCPTool('firecrawl/scrape', {
|
|
1567
|
+
* url: 'https://example.com',
|
|
1568
|
+
* });
|
|
1569
|
+
* console.log(result.data);
|
|
1570
|
+
* console.log(`Charged: $${result.chargedUsd}`);
|
|
1571
|
+
* ```
|
|
1572
|
+
*/
|
|
1573
|
+
async callMCPTool(toolName, args = {}) {
|
|
1574
|
+
this.logger.debug("callMCPTool", { toolName, args });
|
|
1575
|
+
const authHeaders = await this.getMCPAuthHeaders();
|
|
1576
|
+
const response = await fetch(`${this.baseUrl}/api/mcp`, {
|
|
1577
|
+
method: "POST",
|
|
1578
|
+
headers: {
|
|
1579
|
+
"Content-Type": "application/json",
|
|
1580
|
+
...authHeaders
|
|
1581
|
+
},
|
|
1582
|
+
body: JSON.stringify({
|
|
1583
|
+
jsonrpc: "2.0",
|
|
1584
|
+
method: "tools/call",
|
|
1585
|
+
params: { name: toolName, arguments: args },
|
|
1586
|
+
id: Date.now()
|
|
1587
|
+
})
|
|
1588
|
+
});
|
|
1589
|
+
const result = await response.json();
|
|
1590
|
+
if (result.error) {
|
|
1591
|
+
throw new MixrPayError(result.error.message || "MCP tool call failed");
|
|
1592
|
+
}
|
|
1593
|
+
const content = result.result?.content?.[0];
|
|
1594
|
+
const data = content?.text ? JSON.parse(content.text) : null;
|
|
1595
|
+
const mixrpay = result.result?._mixrpay || {};
|
|
1596
|
+
if (mixrpay.chargedUsd) {
|
|
1597
|
+
const payment = {
|
|
1598
|
+
amountUsd: mixrpay.chargedUsd,
|
|
1599
|
+
recipient: toolName.split("/")[0] || toolName,
|
|
1600
|
+
txHash: mixrpay.txHash,
|
|
1601
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1602
|
+
description: `MCP: ${toolName}`,
|
|
1603
|
+
url: `${this.baseUrl}/api/mcp`
|
|
1604
|
+
};
|
|
1605
|
+
this.payments.push(payment);
|
|
1606
|
+
this.totalSpentUsd += mixrpay.chargedUsd;
|
|
1607
|
+
this.logger.payment(mixrpay.chargedUsd, toolName, "MCP call");
|
|
1608
|
+
if (this.onPayment) {
|
|
1609
|
+
this.onPayment(payment);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
return {
|
|
1613
|
+
data,
|
|
1614
|
+
chargedUsd: mixrpay.chargedUsd || 0,
|
|
1615
|
+
txHash: mixrpay.txHash,
|
|
1616
|
+
latencyMs: mixrpay.latencyMs
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Call an MCP tool using session authorization (pre-authorized spending limit).
|
|
1621
|
+
*
|
|
1622
|
+
* Use this when you've already created a session with the tool provider
|
|
1623
|
+
* and want to use that spending limit instead of direct wallet charges.
|
|
1624
|
+
*
|
|
1625
|
+
* @param sessionId - The session ID for the tool provider
|
|
1626
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
1627
|
+
* @param args - Arguments to pass to the tool
|
|
1628
|
+
* @returns Tool execution result
|
|
1629
|
+
*
|
|
1630
|
+
* @example
|
|
1631
|
+
* ```typescript
|
|
1632
|
+
* // Create session with provider first
|
|
1633
|
+
* const session = await wallet.getOrCreateSession({
|
|
1634
|
+
* merchantPublicKey: 'pk_live_firecrawl_...',
|
|
1635
|
+
* spendingLimitUsd: 50,
|
|
1636
|
+
* });
|
|
1637
|
+
*
|
|
1638
|
+
* // Use session for multiple calls
|
|
1639
|
+
* const result = await wallet.callMCPToolWithSession(
|
|
1640
|
+
* session.id,
|
|
1641
|
+
* 'firecrawl/scrape',
|
|
1642
|
+
* { url: 'https://example.com' }
|
|
1643
|
+
* );
|
|
1644
|
+
* ```
|
|
1645
|
+
*/
|
|
1646
|
+
async callMCPToolWithSession(sessionId, toolName, args = {}) {
|
|
1647
|
+
this.logger.debug("callMCPToolWithSession", { sessionId, toolName, args });
|
|
1648
|
+
const response = await fetch(`${this.baseUrl}/api/mcp`, {
|
|
1649
|
+
method: "POST",
|
|
1650
|
+
headers: {
|
|
1651
|
+
"Content-Type": "application/json",
|
|
1652
|
+
"X-Mixr-Session": sessionId
|
|
1653
|
+
},
|
|
1654
|
+
body: JSON.stringify({
|
|
1655
|
+
jsonrpc: "2.0",
|
|
1656
|
+
method: "tools/call",
|
|
1657
|
+
params: { name: toolName, arguments: args },
|
|
1658
|
+
id: Date.now()
|
|
1659
|
+
})
|
|
1660
|
+
});
|
|
1661
|
+
const result = await response.json();
|
|
1662
|
+
if (result.error) {
|
|
1663
|
+
throw new MixrPayError(result.error.message || "MCP tool call failed");
|
|
1664
|
+
}
|
|
1665
|
+
const content = result.result?.content?.[0];
|
|
1666
|
+
const data = content?.text ? JSON.parse(content.text) : null;
|
|
1667
|
+
const mixrpay = result.result?._mixrpay || {};
|
|
1668
|
+
if (mixrpay.chargedUsd) {
|
|
1669
|
+
const payment = {
|
|
1670
|
+
amountUsd: mixrpay.chargedUsd,
|
|
1671
|
+
recipient: toolName.split("/")[0] || toolName,
|
|
1672
|
+
txHash: mixrpay.txHash,
|
|
1673
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1674
|
+
description: `MCP: ${toolName}`,
|
|
1675
|
+
url: `${this.baseUrl}/api/mcp`
|
|
1676
|
+
};
|
|
1677
|
+
this.payments.push(payment);
|
|
1678
|
+
this.totalSpentUsd += mixrpay.chargedUsd;
|
|
1679
|
+
this.logger.payment(mixrpay.chargedUsd, toolName, "MCP call (session)");
|
|
1680
|
+
if (this.onPayment) {
|
|
1681
|
+
this.onPayment(payment);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
data,
|
|
1686
|
+
chargedUsd: mixrpay.chargedUsd || 0,
|
|
1687
|
+
txHash: mixrpay.txHash,
|
|
1688
|
+
latencyMs: mixrpay.latencyMs
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1470
1691
|
};
|
|
1471
1692
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1472
1693
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -745,6 +745,129 @@ declare class AgentWallet {
|
|
|
745
745
|
* Parse session response data into SessionAuthorization object.
|
|
746
746
|
*/
|
|
747
747
|
private parseSessionResponse;
|
|
748
|
+
/**
|
|
749
|
+
* Get authentication headers for MCP wallet-based authentication.
|
|
750
|
+
*
|
|
751
|
+
* These headers prove wallet ownership without transmitting the private key.
|
|
752
|
+
* Use for direct pay-per-call mode (no session needed).
|
|
753
|
+
*
|
|
754
|
+
* @returns Headers object with X-Mixr-Wallet, X-Mixr-Signature, X-Mixr-Timestamp
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* ```typescript
|
|
758
|
+
* const headers = await wallet.getMCPAuthHeaders();
|
|
759
|
+
* const response = await fetch('https://mixrpay.com/api/mcp', {
|
|
760
|
+
* method: 'POST',
|
|
761
|
+
* headers: {
|
|
762
|
+
* 'Content-Type': 'application/json',
|
|
763
|
+
* ...headers,
|
|
764
|
+
* },
|
|
765
|
+
* body: JSON.stringify({
|
|
766
|
+
* jsonrpc: '2.0',
|
|
767
|
+
* method: 'tools/list',
|
|
768
|
+
* id: 1,
|
|
769
|
+
* }),
|
|
770
|
+
* });
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
getMCPAuthHeaders(): Promise<Record<string, string>>;
|
|
774
|
+
/**
|
|
775
|
+
* List available MCP tools from the MixrPay gateway.
|
|
776
|
+
*
|
|
777
|
+
* Returns all tools exposed by MCP providers on the MixrPay marketplace.
|
|
778
|
+
* Each tool includes pricing information.
|
|
779
|
+
*
|
|
780
|
+
* @returns Array of MCP tools with pricing and metadata
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```typescript
|
|
784
|
+
* const tools = await wallet.listMCPTools();
|
|
785
|
+
* for (const tool of tools) {
|
|
786
|
+
* console.log(`${tool.name}: $${tool.priceUsd} - ${tool.description}`);
|
|
787
|
+
* }
|
|
788
|
+
* ```
|
|
789
|
+
*/
|
|
790
|
+
listMCPTools(): Promise<MCPTool[]>;
|
|
791
|
+
/**
|
|
792
|
+
* Call an MCP tool with wallet authentication (direct pay per call).
|
|
793
|
+
*
|
|
794
|
+
* This method signs a fresh auth message for each call, charging
|
|
795
|
+
* directly from your wallet balance without needing a session.
|
|
796
|
+
*
|
|
797
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
798
|
+
* @param args - Arguments to pass to the tool
|
|
799
|
+
* @returns Tool execution result
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```typescript
|
|
803
|
+
* const result = await wallet.callMCPTool('firecrawl/scrape', {
|
|
804
|
+
* url: 'https://example.com',
|
|
805
|
+
* });
|
|
806
|
+
* console.log(result.data);
|
|
807
|
+
* console.log(`Charged: $${result.chargedUsd}`);
|
|
808
|
+
* ```
|
|
809
|
+
*/
|
|
810
|
+
callMCPTool(toolName: string, args?: Record<string, unknown>): Promise<MCPToolResult>;
|
|
811
|
+
/**
|
|
812
|
+
* Call an MCP tool using session authorization (pre-authorized spending limit).
|
|
813
|
+
*
|
|
814
|
+
* Use this when you've already created a session with the tool provider
|
|
815
|
+
* and want to use that spending limit instead of direct wallet charges.
|
|
816
|
+
*
|
|
817
|
+
* @param sessionId - The session ID for the tool provider
|
|
818
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
819
|
+
* @param args - Arguments to pass to the tool
|
|
820
|
+
* @returns Tool execution result
|
|
821
|
+
*
|
|
822
|
+
* @example
|
|
823
|
+
* ```typescript
|
|
824
|
+
* // Create session with provider first
|
|
825
|
+
* const session = await wallet.getOrCreateSession({
|
|
826
|
+
* merchantPublicKey: 'pk_live_firecrawl_...',
|
|
827
|
+
* spendingLimitUsd: 50,
|
|
828
|
+
* });
|
|
829
|
+
*
|
|
830
|
+
* // Use session for multiple calls
|
|
831
|
+
* const result = await wallet.callMCPToolWithSession(
|
|
832
|
+
* session.id,
|
|
833
|
+
* 'firecrawl/scrape',
|
|
834
|
+
* { url: 'https://example.com' }
|
|
835
|
+
* );
|
|
836
|
+
* ```
|
|
837
|
+
*/
|
|
838
|
+
callMCPToolWithSession(sessionId: string, toolName: string, args?: Record<string, unknown>): Promise<MCPToolResult>;
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* MCP Tool definition returned by listMCPTools()
|
|
842
|
+
*/
|
|
843
|
+
interface MCPTool {
|
|
844
|
+
/** Tool name in format "merchant/tool" */
|
|
845
|
+
name: string;
|
|
846
|
+
/** Human-readable description with price */
|
|
847
|
+
description: string;
|
|
848
|
+
/** JSON Schema for tool input parameters */
|
|
849
|
+
inputSchema: Record<string, unknown>;
|
|
850
|
+
/** Price per call in USD */
|
|
851
|
+
priceUsd: number;
|
|
852
|
+
/** Merchant/provider name */
|
|
853
|
+
merchantName: string;
|
|
854
|
+
/** Merchant slug for session creation */
|
|
855
|
+
merchantSlug: string;
|
|
856
|
+
/** Whether the provider is domain-verified */
|
|
857
|
+
verified: boolean;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Result from calling an MCP tool
|
|
861
|
+
*/
|
|
862
|
+
interface MCPToolResult {
|
|
863
|
+
/** The tool's response data */
|
|
864
|
+
data: unknown;
|
|
865
|
+
/** Amount charged in USD */
|
|
866
|
+
chargedUsd: number;
|
|
867
|
+
/** On-chain transaction hash (if available) */
|
|
868
|
+
txHash?: string;
|
|
869
|
+
/** Execution latency in milliseconds */
|
|
870
|
+
latencyMs?: number;
|
|
748
871
|
}
|
|
749
872
|
|
|
750
873
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -745,6 +745,129 @@ declare class AgentWallet {
|
|
|
745
745
|
* Parse session response data into SessionAuthorization object.
|
|
746
746
|
*/
|
|
747
747
|
private parseSessionResponse;
|
|
748
|
+
/**
|
|
749
|
+
* Get authentication headers for MCP wallet-based authentication.
|
|
750
|
+
*
|
|
751
|
+
* These headers prove wallet ownership without transmitting the private key.
|
|
752
|
+
* Use for direct pay-per-call mode (no session needed).
|
|
753
|
+
*
|
|
754
|
+
* @returns Headers object with X-Mixr-Wallet, X-Mixr-Signature, X-Mixr-Timestamp
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* ```typescript
|
|
758
|
+
* const headers = await wallet.getMCPAuthHeaders();
|
|
759
|
+
* const response = await fetch('https://mixrpay.com/api/mcp', {
|
|
760
|
+
* method: 'POST',
|
|
761
|
+
* headers: {
|
|
762
|
+
* 'Content-Type': 'application/json',
|
|
763
|
+
* ...headers,
|
|
764
|
+
* },
|
|
765
|
+
* body: JSON.stringify({
|
|
766
|
+
* jsonrpc: '2.0',
|
|
767
|
+
* method: 'tools/list',
|
|
768
|
+
* id: 1,
|
|
769
|
+
* }),
|
|
770
|
+
* });
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
getMCPAuthHeaders(): Promise<Record<string, string>>;
|
|
774
|
+
/**
|
|
775
|
+
* List available MCP tools from the MixrPay gateway.
|
|
776
|
+
*
|
|
777
|
+
* Returns all tools exposed by MCP providers on the MixrPay marketplace.
|
|
778
|
+
* Each tool includes pricing information.
|
|
779
|
+
*
|
|
780
|
+
* @returns Array of MCP tools with pricing and metadata
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```typescript
|
|
784
|
+
* const tools = await wallet.listMCPTools();
|
|
785
|
+
* for (const tool of tools) {
|
|
786
|
+
* console.log(`${tool.name}: $${tool.priceUsd} - ${tool.description}`);
|
|
787
|
+
* }
|
|
788
|
+
* ```
|
|
789
|
+
*/
|
|
790
|
+
listMCPTools(): Promise<MCPTool[]>;
|
|
791
|
+
/**
|
|
792
|
+
* Call an MCP tool with wallet authentication (direct pay per call).
|
|
793
|
+
*
|
|
794
|
+
* This method signs a fresh auth message for each call, charging
|
|
795
|
+
* directly from your wallet balance without needing a session.
|
|
796
|
+
*
|
|
797
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
798
|
+
* @param args - Arguments to pass to the tool
|
|
799
|
+
* @returns Tool execution result
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```typescript
|
|
803
|
+
* const result = await wallet.callMCPTool('firecrawl/scrape', {
|
|
804
|
+
* url: 'https://example.com',
|
|
805
|
+
* });
|
|
806
|
+
* console.log(result.data);
|
|
807
|
+
* console.log(`Charged: $${result.chargedUsd}`);
|
|
808
|
+
* ```
|
|
809
|
+
*/
|
|
810
|
+
callMCPTool(toolName: string, args?: Record<string, unknown>): Promise<MCPToolResult>;
|
|
811
|
+
/**
|
|
812
|
+
* Call an MCP tool using session authorization (pre-authorized spending limit).
|
|
813
|
+
*
|
|
814
|
+
* Use this when you've already created a session with the tool provider
|
|
815
|
+
* and want to use that spending limit instead of direct wallet charges.
|
|
816
|
+
*
|
|
817
|
+
* @param sessionId - The session ID for the tool provider
|
|
818
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
819
|
+
* @param args - Arguments to pass to the tool
|
|
820
|
+
* @returns Tool execution result
|
|
821
|
+
*
|
|
822
|
+
* @example
|
|
823
|
+
* ```typescript
|
|
824
|
+
* // Create session with provider first
|
|
825
|
+
* const session = await wallet.getOrCreateSession({
|
|
826
|
+
* merchantPublicKey: 'pk_live_firecrawl_...',
|
|
827
|
+
* spendingLimitUsd: 50,
|
|
828
|
+
* });
|
|
829
|
+
*
|
|
830
|
+
* // Use session for multiple calls
|
|
831
|
+
* const result = await wallet.callMCPToolWithSession(
|
|
832
|
+
* session.id,
|
|
833
|
+
* 'firecrawl/scrape',
|
|
834
|
+
* { url: 'https://example.com' }
|
|
835
|
+
* );
|
|
836
|
+
* ```
|
|
837
|
+
*/
|
|
838
|
+
callMCPToolWithSession(sessionId: string, toolName: string, args?: Record<string, unknown>): Promise<MCPToolResult>;
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* MCP Tool definition returned by listMCPTools()
|
|
842
|
+
*/
|
|
843
|
+
interface MCPTool {
|
|
844
|
+
/** Tool name in format "merchant/tool" */
|
|
845
|
+
name: string;
|
|
846
|
+
/** Human-readable description with price */
|
|
847
|
+
description: string;
|
|
848
|
+
/** JSON Schema for tool input parameters */
|
|
849
|
+
inputSchema: Record<string, unknown>;
|
|
850
|
+
/** Price per call in USD */
|
|
851
|
+
priceUsd: number;
|
|
852
|
+
/** Merchant/provider name */
|
|
853
|
+
merchantName: string;
|
|
854
|
+
/** Merchant slug for session creation */
|
|
855
|
+
merchantSlug: string;
|
|
856
|
+
/** Whether the provider is domain-verified */
|
|
857
|
+
verified: boolean;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Result from calling an MCP tool
|
|
861
|
+
*/
|
|
862
|
+
interface MCPToolResult {
|
|
863
|
+
/** The tool's response data */
|
|
864
|
+
data: unknown;
|
|
865
|
+
/** Amount charged in USD */
|
|
866
|
+
chargedUsd: number;
|
|
867
|
+
/** On-chain transaction hash (if available) */
|
|
868
|
+
txHash?: string;
|
|
869
|
+
/** Execution latency in milliseconds */
|
|
870
|
+
latencyMs?: number;
|
|
748
871
|
}
|
|
749
872
|
|
|
750
873
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1433,6 +1433,227 @@ var AgentWallet = class {
|
|
|
1433
1433
|
createdAt: new Date(data.createdAt || data.created_at)
|
|
1434
1434
|
};
|
|
1435
1435
|
}
|
|
1436
|
+
// ===========================================================================
|
|
1437
|
+
// MCP (Model Context Protocol) Methods
|
|
1438
|
+
// ===========================================================================
|
|
1439
|
+
/**
|
|
1440
|
+
* Get authentication headers for MCP wallet-based authentication.
|
|
1441
|
+
*
|
|
1442
|
+
* These headers prove wallet ownership without transmitting the private key.
|
|
1443
|
+
* Use for direct pay-per-call mode (no session needed).
|
|
1444
|
+
*
|
|
1445
|
+
* @returns Headers object with X-Mixr-Wallet, X-Mixr-Signature, X-Mixr-Timestamp
|
|
1446
|
+
*
|
|
1447
|
+
* @example
|
|
1448
|
+
* ```typescript
|
|
1449
|
+
* const headers = await wallet.getMCPAuthHeaders();
|
|
1450
|
+
* const response = await fetch('https://mixrpay.com/api/mcp', {
|
|
1451
|
+
* method: 'POST',
|
|
1452
|
+
* headers: {
|
|
1453
|
+
* 'Content-Type': 'application/json',
|
|
1454
|
+
* ...headers,
|
|
1455
|
+
* },
|
|
1456
|
+
* body: JSON.stringify({
|
|
1457
|
+
* jsonrpc: '2.0',
|
|
1458
|
+
* method: 'tools/list',
|
|
1459
|
+
* id: 1,
|
|
1460
|
+
* }),
|
|
1461
|
+
* });
|
|
1462
|
+
* ```
|
|
1463
|
+
*/
|
|
1464
|
+
async getMCPAuthHeaders() {
|
|
1465
|
+
const timestamp = Date.now().toString();
|
|
1466
|
+
const message = `MixrPay MCP Auth
|
|
1467
|
+
Wallet: ${this.walletAddress}
|
|
1468
|
+
Timestamp: ${timestamp}`;
|
|
1469
|
+
const signature = await this.sessionKey.signMessage(message);
|
|
1470
|
+
return {
|
|
1471
|
+
"X-Mixr-Wallet": this.walletAddress,
|
|
1472
|
+
"X-Mixr-Signature": signature,
|
|
1473
|
+
"X-Mixr-Timestamp": timestamp
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* List available MCP tools from the MixrPay gateway.
|
|
1478
|
+
*
|
|
1479
|
+
* Returns all tools exposed by MCP providers on the MixrPay marketplace.
|
|
1480
|
+
* Each tool includes pricing information.
|
|
1481
|
+
*
|
|
1482
|
+
* @returns Array of MCP tools with pricing and metadata
|
|
1483
|
+
*
|
|
1484
|
+
* @example
|
|
1485
|
+
* ```typescript
|
|
1486
|
+
* const tools = await wallet.listMCPTools();
|
|
1487
|
+
* for (const tool of tools) {
|
|
1488
|
+
* console.log(`${tool.name}: $${tool.priceUsd} - ${tool.description}`);
|
|
1489
|
+
* }
|
|
1490
|
+
* ```
|
|
1491
|
+
*/
|
|
1492
|
+
async listMCPTools() {
|
|
1493
|
+
this.logger.debug("listMCPTools");
|
|
1494
|
+
const response = await fetch(`${this.baseUrl}/api/mcp`, {
|
|
1495
|
+
method: "POST",
|
|
1496
|
+
headers: { "Content-Type": "application/json" },
|
|
1497
|
+
body: JSON.stringify({
|
|
1498
|
+
jsonrpc: "2.0",
|
|
1499
|
+
method: "tools/list",
|
|
1500
|
+
id: Date.now()
|
|
1501
|
+
})
|
|
1502
|
+
});
|
|
1503
|
+
if (!response.ok) {
|
|
1504
|
+
throw new MixrPayError(`Failed to list MCP tools: ${response.status}`);
|
|
1505
|
+
}
|
|
1506
|
+
const result = await response.json();
|
|
1507
|
+
if (result.error) {
|
|
1508
|
+
throw new MixrPayError(result.error.message || "Failed to list MCP tools");
|
|
1509
|
+
}
|
|
1510
|
+
return (result.result?.tools || []).map((tool) => ({
|
|
1511
|
+
name: tool.name,
|
|
1512
|
+
description: tool.description,
|
|
1513
|
+
inputSchema: tool.inputSchema,
|
|
1514
|
+
priceUsd: tool["x-mixrpay"]?.priceUsd || 0,
|
|
1515
|
+
merchantName: tool["x-mixrpay"]?.merchantName,
|
|
1516
|
+
merchantSlug: tool["x-mixrpay"]?.merchantSlug,
|
|
1517
|
+
verified: tool["x-mixrpay"]?.verified || false
|
|
1518
|
+
}));
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Call an MCP tool with wallet authentication (direct pay per call).
|
|
1522
|
+
*
|
|
1523
|
+
* This method signs a fresh auth message for each call, charging
|
|
1524
|
+
* directly from your wallet balance without needing a session.
|
|
1525
|
+
*
|
|
1526
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
1527
|
+
* @param args - Arguments to pass to the tool
|
|
1528
|
+
* @returns Tool execution result
|
|
1529
|
+
*
|
|
1530
|
+
* @example
|
|
1531
|
+
* ```typescript
|
|
1532
|
+
* const result = await wallet.callMCPTool('firecrawl/scrape', {
|
|
1533
|
+
* url: 'https://example.com',
|
|
1534
|
+
* });
|
|
1535
|
+
* console.log(result.data);
|
|
1536
|
+
* console.log(`Charged: $${result.chargedUsd}`);
|
|
1537
|
+
* ```
|
|
1538
|
+
*/
|
|
1539
|
+
async callMCPTool(toolName, args = {}) {
|
|
1540
|
+
this.logger.debug("callMCPTool", { toolName, args });
|
|
1541
|
+
const authHeaders = await this.getMCPAuthHeaders();
|
|
1542
|
+
const response = await fetch(`${this.baseUrl}/api/mcp`, {
|
|
1543
|
+
method: "POST",
|
|
1544
|
+
headers: {
|
|
1545
|
+
"Content-Type": "application/json",
|
|
1546
|
+
...authHeaders
|
|
1547
|
+
},
|
|
1548
|
+
body: JSON.stringify({
|
|
1549
|
+
jsonrpc: "2.0",
|
|
1550
|
+
method: "tools/call",
|
|
1551
|
+
params: { name: toolName, arguments: args },
|
|
1552
|
+
id: Date.now()
|
|
1553
|
+
})
|
|
1554
|
+
});
|
|
1555
|
+
const result = await response.json();
|
|
1556
|
+
if (result.error) {
|
|
1557
|
+
throw new MixrPayError(result.error.message || "MCP tool call failed");
|
|
1558
|
+
}
|
|
1559
|
+
const content = result.result?.content?.[0];
|
|
1560
|
+
const data = content?.text ? JSON.parse(content.text) : null;
|
|
1561
|
+
const mixrpay = result.result?._mixrpay || {};
|
|
1562
|
+
if (mixrpay.chargedUsd) {
|
|
1563
|
+
const payment = {
|
|
1564
|
+
amountUsd: mixrpay.chargedUsd,
|
|
1565
|
+
recipient: toolName.split("/")[0] || toolName,
|
|
1566
|
+
txHash: mixrpay.txHash,
|
|
1567
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1568
|
+
description: `MCP: ${toolName}`,
|
|
1569
|
+
url: `${this.baseUrl}/api/mcp`
|
|
1570
|
+
};
|
|
1571
|
+
this.payments.push(payment);
|
|
1572
|
+
this.totalSpentUsd += mixrpay.chargedUsd;
|
|
1573
|
+
this.logger.payment(mixrpay.chargedUsd, toolName, "MCP call");
|
|
1574
|
+
if (this.onPayment) {
|
|
1575
|
+
this.onPayment(payment);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return {
|
|
1579
|
+
data,
|
|
1580
|
+
chargedUsd: mixrpay.chargedUsd || 0,
|
|
1581
|
+
txHash: mixrpay.txHash,
|
|
1582
|
+
latencyMs: mixrpay.latencyMs
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* Call an MCP tool using session authorization (pre-authorized spending limit).
|
|
1587
|
+
*
|
|
1588
|
+
* Use this when you've already created a session with the tool provider
|
|
1589
|
+
* and want to use that spending limit instead of direct wallet charges.
|
|
1590
|
+
*
|
|
1591
|
+
* @param sessionId - The session ID for the tool provider
|
|
1592
|
+
* @param toolName - The tool name in format "merchant/tool"
|
|
1593
|
+
* @param args - Arguments to pass to the tool
|
|
1594
|
+
* @returns Tool execution result
|
|
1595
|
+
*
|
|
1596
|
+
* @example
|
|
1597
|
+
* ```typescript
|
|
1598
|
+
* // Create session with provider first
|
|
1599
|
+
* const session = await wallet.getOrCreateSession({
|
|
1600
|
+
* merchantPublicKey: 'pk_live_firecrawl_...',
|
|
1601
|
+
* spendingLimitUsd: 50,
|
|
1602
|
+
* });
|
|
1603
|
+
*
|
|
1604
|
+
* // Use session for multiple calls
|
|
1605
|
+
* const result = await wallet.callMCPToolWithSession(
|
|
1606
|
+
* session.id,
|
|
1607
|
+
* 'firecrawl/scrape',
|
|
1608
|
+
* { url: 'https://example.com' }
|
|
1609
|
+
* );
|
|
1610
|
+
* ```
|
|
1611
|
+
*/
|
|
1612
|
+
async callMCPToolWithSession(sessionId, toolName, args = {}) {
|
|
1613
|
+
this.logger.debug("callMCPToolWithSession", { sessionId, toolName, args });
|
|
1614
|
+
const response = await fetch(`${this.baseUrl}/api/mcp`, {
|
|
1615
|
+
method: "POST",
|
|
1616
|
+
headers: {
|
|
1617
|
+
"Content-Type": "application/json",
|
|
1618
|
+
"X-Mixr-Session": sessionId
|
|
1619
|
+
},
|
|
1620
|
+
body: JSON.stringify({
|
|
1621
|
+
jsonrpc: "2.0",
|
|
1622
|
+
method: "tools/call",
|
|
1623
|
+
params: { name: toolName, arguments: args },
|
|
1624
|
+
id: Date.now()
|
|
1625
|
+
})
|
|
1626
|
+
});
|
|
1627
|
+
const result = await response.json();
|
|
1628
|
+
if (result.error) {
|
|
1629
|
+
throw new MixrPayError(result.error.message || "MCP tool call failed");
|
|
1630
|
+
}
|
|
1631
|
+
const content = result.result?.content?.[0];
|
|
1632
|
+
const data = content?.text ? JSON.parse(content.text) : null;
|
|
1633
|
+
const mixrpay = result.result?._mixrpay || {};
|
|
1634
|
+
if (mixrpay.chargedUsd) {
|
|
1635
|
+
const payment = {
|
|
1636
|
+
amountUsd: mixrpay.chargedUsd,
|
|
1637
|
+
recipient: toolName.split("/")[0] || toolName,
|
|
1638
|
+
txHash: mixrpay.txHash,
|
|
1639
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1640
|
+
description: `MCP: ${toolName}`,
|
|
1641
|
+
url: `${this.baseUrl}/api/mcp`
|
|
1642
|
+
};
|
|
1643
|
+
this.payments.push(payment);
|
|
1644
|
+
this.totalSpentUsd += mixrpay.chargedUsd;
|
|
1645
|
+
this.logger.payment(mixrpay.chargedUsd, toolName, "MCP call (session)");
|
|
1646
|
+
if (this.onPayment) {
|
|
1647
|
+
this.onPayment(payment);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
return {
|
|
1651
|
+
data,
|
|
1652
|
+
chargedUsd: mixrpay.chargedUsd || 0,
|
|
1653
|
+
txHash: mixrpay.txHash,
|
|
1654
|
+
latencyMs: mixrpay.latencyMs
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1436
1657
|
};
|
|
1437
1658
|
export {
|
|
1438
1659
|
AgentWallet,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mixrpay/agent-sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "MixrPay Agent SDK - Enable AI agents to make x402 payments with session keys",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,19 +29,7 @@
|
|
|
29
29
|
"typecheck": "tsc --noEmit",
|
|
30
30
|
"prepublishOnly": "npm run build"
|
|
31
31
|
},
|
|
32
|
-
"keywords": [
|
|
33
|
-
"payments",
|
|
34
|
-
"ai-agents",
|
|
35
|
-
"x402",
|
|
36
|
-
"cryptocurrency",
|
|
37
|
-
"usdc",
|
|
38
|
-
"blockchain",
|
|
39
|
-
"web3",
|
|
40
|
-
"vercel-ai-sdk",
|
|
41
|
-
"langchain",
|
|
42
|
-
"ai",
|
|
43
|
-
"agent"
|
|
44
|
-
],
|
|
32
|
+
"keywords": [],
|
|
45
33
|
"author": "MixrPay <support@mixrpay.com>",
|
|
46
34
|
"license": "MIT",
|
|
47
35
|
"repository": {
|