@yativo/mcp-server 1.0.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.
- package/README.md +108 -0
- package/package.json +19 -0
- package/src/index.js +283 -0
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Yativo Agentic Wallet MCP Server
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server that gives AI agents direct access to Yativo's MPC crypto wallets. Supports balance checks, payments, transaction history, and **x402 auto-pay** for paywalled APIs.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd yativo-crypto-mcp
|
|
9
|
+
npm install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
Set these environment variables:
|
|
15
|
+
|
|
16
|
+
| Variable | Required | Description |
|
|
17
|
+
|----------|----------|-------------|
|
|
18
|
+
| `YATIVO_API_KEY` | Yes | Connector API key (starts with `yac_`) |
|
|
19
|
+
| `YATIVO_WALLET_ID` | Recommended | Default wallet ID (starts with `aw_`) |
|
|
20
|
+
| `YATIVO_API_URL` | No | API base URL (default: `https://api.yativo.com/api/v1`) |
|
|
21
|
+
|
|
22
|
+
## Claude Desktop Setup
|
|
23
|
+
|
|
24
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"yativo-wallet": {
|
|
30
|
+
"command": "node",
|
|
31
|
+
"args": ["/path/to/yativo-crypto-mcp/src/index.js"],
|
|
32
|
+
"env": {
|
|
33
|
+
"YATIVO_API_KEY": "yac_your_connector_api_key_here",
|
|
34
|
+
"YATIVO_WALLET_ID": "aw_your_wallet_id"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## VS Code / Copilot Setup
|
|
42
|
+
|
|
43
|
+
Add to `.vscode/mcp.json`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"servers": {
|
|
48
|
+
"yativo-wallet": {
|
|
49
|
+
"command": "node",
|
|
50
|
+
"args": ["${workspaceFolder}/yativo-crypto-mcp/src/index.js"],
|
|
51
|
+
"env": {
|
|
52
|
+
"YATIVO_API_KEY": "yac_your_connector_api_key_here",
|
|
53
|
+
"YATIVO_WALLET_ID": "aw_your_wallet_id"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Available Tools
|
|
61
|
+
|
|
62
|
+
### `get_balance`
|
|
63
|
+
Check wallet balance and address.
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
"Check my wallet balance"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `send_payment`
|
|
70
|
+
Send crypto to a recipient address. Requires audit trail fields.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
"Send $25 USDC to 0x742d...D18 for the API subscription"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `x402_fetch`
|
|
77
|
+
Fetch a URL with automatic x402 payment. If the server returns HTTP 402 with x402 headers, the wallet signs a USDC payment and retries.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
"Fetch https://api.example.com/premium/data — pay if it's x402 paywalled"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `get_transactions`
|
|
84
|
+
View recent transaction history.
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
"Show my last 5 transactions"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Resources
|
|
91
|
+
|
|
92
|
+
### `wallet://info`
|
|
93
|
+
Returns the current wallet's ID, address, chain, and balance as JSON.
|
|
94
|
+
|
|
95
|
+
## How x402 Works
|
|
96
|
+
|
|
97
|
+
When you ask the agent to fetch a URL:
|
|
98
|
+
|
|
99
|
+
1. The MCP server calls `POST /agent/x402-fetch`
|
|
100
|
+
2. The backend fetches the URL
|
|
101
|
+
3. If the server returns **HTTP 402** with an `X-PAYMENT` header:
|
|
102
|
+
- Parses USDC payment requirements
|
|
103
|
+
- Signs an EIP-3009 `transferWithAuthorization` with the MPC key
|
|
104
|
+
- Retries the request with the payment signature
|
|
105
|
+
4. Returns the actual API response to the agent
|
|
106
|
+
5. Records the payment with full audit trail
|
|
107
|
+
|
|
108
|
+
Supported: USDC on Ethereum, Base, Polygon, Arbitrum, Optimism, Gnosis.
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yativo/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP (Model Context Protocol) server for Yativo Agentic Wallets — lets AI agents manage crypto wallets, send payments, and pay for x402 APIs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"yativo-mcp": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/index.js",
|
|
12
|
+
"dev": "node --watch src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": ["mcp", "model-context-protocol", "crypto", "wallet", "agent", "x402", "usdc"],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Yativo Agentic Wallet MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Exposes MPC agentic wallet operations as MCP tools for AI agents.
|
|
7
|
+
* Agents can check balances, send payments, fetch x402 paywalled APIs,
|
|
8
|
+
* and view transaction history — all through the Model Context Protocol.
|
|
9
|
+
*
|
|
10
|
+
* Configuration (environment variables):
|
|
11
|
+
* YATIVO_API_URL - Base URL of the Yativo API (default: https://api.yativo.com/api/v1)
|
|
12
|
+
* YATIVO_API_KEY - Connector API key (starts with yac_)
|
|
13
|
+
* YATIVO_WALLET_ID - Default agentic wallet ID (starts with aw_)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
17
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
|
|
20
|
+
const API_URL = process.env.YATIVO_API_URL || 'https://api.yativo.com/api/v1';
|
|
21
|
+
const API_KEY = process.env.YATIVO_API_KEY || '';
|
|
22
|
+
const DEFAULT_WALLET_ID = process.env.YATIVO_WALLET_ID || '';
|
|
23
|
+
|
|
24
|
+
// ─── HTTP Helper ──────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
async function apiRequest(method, path, body = null, queryParams = null) {
|
|
27
|
+
let url = `${API_URL}${path}`;
|
|
28
|
+
if (queryParams) {
|
|
29
|
+
const params = new URLSearchParams();
|
|
30
|
+
for (const [k, v] of Object.entries(queryParams)) {
|
|
31
|
+
if (v != null) params.append(k, String(v));
|
|
32
|
+
}
|
|
33
|
+
const qs = params.toString();
|
|
34
|
+
if (qs) url += `?${qs}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const headers = {
|
|
38
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const options = { method, headers };
|
|
43
|
+
if (body && method !== 'GET') {
|
|
44
|
+
options.body = JSON.stringify(body);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const response = await fetch(url, options);
|
|
48
|
+
const data = await response.json();
|
|
49
|
+
|
|
50
|
+
if (!response.ok || !data.success) {
|
|
51
|
+
throw new Error(data.message || `API error: ${response.status}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveWalletId(walletId) {
|
|
58
|
+
const id = walletId || DEFAULT_WALLET_ID;
|
|
59
|
+
if (!id) throw new Error('wallet_id is required (set YATIVO_WALLET_ID or pass it explicitly)');
|
|
60
|
+
return id;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── MCP Server ───────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
const server = new McpServer({
|
|
66
|
+
name: 'yativo-wallet',
|
|
67
|
+
version: '1.0.0',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// ─── Tool: get_balance ────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
server.tool(
|
|
73
|
+
'get_balance',
|
|
74
|
+
'Check the balance and address of your agentic crypto wallet',
|
|
75
|
+
{
|
|
76
|
+
wallet_id: z.string().optional().describe('Wallet ID (uses default if not provided)'),
|
|
77
|
+
},
|
|
78
|
+
async ({ wallet_id }) => {
|
|
79
|
+
const wid = resolveWalletId(wallet_id);
|
|
80
|
+
const data = await apiRequest('GET', '/agent/balance', null, { wallet_id: wid });
|
|
81
|
+
const w = data.data;
|
|
82
|
+
return {
|
|
83
|
+
content: [{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: [
|
|
86
|
+
`Wallet: ${w.wallet_id}`,
|
|
87
|
+
`Address: ${w.wallet_address}`,
|
|
88
|
+
`Chain: ${w.chain}`,
|
|
89
|
+
`Balance: $${w.balance.toFixed(2)}`,
|
|
90
|
+
w.assets && Object.keys(w.assets).length > 0
|
|
91
|
+
? `Assets: ${JSON.stringify(w.assets)}`
|
|
92
|
+
: null,
|
|
93
|
+
].filter(Boolean).join('\n'),
|
|
94
|
+
}],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// ─── Tool: send_payment ───────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
server.tool(
|
|
102
|
+
'send_payment',
|
|
103
|
+
'Send a crypto payment from your agentic wallet to a recipient address. Requires audit trail fields (service_name, payment_reason).',
|
|
104
|
+
{
|
|
105
|
+
recipient_address: z.string().describe('The destination wallet address'),
|
|
106
|
+
amount: z.number().positive().describe('Amount in USD'),
|
|
107
|
+
asset: z.string().default('USDC').describe('Asset to send (e.g. USDC, SOL, ETH)'),
|
|
108
|
+
chain: z.string().optional().describe('Chain override (e.g. ethereum, base, solana)'),
|
|
109
|
+
service_name: z.string().describe('Name of the service being paid'),
|
|
110
|
+
service_url: z.string().optional().describe('URL of the service being paid'),
|
|
111
|
+
payment_reason: z.string().describe('Why this payment is being made'),
|
|
112
|
+
payment_category: z.enum([
|
|
113
|
+
'prediction_market', 'legal_escrow', 'ai_service',
|
|
114
|
+
'subscription', 'purchase', 'transfer', 'settlement', 'other',
|
|
115
|
+
]).default('other').describe('Payment category'),
|
|
116
|
+
wallet_id: z.string().optional().describe('Wallet ID (uses default if not provided)'),
|
|
117
|
+
},
|
|
118
|
+
async (params) => {
|
|
119
|
+
const wid = resolveWalletId(params.wallet_id);
|
|
120
|
+
const data = await apiRequest('POST', '/agent/transact', {
|
|
121
|
+
wallet_id: wid,
|
|
122
|
+
amount: params.amount,
|
|
123
|
+
asset: params.asset,
|
|
124
|
+
chain: params.chain,
|
|
125
|
+
recipient_address: params.recipient_address,
|
|
126
|
+
service_name: params.service_name,
|
|
127
|
+
service_url: params.service_url,
|
|
128
|
+
payment_reason: params.payment_reason,
|
|
129
|
+
payment_category: params.payment_category,
|
|
130
|
+
});
|
|
131
|
+
const tx = data.data;
|
|
132
|
+
return {
|
|
133
|
+
content: [{
|
|
134
|
+
type: 'text',
|
|
135
|
+
text: [
|
|
136
|
+
`Payment sent successfully!`,
|
|
137
|
+
`Transaction: ${tx.transaction_id}`,
|
|
138
|
+
`Hash: ${tx.tx_hash}`,
|
|
139
|
+
`Amount: $${tx.amount} ${tx.asset}`,
|
|
140
|
+
`To: ${tx.recipient_address}`,
|
|
141
|
+
`Chain: ${tx.chain}`,
|
|
142
|
+
`Status: ${tx.status}`,
|
|
143
|
+
tx.fee > 0 ? `Fee: $${tx.fee}` : null,
|
|
144
|
+
tx.is_free_transaction ? '(Free tier transaction)' : null,
|
|
145
|
+
].filter(Boolean).join('\n'),
|
|
146
|
+
}],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// ─── Tool: x402_fetch ─────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
server.tool(
|
|
154
|
+
'x402_fetch',
|
|
155
|
+
'Fetch a URL with automatic x402 payment support. If the URL returns HTTP 402 (payment required), the wallet auto-pays with USDC and retries. Use this for x402-paywalled APIs.',
|
|
156
|
+
{
|
|
157
|
+
url: z.string().url().describe('The URL to fetch (if it requires x402 payment, it will be auto-paid)'),
|
|
158
|
+
method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).default('GET').describe('HTTP method'),
|
|
159
|
+
headers: z.record(z.string()).optional().describe('Additional headers to send'),
|
|
160
|
+
body: z.any().optional().describe('Request body (for POST/PUT/PATCH)'),
|
|
161
|
+
wallet_id: z.string().optional().describe('Wallet ID (uses default if not provided)'),
|
|
162
|
+
},
|
|
163
|
+
async (params) => {
|
|
164
|
+
const wid = resolveWalletId(params.wallet_id);
|
|
165
|
+
const data = await apiRequest('POST', '/agent/x402-fetch', {
|
|
166
|
+
wallet_id: wid,
|
|
167
|
+
url: params.url,
|
|
168
|
+
method: params.method,
|
|
169
|
+
headers: params.headers,
|
|
170
|
+
body: params.body,
|
|
171
|
+
});
|
|
172
|
+
const result = data.data;
|
|
173
|
+
|
|
174
|
+
const lines = [`Status: ${result.status}`];
|
|
175
|
+
|
|
176
|
+
if (result.paid && result.payment) {
|
|
177
|
+
lines.push(`[x402] Paid $${result.payment.amount_usd} ${result.payment.asset} on ${result.payment.network}`);
|
|
178
|
+
lines.push(`[x402] Paid to: ${result.payment.pay_to}`);
|
|
179
|
+
} else {
|
|
180
|
+
lines.push('(No payment required)');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push('--- Response Body ---');
|
|
185
|
+
if (typeof result.body === 'string') {
|
|
186
|
+
lines.push(result.body);
|
|
187
|
+
} else {
|
|
188
|
+
lines.push(JSON.stringify(result.body, null, 2));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// ─── Tool: get_transactions ───────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
server.tool(
|
|
200
|
+
'get_transactions',
|
|
201
|
+
'Get recent transaction history for the agentic wallet',
|
|
202
|
+
{
|
|
203
|
+
wallet_id: z.string().optional().describe('Wallet ID (uses default if not provided)'),
|
|
204
|
+
page: z.number().int().positive().default(1).describe('Page number'),
|
|
205
|
+
limit: z.number().int().min(1).max(100).default(10).describe('Results per page'),
|
|
206
|
+
},
|
|
207
|
+
async (params) => {
|
|
208
|
+
const wid = resolveWalletId(params.wallet_id);
|
|
209
|
+
const data = await apiRequest('GET', '/agent/transactions', null, {
|
|
210
|
+
wallet_id: wid,
|
|
211
|
+
page: params.page,
|
|
212
|
+
limit: params.limit,
|
|
213
|
+
});
|
|
214
|
+
const txs = data.data.transactions;
|
|
215
|
+
|
|
216
|
+
if (!txs || txs.length === 0) {
|
|
217
|
+
return { content: [{ type: 'text', text: 'No transactions found.' }] };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const lines = txs.map((tx, i) => [
|
|
221
|
+
`${i + 1}. ${tx.type} — $${tx.amount} ${tx.asset}`,
|
|
222
|
+
` Status: ${tx.status} | ${tx.created_at}`,
|
|
223
|
+
tx.tx_hash ? ` Hash: ${tx.tx_hash}` : null,
|
|
224
|
+
tx.recipient_address ? ` To: ${tx.recipient_address}` : null,
|
|
225
|
+
].filter(Boolean).join('\n'));
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
content: [{ type: 'text', text: lines.join('\n\n') }],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// ─── Resource: wallet info ────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
server.resource(
|
|
236
|
+
'wallet-info',
|
|
237
|
+
'wallet://info',
|
|
238
|
+
async (uri) => {
|
|
239
|
+
if (!DEFAULT_WALLET_ID) {
|
|
240
|
+
return {
|
|
241
|
+
contents: [{
|
|
242
|
+
uri: uri.href,
|
|
243
|
+
text: 'No default wallet configured. Set YATIVO_WALLET_ID environment variable.',
|
|
244
|
+
mimeType: 'text/plain',
|
|
245
|
+
}],
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const data = await apiRequest('GET', '/agent/balance', null, { wallet_id: DEFAULT_WALLET_ID });
|
|
250
|
+
const w = data.data;
|
|
251
|
+
return {
|
|
252
|
+
contents: [{
|
|
253
|
+
uri: uri.href,
|
|
254
|
+
text: JSON.stringify({
|
|
255
|
+
wallet_id: w.wallet_id,
|
|
256
|
+
wallet_address: w.wallet_address,
|
|
257
|
+
chain: w.chain,
|
|
258
|
+
balance: w.balance,
|
|
259
|
+
assets: w.assets,
|
|
260
|
+
}, null, 2),
|
|
261
|
+
mimeType: 'application/json',
|
|
262
|
+
}],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// ─── Start ────────────────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
async function main() {
|
|
270
|
+
if (!API_KEY) {
|
|
271
|
+
console.error('Error: YATIVO_API_KEY environment variable is required');
|
|
272
|
+
console.error('Set it to your connector API key (starts with yac_)');
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const transport = new StdioServerTransport();
|
|
277
|
+
await server.connect(transport);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
main().catch((err) => {
|
|
281
|
+
console.error('Fatal:', err);
|
|
282
|
+
process.exit(1);
|
|
283
|
+
});
|