@dexterai/mcp 0.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.
- package/README.md +104 -0
- package/dist/index.js +867 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# @dexterai/mcp
|
|
2
|
+
|
|
3
|
+
x402 gateway for AI agents. Search, pay, and call any x402 API through Dexter.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
### Guided Install (recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @dexterai/mcp install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Supports: **Cursor**, **Claude Code**, **Codex**, **VS Code**, **Windsurf**, **Gemini CLI**
|
|
14
|
+
|
|
15
|
+
### Manual Install
|
|
16
|
+
|
|
17
|
+
**Cursor** — add to `~/.cursor/mcp.json`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"dexter-x402": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "@dexterai/mcp@latest"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Claude Code:**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
claude mcp add dexter-x402 -- npx -y @dexterai/mcp@latest
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Codex** — add to `~/.codex/config.toml`:
|
|
37
|
+
|
|
38
|
+
```toml
|
|
39
|
+
[mcp_servers.dexter-x402]
|
|
40
|
+
command = "npx"
|
|
41
|
+
args = ["-y", "@dexterai/mcp@latest"]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## How It Works
|
|
45
|
+
|
|
46
|
+
On first run, a Solana wallet is created at `~/.dexterai-mcp/wallet.json`. Deposit USDC to the wallet address and the MCP handles payments automatically.
|
|
47
|
+
|
|
48
|
+
### Tools
|
|
49
|
+
|
|
50
|
+
| Tool | Description |
|
|
51
|
+
|------|-------------|
|
|
52
|
+
| `x402_search` | Search the Dexter marketplace for paid APIs |
|
|
53
|
+
| `x402_fetch` | Call any x402 API with automatic payment |
|
|
54
|
+
| `x402_check` | Preview endpoint pricing without paying |
|
|
55
|
+
| `x402_wallet` | Show wallet address and USDC balance |
|
|
56
|
+
|
|
57
|
+
### Example
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Agent: "Find me a Jupiter DEX quote API"
|
|
61
|
+
→ x402_search("jupiter quote")
|
|
62
|
+
→ Returns: Jupiter DEX Quote — $0.05/call — verified
|
|
63
|
+
|
|
64
|
+
Agent: "Get a quote for 1 SOL → USDC"
|
|
65
|
+
→ x402_fetch("https://x402.dexter.cash/api/jupiter/quote?inputMint=So1...&outputMint=EPjF...&amount=1000000000")
|
|
66
|
+
→ Pays $0.05 USDC automatically, returns the quote
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## CLI
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Show wallet address and balance
|
|
73
|
+
npx @dexterai/mcp wallet
|
|
74
|
+
|
|
75
|
+
# Search the marketplace
|
|
76
|
+
npx @dexterai/mcp search "token analysis"
|
|
77
|
+
|
|
78
|
+
# Call an x402 endpoint (auto-pays)
|
|
79
|
+
npx @dexterai/mcp fetch "https://x402.dexter.cash/api/jupiter/quote?inputMint=So11...&amount=1000000000"
|
|
80
|
+
|
|
81
|
+
# Check pricing without paying
|
|
82
|
+
npx @dexterai/mcp fetch "https://x402.dexter.cash/api/v2-test" --method POST
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Wallet
|
|
86
|
+
|
|
87
|
+
The wallet is a standard Solana keypair stored at `~/.dexterai-mcp/wallet.json` with `600` permissions. You own the key — it never leaves your machine.
|
|
88
|
+
|
|
89
|
+
Override with environment variable:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
export DEXTER_PRIVATE_KEY="your-base58-private-key"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Multi-Chain
|
|
96
|
+
|
|
97
|
+
Supports Solana and EVM chains (Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE Europa) through the `@dexterai/x402` SDK. The MCP auto-detects which chain a 402 requires and uses the appropriate wallet.
|
|
98
|
+
|
|
99
|
+
## Links
|
|
100
|
+
|
|
101
|
+
- **Marketplace:** https://dexter.cash/marketplace
|
|
102
|
+
- **Facilitator:** https://x402.dexter.cash
|
|
103
|
+
- **SDK:** https://www.npmjs.com/package/@dexterai/x402
|
|
104
|
+
- **Twitter:** [@dexteraisol](https://twitter.com/dexteraisol)
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
10
|
+
var __esm = (fn, res) => function __init() {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
};
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/config.ts
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
function getApiBase(dev) {
|
|
22
|
+
return dev ? DEXTER_API_DEV : DEXTER_API_PROD;
|
|
23
|
+
}
|
|
24
|
+
var DATA_DIR, WALLET_FILE, DEXTER_API_PROD, DEXTER_API_DEV, MARKETPLACE_PATH, VERSION;
|
|
25
|
+
var init_config = __esm({
|
|
26
|
+
"src/config.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
DATA_DIR = join(homedir(), ".dexterai-mcp");
|
|
29
|
+
WALLET_FILE = join(DATA_DIR, "wallet.json");
|
|
30
|
+
DEXTER_API_PROD = "https://x402.dexter.cash";
|
|
31
|
+
DEXTER_API_DEV = "http://127.0.0.1:3030";
|
|
32
|
+
MARKETPLACE_PATH = "/api/facilitator/marketplace/resources";
|
|
33
|
+
VERSION = "0.1.0";
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// src/tools/search.ts
|
|
38
|
+
var search_exports = {};
|
|
39
|
+
__export(search_exports, {
|
|
40
|
+
cliSearch: () => cliSearch,
|
|
41
|
+
registerSearchTool: () => registerSearchTool
|
|
42
|
+
});
|
|
43
|
+
import { z } from "zod";
|
|
44
|
+
function formatResource(r) {
|
|
45
|
+
return {
|
|
46
|
+
name: r.displayName || r.resourceUrl,
|
|
47
|
+
url: r.resourceUrl,
|
|
48
|
+
method: r.method || "GET",
|
|
49
|
+
price: r.priceLabel || (r.priceUsdc != null ? `$${r.priceUsdc.toFixed(2)}` : "free"),
|
|
50
|
+
network: r.priceNetwork || null,
|
|
51
|
+
description: r.description || "",
|
|
52
|
+
category: r.category || "uncategorized",
|
|
53
|
+
qualityScore: r.qualityScore ?? null,
|
|
54
|
+
verified: r.verificationStatus === "pass",
|
|
55
|
+
totalCalls: r.totalSettlements ?? 0,
|
|
56
|
+
seller: r.seller?.displayName || null
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function searchMarketplace(params, opts) {
|
|
60
|
+
const qs = new URLSearchParams();
|
|
61
|
+
if (params.query) qs.set("search", params.query);
|
|
62
|
+
if (params.category) qs.set("category", params.category);
|
|
63
|
+
if (params.network) qs.set("network", params.network);
|
|
64
|
+
if (params.maxPriceUsdc != null) qs.set("maxPrice", String(params.maxPriceUsdc));
|
|
65
|
+
if (params.verifiedOnly) qs.set("verified", "true");
|
|
66
|
+
qs.set("sort", params.sort || "quality_score");
|
|
67
|
+
qs.set("order", "desc");
|
|
68
|
+
qs.set("limit", String(Math.min(params.limit || 20, 50)));
|
|
69
|
+
const url = `${getApiBase(opts.dev)}${MARKETPLACE_PATH}?${qs}`;
|
|
70
|
+
const res = await fetch(url, { headers: { Accept: "application/json" } });
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
throw new Error(`Marketplace returned ${res.status}: ${await res.text().catch(() => "")}`);
|
|
73
|
+
}
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
return {
|
|
76
|
+
resources: (data.resources || []).map(formatResource),
|
|
77
|
+
total: data.resources?.length || 0
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function registerSearchTool(server, opts) {
|
|
81
|
+
server.tool(
|
|
82
|
+
"x402_search",
|
|
83
|
+
"Search the Dexter x402 marketplace for paid API resources. Returns services with pricing, quality scores, and verification status. Use this to discover APIs an agent can pay for and call with x402_fetch.",
|
|
84
|
+
{
|
|
85
|
+
query: z.string().optional().describe("Search term, e.g. 'token analysis', 'image generation'"),
|
|
86
|
+
category: z.string().optional().describe("Filter by category"),
|
|
87
|
+
network: z.string().optional().describe("Filter by payment network: 'solana', 'base', 'polygon'"),
|
|
88
|
+
maxPriceUsdc: z.number().optional().describe("Maximum price per call in USDC"),
|
|
89
|
+
verifiedOnly: z.boolean().optional().describe("Only return verified endpoints"),
|
|
90
|
+
sort: z.enum(["relevance", "quality_score", "settlements", "volume", "recent"]).optional().describe("Sort order (default: quality_score)"),
|
|
91
|
+
limit: z.number().optional().default(20).describe("Max results (1-50)")
|
|
92
|
+
},
|
|
93
|
+
async (args) => {
|
|
94
|
+
try {
|
|
95
|
+
const result = await searchMarketplace(args, opts);
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: "text",
|
|
100
|
+
text: JSON.stringify(
|
|
101
|
+
{
|
|
102
|
+
success: true,
|
|
103
|
+
count: result.total,
|
|
104
|
+
resources: result.resources,
|
|
105
|
+
tip: "Use x402_fetch to call any of these endpoints."
|
|
106
|
+
},
|
|
107
|
+
null,
|
|
108
|
+
2
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
};
|
|
113
|
+
} catch (err) {
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text", text: JSON.stringify({ error: err.message }) }],
|
|
116
|
+
isError: true
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
async function cliSearch(query, opts) {
|
|
123
|
+
const result = await searchMarketplace({ query }, opts);
|
|
124
|
+
console.log(JSON.stringify({ success: true, count: result.total, resources: result.resources }, null, 2));
|
|
125
|
+
}
|
|
126
|
+
var init_search = __esm({
|
|
127
|
+
"src/tools/search.ts"() {
|
|
128
|
+
"use strict";
|
|
129
|
+
init_config();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// src/wallet/index.ts
|
|
134
|
+
var wallet_exports = {};
|
|
135
|
+
__export(wallet_exports, {
|
|
136
|
+
getSolanaBalance: () => getSolanaBalance,
|
|
137
|
+
loadOrCreateWallet: () => loadOrCreateWallet,
|
|
138
|
+
showWalletInfo: () => showWalletInfo
|
|
139
|
+
});
|
|
140
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
141
|
+
import { Keypair, Connection, PublicKey } from "@solana/web3.js";
|
|
142
|
+
import { getAssociatedTokenAddress } from "@solana/spl-token";
|
|
143
|
+
import bs58 from "bs58";
|
|
144
|
+
async function loadOrCreateWallet() {
|
|
145
|
+
const envKey = process.env.DEXTER_PRIVATE_KEY || process.env.SOLANA_PRIVATE_KEY;
|
|
146
|
+
if (envKey) {
|
|
147
|
+
const keypair2 = keypairFromString(envKey);
|
|
148
|
+
return {
|
|
149
|
+
info: {
|
|
150
|
+
solanaPrivateKey: bs58.encode(keypair2.secretKey),
|
|
151
|
+
solanaAddress: keypair2.publicKey.toBase58(),
|
|
152
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
153
|
+
},
|
|
154
|
+
solanaKeypair: keypair2
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (existsSync(WALLET_FILE)) {
|
|
158
|
+
try {
|
|
159
|
+
const raw = readFileSync(WALLET_FILE, "utf-8");
|
|
160
|
+
const data = JSON.parse(raw);
|
|
161
|
+
const keypair2 = keypairFromString(data.solanaPrivateKey);
|
|
162
|
+
return { info: data, solanaKeypair: keypair2 };
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error(`Failed to load wallet from ${WALLET_FILE}:`, err);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const keypair = Keypair.generate();
|
|
169
|
+
const info = {
|
|
170
|
+
solanaPrivateKey: bs58.encode(keypair.secretKey),
|
|
171
|
+
solanaAddress: keypair.publicKey.toBase58(),
|
|
172
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
173
|
+
};
|
|
174
|
+
mkdirSync(DATA_DIR, { recursive: true, mode: 448 });
|
|
175
|
+
writeFileSync(WALLET_FILE, JSON.stringify(info, null, 2), { mode: 384 });
|
|
176
|
+
console.error(`[dexter-mcp] New wallet created: ${info.solanaAddress}`);
|
|
177
|
+
console.error(`[dexter-mcp] Saved to ${WALLET_FILE}`);
|
|
178
|
+
console.error(`[dexter-mcp] Deposit USDC (Solana) to this address to start paying for x402 APIs.`);
|
|
179
|
+
return { info, solanaKeypair: keypair };
|
|
180
|
+
}
|
|
181
|
+
function keypairFromString(key) {
|
|
182
|
+
try {
|
|
183
|
+
return Keypair.fromSecretKey(bs58.decode(key));
|
|
184
|
+
} catch {
|
|
185
|
+
try {
|
|
186
|
+
const arr = JSON.parse(key);
|
|
187
|
+
if (Array.isArray(arr)) {
|
|
188
|
+
return Keypair.fromSecretKey(Uint8Array.from(arr));
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
throw new Error("Invalid private key format. Expected base58 string or JSON byte array.");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function getSolanaBalance(address, rpcUrl = "https://api.dexter.cash/api/solana/rpc") {
|
|
196
|
+
const connection = new Connection(rpcUrl, "confirmed");
|
|
197
|
+
const pubkey = new PublicKey(address);
|
|
198
|
+
const [solBalance, usdcBalance] = await Promise.all([
|
|
199
|
+
connection.getBalance(pubkey).catch(() => 0),
|
|
200
|
+
getUsdcBalance(connection, pubkey)
|
|
201
|
+
]);
|
|
202
|
+
return {
|
|
203
|
+
sol: solBalance / 1e9,
|
|
204
|
+
usdc: usdcBalance
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
async function getUsdcBalance(connection, owner) {
|
|
208
|
+
try {
|
|
209
|
+
const ata = await getAssociatedTokenAddress(USDC_MINT, owner);
|
|
210
|
+
const info = await connection.getTokenAccountBalance(ata);
|
|
211
|
+
return Number(info.value.uiAmount ?? 0);
|
|
212
|
+
} catch {
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function showWalletInfo(opts) {
|
|
217
|
+
const wallet = await loadOrCreateWallet();
|
|
218
|
+
if (!wallet) {
|
|
219
|
+
console.log(JSON.stringify({ error: "Failed to load wallet" }));
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
const balance = await getSolanaBalance(wallet.info.solanaAddress);
|
|
223
|
+
console.log(JSON.stringify({
|
|
224
|
+
address: wallet.info.solanaAddress,
|
|
225
|
+
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
226
|
+
balances: {
|
|
227
|
+
sol: balance.sol,
|
|
228
|
+
usdc: balance.usdc
|
|
229
|
+
},
|
|
230
|
+
walletFile: WALLET_FILE,
|
|
231
|
+
tip: balance.usdc === 0 ? `Deposit USDC (Solana) to ${wallet.info.solanaAddress} to start paying for x402 APIs.` : void 0
|
|
232
|
+
}, null, 2));
|
|
233
|
+
}
|
|
234
|
+
var USDC_MINT;
|
|
235
|
+
var init_wallet = __esm({
|
|
236
|
+
"src/wallet/index.ts"() {
|
|
237
|
+
"use strict";
|
|
238
|
+
init_config();
|
|
239
|
+
USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// src/tools/fetch.ts
|
|
244
|
+
var fetch_exports = {};
|
|
245
|
+
__export(fetch_exports, {
|
|
246
|
+
cliFetch: () => cliFetch,
|
|
247
|
+
registerFetchTool: () => registerFetchTool
|
|
248
|
+
});
|
|
249
|
+
import { z as z2 } from "zod";
|
|
250
|
+
async function parseResponse(res) {
|
|
251
|
+
const contentType = res.headers.get("content-type") || "";
|
|
252
|
+
if (contentType.includes("json")) {
|
|
253
|
+
try {
|
|
254
|
+
return await res.json();
|
|
255
|
+
} catch {
|
|
256
|
+
return await res.text();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return await res.text();
|
|
260
|
+
}
|
|
261
|
+
function extractSettlement(res) {
|
|
262
|
+
const header = res.headers.get("payment-response") || res.headers.get("PAYMENT-RESPONSE");
|
|
263
|
+
if (!header) return null;
|
|
264
|
+
try {
|
|
265
|
+
return JSON.parse(atob(header));
|
|
266
|
+
} catch {
|
|
267
|
+
try {
|
|
268
|
+
return JSON.parse(header);
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function parse402(body) {
|
|
275
|
+
const obj = body;
|
|
276
|
+
if (!obj?.accepts || !Array.isArray(obj.accepts)) return { requirements: null, firstAccept: null };
|
|
277
|
+
return {
|
|
278
|
+
requirements: { accepts: obj.accepts, x402Version: obj.x402Version ?? 2, resource: obj.resource },
|
|
279
|
+
firstAccept: obj.accepts[0] || null
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async function createQrSession(accept, resourceUrl, dev) {
|
|
283
|
+
const sessionRes = await fetch(`${getApiBase(dev)}/v2/pay/session`, {
|
|
284
|
+
method: "POST",
|
|
285
|
+
headers: { "Content-Type": "application/json" },
|
|
286
|
+
body: JSON.stringify({
|
|
287
|
+
payTo: accept.payTo,
|
|
288
|
+
amount: String(accept.amount || accept.maxAmountRequired),
|
|
289
|
+
asset: accept.asset,
|
|
290
|
+
feePayer: accept.extra?.feePayer || "",
|
|
291
|
+
resourceUrl
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
return await sessionRes.json();
|
|
295
|
+
}
|
|
296
|
+
async function x402Fetch(params, wallet, opts) {
|
|
297
|
+
const requestHeaders = {
|
|
298
|
+
"Content-Type": "application/json",
|
|
299
|
+
...params.headers || {}
|
|
300
|
+
};
|
|
301
|
+
const fetchOpts = {
|
|
302
|
+
method: params.method || "GET",
|
|
303
|
+
headers: requestHeaders
|
|
304
|
+
};
|
|
305
|
+
if (params.body && params.method !== "GET") {
|
|
306
|
+
fetchOpts.body = params.body;
|
|
307
|
+
}
|
|
308
|
+
const probeRes = await fetch(params.url, fetchOpts);
|
|
309
|
+
if (probeRes.status !== 402) {
|
|
310
|
+
return { status: probeRes.status, data: await parseResponse(probeRes) };
|
|
311
|
+
}
|
|
312
|
+
let body402 = null;
|
|
313
|
+
try {
|
|
314
|
+
body402 = await probeRes.json();
|
|
315
|
+
} catch {
|
|
316
|
+
try {
|
|
317
|
+
body402 = await probeRes.text();
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const { requirements, firstAccept } = parse402(body402);
|
|
322
|
+
if (wallet) {
|
|
323
|
+
try {
|
|
324
|
+
const { wrapFetch } = await import("@dexterai/x402/client");
|
|
325
|
+
const x402FetchFn = wrapFetch(fetch, {
|
|
326
|
+
walletPrivateKey: wallet.info.solanaPrivateKey
|
|
327
|
+
});
|
|
328
|
+
const paidRes = await x402FetchFn(params.url, fetchOpts);
|
|
329
|
+
const data = await parseResponse(paidRes);
|
|
330
|
+
const settlement = extractSettlement(paidRes);
|
|
331
|
+
return {
|
|
332
|
+
status: paidRes.status,
|
|
333
|
+
data,
|
|
334
|
+
payment: settlement ? { settled: true, details: settlement } : { settled: false }
|
|
335
|
+
};
|
|
336
|
+
} catch (err) {
|
|
337
|
+
return { status: 402, error: `Payment failed: ${err.message}`, requirements };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (firstAccept && String(firstAccept.network || "").startsWith("solana")) {
|
|
341
|
+
try {
|
|
342
|
+
const session = await createQrSession(firstAccept, params.url, opts.dev);
|
|
343
|
+
if (!session.ok) {
|
|
344
|
+
return { status: 402, error: "Failed to create payment session", requirements };
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
status: 402,
|
|
348
|
+
mode: "qr",
|
|
349
|
+
message: "Scan the QR code with Phantom or Solflare to pay, then this tool will automatically complete the request.",
|
|
350
|
+
qr: {
|
|
351
|
+
solanaPayUrl: session.solanaPayUrl,
|
|
352
|
+
nonce: session.nonce,
|
|
353
|
+
expiresAt: session.expiresAt
|
|
354
|
+
},
|
|
355
|
+
pollUrl: `${getApiBase(opts.dev)}/v2/pay/status/${session.nonce}`,
|
|
356
|
+
requirements
|
|
357
|
+
};
|
|
358
|
+
} catch {
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
status: 402,
|
|
363
|
+
message: "Payment required. Set DEXTER_PRIVATE_KEY for auto-pay, or use a Solana wallet to pay manually.",
|
|
364
|
+
requirements
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function registerFetchTool(server, wallet, opts) {
|
|
368
|
+
const hasWallet = wallet !== null;
|
|
369
|
+
server.tool(
|
|
370
|
+
"x402_fetch",
|
|
371
|
+
hasWallet ? "Call any x402-protected API with automatic payment. Signs and pays using your local wallet. Returns the API response directly." : "Call any x402-protected API. Returns payment requirements. Configure DEXTER_PRIVATE_KEY to enable automatic payment.",
|
|
372
|
+
{
|
|
373
|
+
url: z2.string().url().describe("The x402 resource URL to call"),
|
|
374
|
+
method: z2.enum(["GET", "POST", "PUT", "DELETE"]).default("GET").describe("HTTP method"),
|
|
375
|
+
body: z2.string().optional().describe("JSON request body for POST/PUT")
|
|
376
|
+
},
|
|
377
|
+
async (args) => {
|
|
378
|
+
try {
|
|
379
|
+
const result = await x402Fetch(
|
|
380
|
+
{ url: args.url, method: args.method, body: args.body },
|
|
381
|
+
wallet,
|
|
382
|
+
opts
|
|
383
|
+
);
|
|
384
|
+
return {
|
|
385
|
+
content: [
|
|
386
|
+
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
387
|
+
]
|
|
388
|
+
};
|
|
389
|
+
} catch (err) {
|
|
390
|
+
return {
|
|
391
|
+
content: [{ type: "text", text: JSON.stringify({ error: err.message }) }],
|
|
392
|
+
isError: true
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
async function cliFetch(url, opts) {
|
|
399
|
+
const { loadOrCreateWallet: loadOrCreateWallet2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
|
|
400
|
+
const wallet = await loadOrCreateWallet2();
|
|
401
|
+
const result = await x402Fetch(
|
|
402
|
+
{ url, method: opts.method, body: opts.body },
|
|
403
|
+
wallet,
|
|
404
|
+
opts
|
|
405
|
+
);
|
|
406
|
+
console.log(JSON.stringify(result, null, 2));
|
|
407
|
+
}
|
|
408
|
+
var init_fetch = __esm({
|
|
409
|
+
"src/tools/fetch.ts"() {
|
|
410
|
+
"use strict";
|
|
411
|
+
init_config();
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// src/tools/check.ts
|
|
416
|
+
import { z as z3 } from "zod";
|
|
417
|
+
async function checkEndpoint(url, method) {
|
|
418
|
+
const res = await fetch(url, {
|
|
419
|
+
method,
|
|
420
|
+
headers: { "Content-Type": "application/json" },
|
|
421
|
+
body: method !== "GET" ? "{}" : void 0
|
|
422
|
+
});
|
|
423
|
+
if (res.status !== 402) {
|
|
424
|
+
return {
|
|
425
|
+
requiresPayment: false,
|
|
426
|
+
statusCode: res.status,
|
|
427
|
+
free: true
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
let body = null;
|
|
431
|
+
try {
|
|
432
|
+
body = await res.json();
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
const accepts = body?.accepts;
|
|
436
|
+
if (!accepts?.length) {
|
|
437
|
+
return {
|
|
438
|
+
requiresPayment: true,
|
|
439
|
+
statusCode: 402,
|
|
440
|
+
error: "No payment options found in 402 response"
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
const paymentOptions = accepts.map((a) => {
|
|
444
|
+
const amount = Number(a.amount || a.maxAmountRequired || 0);
|
|
445
|
+
const decimals = Number(a.extra && typeof a.extra === "object" && "decimals" in a.extra ? a.extra.decimals : 6);
|
|
446
|
+
return {
|
|
447
|
+
price: amount / Math.pow(10, decimals),
|
|
448
|
+
priceFormatted: `$${(amount / Math.pow(10, decimals)).toFixed(decimals > 2 ? 4 : 2)}`,
|
|
449
|
+
network: a.network,
|
|
450
|
+
scheme: a.scheme,
|
|
451
|
+
asset: a.asset,
|
|
452
|
+
payTo: a.payTo
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
return {
|
|
456
|
+
requiresPayment: true,
|
|
457
|
+
statusCode: 402,
|
|
458
|
+
x402Version: body?.x402Version ?? 2,
|
|
459
|
+
paymentOptions,
|
|
460
|
+
resource: body?.resource
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function registerCheckTool(server, opts) {
|
|
464
|
+
server.tool(
|
|
465
|
+
"x402_check",
|
|
466
|
+
"Check if an endpoint requires x402 payment and see its pricing. Does NOT make a payment \u2014 just probes for requirements.",
|
|
467
|
+
{
|
|
468
|
+
url: z3.string().url().describe("The URL to check"),
|
|
469
|
+
method: z3.enum(["GET", "POST", "PUT", "DELETE"]).default("GET").describe("HTTP method to probe with")
|
|
470
|
+
},
|
|
471
|
+
async (args) => {
|
|
472
|
+
try {
|
|
473
|
+
const result = await checkEndpoint(args.url, args.method);
|
|
474
|
+
return {
|
|
475
|
+
content: [
|
|
476
|
+
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
477
|
+
]
|
|
478
|
+
};
|
|
479
|
+
} catch (err) {
|
|
480
|
+
return {
|
|
481
|
+
content: [{ type: "text", text: JSON.stringify({ error: err.message }) }],
|
|
482
|
+
isError: true
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
var init_check = __esm({
|
|
489
|
+
"src/tools/check.ts"() {
|
|
490
|
+
"use strict";
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// src/tools/wallet-tool.ts
|
|
495
|
+
function registerWalletTool(server, wallet, opts) {
|
|
496
|
+
server.tool(
|
|
497
|
+
"x402_wallet",
|
|
498
|
+
"Show wallet address, USDC balance, and deposit instructions. The wallet is used to automatically pay for x402 API calls.",
|
|
499
|
+
{},
|
|
500
|
+
async () => {
|
|
501
|
+
if (!wallet) {
|
|
502
|
+
return {
|
|
503
|
+
content: [
|
|
504
|
+
{
|
|
505
|
+
type: "text",
|
|
506
|
+
text: JSON.stringify({
|
|
507
|
+
error: "No wallet configured",
|
|
508
|
+
tip: "Set DEXTER_PRIVATE_KEY env var or run `npx @dexterai/mcp wallet` to create one."
|
|
509
|
+
}, null, 2)
|
|
510
|
+
}
|
|
511
|
+
]
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
const balance = await getSolanaBalance(wallet.info.solanaAddress);
|
|
516
|
+
return {
|
|
517
|
+
content: [
|
|
518
|
+
{
|
|
519
|
+
type: "text",
|
|
520
|
+
text: JSON.stringify(
|
|
521
|
+
{
|
|
522
|
+
address: wallet.info.solanaAddress,
|
|
523
|
+
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
524
|
+
networkName: "Solana Mainnet",
|
|
525
|
+
balances: {
|
|
526
|
+
sol: balance.sol,
|
|
527
|
+
usdc: balance.usdc
|
|
528
|
+
},
|
|
529
|
+
walletFile: WALLET_FILE,
|
|
530
|
+
tip: balance.usdc === 0 ? `Deposit USDC (Solana) to ${wallet.info.solanaAddress} to start paying for x402 APIs.` : void 0
|
|
531
|
+
},
|
|
532
|
+
null,
|
|
533
|
+
2
|
|
534
|
+
)
|
|
535
|
+
}
|
|
536
|
+
]
|
|
537
|
+
};
|
|
538
|
+
} catch (err) {
|
|
539
|
+
return {
|
|
540
|
+
content: [
|
|
541
|
+
{ type: "text", text: JSON.stringify({ error: err.message }) }
|
|
542
|
+
],
|
|
543
|
+
isError: true
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
var init_wallet_tool = __esm({
|
|
550
|
+
"src/tools/wallet-tool.ts"() {
|
|
551
|
+
"use strict";
|
|
552
|
+
init_wallet();
|
|
553
|
+
init_config();
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// src/server/index.ts
|
|
558
|
+
var server_exports = {};
|
|
559
|
+
__export(server_exports, {
|
|
560
|
+
startServer: () => startServer
|
|
561
|
+
});
|
|
562
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
563
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
564
|
+
async function startServer(opts) {
|
|
565
|
+
const wallet = await loadOrCreateWallet();
|
|
566
|
+
const server = new McpServer({
|
|
567
|
+
name: "Dexter x402 Gateway",
|
|
568
|
+
version: VERSION
|
|
569
|
+
});
|
|
570
|
+
registerSearchTool(server, opts);
|
|
571
|
+
registerFetchTool(server, wallet, opts);
|
|
572
|
+
registerCheckTool(server, opts);
|
|
573
|
+
registerWalletTool(server, wallet, opts);
|
|
574
|
+
if (opts.transport === "stdio") {
|
|
575
|
+
const transport = new StdioServerTransport();
|
|
576
|
+
await server.connect(transport);
|
|
577
|
+
process.on("SIGINT", async () => {
|
|
578
|
+
await server.close();
|
|
579
|
+
process.exit(0);
|
|
580
|
+
});
|
|
581
|
+
process.on("SIGTERM", async () => {
|
|
582
|
+
await server.close();
|
|
583
|
+
process.exit(0);
|
|
584
|
+
});
|
|
585
|
+
} else {
|
|
586
|
+
console.error("HTTP transport not yet implemented. Use --transport=stdio");
|
|
587
|
+
process.exit(1);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
var init_server = __esm({
|
|
591
|
+
"src/server/index.ts"() {
|
|
592
|
+
"use strict";
|
|
593
|
+
init_config();
|
|
594
|
+
init_search();
|
|
595
|
+
init_fetch();
|
|
596
|
+
init_check();
|
|
597
|
+
init_wallet_tool();
|
|
598
|
+
init_wallet();
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// src/cli/install/clients.ts
|
|
603
|
+
import { homedir as homedir2 } from "os";
|
|
604
|
+
import { join as join2 } from "path";
|
|
605
|
+
function getConfigDir() {
|
|
606
|
+
const platform = process.platform;
|
|
607
|
+
if (platform === "win32") {
|
|
608
|
+
return process.env.APPDATA || join2(homedir2(), "AppData", "Roaming");
|
|
609
|
+
}
|
|
610
|
+
if (platform === "darwin") {
|
|
611
|
+
return join2(homedir2(), "Library", "Application Support");
|
|
612
|
+
}
|
|
613
|
+
return process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
|
|
614
|
+
}
|
|
615
|
+
function getClientConfig(client, dev) {
|
|
616
|
+
const cmd = dev ? SERVER_CMD_DEV : SERVER_CMD;
|
|
617
|
+
switch (client) {
|
|
618
|
+
case "cursor":
|
|
619
|
+
return {
|
|
620
|
+
configPath: join2(homedir2(), ".cursor", "mcp.json"),
|
|
621
|
+
sectionKey: "mcpServers",
|
|
622
|
+
entry: cmd
|
|
623
|
+
};
|
|
624
|
+
case "claude-code":
|
|
625
|
+
return {
|
|
626
|
+
configPath: join2(homedir2(), ".claude.json"),
|
|
627
|
+
sectionKey: "mcpServers",
|
|
628
|
+
entry: cmd
|
|
629
|
+
};
|
|
630
|
+
case "codex": {
|
|
631
|
+
const codexHome = process.env.CODEX_HOME || join2(homedir2(), ".codex");
|
|
632
|
+
return {
|
|
633
|
+
configPath: join2(codexHome, "config.toml"),
|
|
634
|
+
sectionKey: "mcp_servers",
|
|
635
|
+
entry: cmd,
|
|
636
|
+
manual: true
|
|
637
|
+
// TOML requires different handling
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
case "vscode": {
|
|
641
|
+
const configDir = getConfigDir();
|
|
642
|
+
const vscodeDirs = ["Code", "Code - Insiders"];
|
|
643
|
+
const dir = vscodeDirs.find((d) => {
|
|
644
|
+
try {
|
|
645
|
+
return __require("fs").existsSync(join2(configDir, d));
|
|
646
|
+
} catch {
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
}) || "Code";
|
|
650
|
+
return {
|
|
651
|
+
configPath: join2(configDir, dir, "User", "mcp.json"),
|
|
652
|
+
sectionKey: "mcpServers",
|
|
653
|
+
entry: cmd
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
case "windsurf":
|
|
657
|
+
return {
|
|
658
|
+
configPath: join2(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
|
|
659
|
+
sectionKey: "mcpServers",
|
|
660
|
+
entry: cmd
|
|
661
|
+
};
|
|
662
|
+
case "gemini-cli":
|
|
663
|
+
return {
|
|
664
|
+
configPath: join2(homedir2(), ".gemini", "settings.json"),
|
|
665
|
+
sectionKey: "mcpServers",
|
|
666
|
+
entry: cmd
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
var CLIENTS, SERVER_CMD, SERVER_CMD_DEV;
|
|
671
|
+
var init_clients = __esm({
|
|
672
|
+
"src/cli/install/clients.ts"() {
|
|
673
|
+
"use strict";
|
|
674
|
+
CLIENTS = {
|
|
675
|
+
cursor: {
|
|
676
|
+
name: "Cursor",
|
|
677
|
+
description: "Cursor AI code editor"
|
|
678
|
+
},
|
|
679
|
+
"claude-code": {
|
|
680
|
+
name: "Claude Code",
|
|
681
|
+
description: "Anthropic Claude Code CLI"
|
|
682
|
+
},
|
|
683
|
+
codex: {
|
|
684
|
+
name: "Codex",
|
|
685
|
+
description: "OpenAI Codex CLI"
|
|
686
|
+
},
|
|
687
|
+
vscode: {
|
|
688
|
+
name: "VS Code",
|
|
689
|
+
description: "Visual Studio Code with MCP support"
|
|
690
|
+
},
|
|
691
|
+
windsurf: {
|
|
692
|
+
name: "Windsurf",
|
|
693
|
+
description: "Codeium Windsurf editor"
|
|
694
|
+
},
|
|
695
|
+
"gemini-cli": {
|
|
696
|
+
name: "Gemini CLI",
|
|
697
|
+
description: "Google Gemini CLI"
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
SERVER_CMD = {
|
|
701
|
+
command: "npx",
|
|
702
|
+
args: ["-y", "@dexterai/mcp@latest"]
|
|
703
|
+
};
|
|
704
|
+
SERVER_CMD_DEV = {
|
|
705
|
+
command: "node",
|
|
706
|
+
args: [process.cwd() + "/dist/index.js", "--dev"]
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// src/cli/install/index.ts
|
|
712
|
+
var install_exports = {};
|
|
713
|
+
__export(install_exports, {
|
|
714
|
+
runInstall: () => runInstall
|
|
715
|
+
});
|
|
716
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
717
|
+
import { dirname } from "path";
|
|
718
|
+
async function runInstall(opts) {
|
|
719
|
+
console.log("Setting up wallet...");
|
|
720
|
+
const wallet = await loadOrCreateWallet();
|
|
721
|
+
if (!wallet) {
|
|
722
|
+
console.error("Failed to create wallet. Exiting.");
|
|
723
|
+
process.exit(1);
|
|
724
|
+
}
|
|
725
|
+
console.log(`Wallet: ${wallet.info.solanaAddress}
|
|
726
|
+
`);
|
|
727
|
+
let clientId = opts.client;
|
|
728
|
+
if (!clientId) {
|
|
729
|
+
if (opts.yes) {
|
|
730
|
+
console.error("--client is required when using --yes");
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
console.log("Select an AI client to install into:\n");
|
|
734
|
+
const ids = Object.keys(CLIENTS);
|
|
735
|
+
ids.forEach((id, i) => console.log(` ${i + 1}. ${CLIENTS[id].name}`));
|
|
736
|
+
console.log();
|
|
737
|
+
const readline = await import("readline");
|
|
738
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
739
|
+
const answer = await new Promise((resolve) => {
|
|
740
|
+
rl.question("Choice (number): ", resolve);
|
|
741
|
+
});
|
|
742
|
+
rl.close();
|
|
743
|
+
const idx = parseInt(answer, 10) - 1;
|
|
744
|
+
if (idx < 0 || idx >= ids.length) {
|
|
745
|
+
console.error("Invalid choice.");
|
|
746
|
+
process.exit(1);
|
|
747
|
+
}
|
|
748
|
+
clientId = ids[idx];
|
|
749
|
+
}
|
|
750
|
+
if (!CLIENTS[clientId]) {
|
|
751
|
+
console.error(`Unknown client: ${clientId}`);
|
|
752
|
+
console.error(`Available: ${Object.keys(CLIENTS).join(", ")}`);
|
|
753
|
+
process.exit(1);
|
|
754
|
+
}
|
|
755
|
+
const config = getClientConfig(clientId, opts.dev);
|
|
756
|
+
if (config.manual) {
|
|
757
|
+
console.log(`
|
|
758
|
+
${CLIENTS[clientId].name} requires manual configuration.
|
|
759
|
+
`);
|
|
760
|
+
console.log("Add this to your MCP config:\n");
|
|
761
|
+
console.log(JSON.stringify(config.entry, null, 2));
|
|
762
|
+
console.log(`
|
|
763
|
+
Config file: ${config.configPath}`);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
console.log(`
|
|
767
|
+
Installing into ${CLIENTS[clientId].name}...`);
|
|
768
|
+
mkdirSync2(dirname(config.configPath), { recursive: true });
|
|
769
|
+
let existing = {};
|
|
770
|
+
if (existsSync2(config.configPath)) {
|
|
771
|
+
try {
|
|
772
|
+
existing = JSON.parse(readFileSync2(config.configPath, "utf-8"));
|
|
773
|
+
} catch {
|
|
774
|
+
existing = {};
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
const section = existing[config.sectionKey] || {};
|
|
778
|
+
section["dexter-x402"] = config.entry;
|
|
779
|
+
existing[config.sectionKey] = section;
|
|
780
|
+
writeFileSync2(config.configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
781
|
+
console.log(`Written to ${config.configPath}`);
|
|
782
|
+
console.log(`
|
|
783
|
+
Dexter x402 Gateway installed for ${CLIENTS[clientId].name}.`);
|
|
784
|
+
console.log(`Wallet: ${wallet.info.solanaAddress}`);
|
|
785
|
+
console.log(`
|
|
786
|
+
Deposit USDC (Solana) to start paying for x402 APIs.`);
|
|
787
|
+
}
|
|
788
|
+
var init_install = __esm({
|
|
789
|
+
"src/cli/install/index.ts"() {
|
|
790
|
+
"use strict";
|
|
791
|
+
init_wallet();
|
|
792
|
+
init_clients();
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// src/index.ts
|
|
797
|
+
import yargs from "yargs";
|
|
798
|
+
import { hideBin } from "yargs/helpers";
|
|
799
|
+
void yargs(hideBin(process.argv)).scriptName("@dexterai/mcp").usage("$0 [command] [options]").option("dev", {
|
|
800
|
+
type: "boolean",
|
|
801
|
+
description: "Use localhost endpoints instead of production",
|
|
802
|
+
default: false
|
|
803
|
+
}).command(
|
|
804
|
+
["$0", "server"],
|
|
805
|
+
"Start the MCP server (default)",
|
|
806
|
+
(y) => y.option("transport", {
|
|
807
|
+
choices: ["stdio", "http"],
|
|
808
|
+
default: "stdio",
|
|
809
|
+
description: "Transport mode"
|
|
810
|
+
}),
|
|
811
|
+
async (args) => {
|
|
812
|
+
const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
813
|
+
await startServer2({
|
|
814
|
+
transport: args.transport,
|
|
815
|
+
dev: args.dev
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
).command(
|
|
819
|
+
"install",
|
|
820
|
+
"Install Dexter MCP into an AI client (Cursor, Claude, Codex, etc.)",
|
|
821
|
+
(y) => y.option("client", {
|
|
822
|
+
type: "string",
|
|
823
|
+
description: "Client to install into"
|
|
824
|
+
}).option("yes", {
|
|
825
|
+
alias: "y",
|
|
826
|
+
type: "boolean",
|
|
827
|
+
description: "Skip prompts",
|
|
828
|
+
default: false
|
|
829
|
+
}),
|
|
830
|
+
async (args) => {
|
|
831
|
+
const { runInstall: runInstall2 } = await Promise.resolve().then(() => (init_install(), install_exports));
|
|
832
|
+
await runInstall2({ client: args.client, yes: args.yes, dev: args.dev });
|
|
833
|
+
}
|
|
834
|
+
).command(
|
|
835
|
+
"wallet",
|
|
836
|
+
"Show wallet address and balances",
|
|
837
|
+
() => {
|
|
838
|
+
},
|
|
839
|
+
async (args) => {
|
|
840
|
+
const { showWalletInfo: showWalletInfo2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
|
|
841
|
+
await showWalletInfo2({ dev: args.dev });
|
|
842
|
+
}
|
|
843
|
+
).command(
|
|
844
|
+
"search <query>",
|
|
845
|
+
"Search the Dexter x402 marketplace",
|
|
846
|
+
(y) => y.positional("query", { type: "string", demandOption: true }),
|
|
847
|
+
async (args) => {
|
|
848
|
+
const { cliSearch: cliSearch2 } = await Promise.resolve().then(() => (init_search(), search_exports));
|
|
849
|
+
await cliSearch2(args.query, { dev: args.dev });
|
|
850
|
+
}
|
|
851
|
+
).command(
|
|
852
|
+
"fetch <url>",
|
|
853
|
+
"Fetch an x402-protected resource with automatic payment",
|
|
854
|
+
(y) => y.positional("url", { type: "string", demandOption: true }).option("method", {
|
|
855
|
+
choices: ["GET", "POST", "PUT", "DELETE"],
|
|
856
|
+
default: "GET"
|
|
857
|
+
}).option("body", { type: "string", description: "JSON request body" }),
|
|
858
|
+
async (args) => {
|
|
859
|
+
const { cliFetch: cliFetch2 } = await Promise.resolve().then(() => (init_fetch(), fetch_exports));
|
|
860
|
+
await cliFetch2(args.url, {
|
|
861
|
+
method: args.method,
|
|
862
|
+
body: args.body,
|
|
863
|
+
dev: args.dev
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
).strict().help().parse();
|
|
867
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/tools/search.ts","../src/wallet/index.ts","../src/tools/fetch.ts","../src/tools/check.ts","../src/tools/wallet-tool.ts","../src/server/index.ts","../src/cli/install/clients.ts","../src/cli/install/index.ts","../src/index.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const DATA_DIR = join(homedir(), \".dexterai-mcp\");\nexport const WALLET_FILE = join(DATA_DIR, \"wallet.json\");\n\nexport const DEXTER_API_PROD = \"https://x402.dexter.cash\";\nexport const DEXTER_API_DEV = \"http://127.0.0.1:3030\";\n\nexport const MARKETPLACE_PATH = \"/api/facilitator/marketplace/resources\";\n\nexport function getApiBase(dev: boolean): string {\n return dev ? DEXTER_API_DEV : DEXTER_API_PROD;\n}\n\nexport const VERSION = \"0.1.0\";\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getApiBase, MARKETPLACE_PATH } from \"../config.js\";\n\ninterface SearchOpts {\n dev: boolean;\n}\n\ninterface MarketplaceResource {\n resourceUrl: string;\n displayName?: string;\n description?: string | null;\n method?: string;\n priceUsdc?: number | null;\n priceLabel?: string | null;\n priceNetwork?: string | null;\n qualityScore?: number | null;\n verificationStatus?: string | null;\n totalSettlements?: number;\n totalVolumeUsdc?: number;\n category?: string | null;\n seller?: { displayName?: string | null };\n reputationScore?: number | null;\n}\n\nfunction formatResource(r: MarketplaceResource) {\n return {\n name: r.displayName || r.resourceUrl,\n url: r.resourceUrl,\n method: r.method || \"GET\",\n price: r.priceLabel || (r.priceUsdc != null ? `$${r.priceUsdc.toFixed(2)}` : \"free\"),\n network: r.priceNetwork || null,\n description: r.description || \"\",\n category: r.category || \"uncategorized\",\n qualityScore: r.qualityScore ?? null,\n verified: r.verificationStatus === \"pass\",\n totalCalls: r.totalSettlements ?? 0,\n seller: r.seller?.displayName || null,\n };\n}\n\nasync function searchMarketplace(\n params: {\n query?: string;\n category?: string;\n network?: string;\n maxPriceUsdc?: number;\n verifiedOnly?: boolean;\n sort?: string;\n limit?: number;\n },\n opts: SearchOpts,\n) {\n const qs = new URLSearchParams();\n if (params.query) qs.set(\"search\", params.query);\n if (params.category) qs.set(\"category\", params.category);\n if (params.network) qs.set(\"network\", params.network);\n if (params.maxPriceUsdc != null) qs.set(\"maxPrice\", String(params.maxPriceUsdc));\n if (params.verifiedOnly) qs.set(\"verified\", \"true\");\n qs.set(\"sort\", params.sort || \"quality_score\");\n qs.set(\"order\", \"desc\");\n qs.set(\"limit\", String(Math.min(params.limit || 20, 50)));\n\n const url = `${getApiBase(opts.dev)}${MARKETPLACE_PATH}?${qs}`;\n const res = await fetch(url, { headers: { Accept: \"application/json\" } });\n\n if (!res.ok) {\n throw new Error(`Marketplace returned ${res.status}: ${await res.text().catch(() => \"\")}`);\n }\n\n const data = await res.json() as { resources?: MarketplaceResource[] };\n return {\n resources: (data.resources || []).map(formatResource),\n total: data.resources?.length || 0,\n };\n}\n\nexport function registerSearchTool(server: McpServer, opts: SearchOpts): void {\n server.tool(\n \"x402_search\",\n \"Search the Dexter x402 marketplace for paid API resources. \" +\n \"Returns services with pricing, quality scores, and verification status. \" +\n \"Use this to discover APIs an agent can pay for and call with x402_fetch.\",\n {\n query: z.string().optional().describe(\"Search term, e.g. 'token analysis', 'image generation'\"),\n category: z.string().optional().describe(\"Filter by category\"),\n network: z.string().optional().describe(\"Filter by payment network: 'solana', 'base', 'polygon'\"),\n maxPriceUsdc: z.number().optional().describe(\"Maximum price per call in USDC\"),\n verifiedOnly: z.boolean().optional().describe(\"Only return verified endpoints\"),\n sort: z\n .enum([\"relevance\", \"quality_score\", \"settlements\", \"volume\", \"recent\"])\n .optional()\n .describe(\"Sort order (default: quality_score)\"),\n limit: z.number().optional().default(20).describe(\"Max results (1-50)\"),\n },\n async (args) => {\n try {\n const result = await searchMarketplace(args, opts);\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify(\n {\n success: true,\n count: result.total,\n resources: result.resources,\n tip: \"Use x402_fetch to call any of these endpoints.\",\n },\n null,\n 2,\n ),\n },\n ],\n };\n } catch (err: any) {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify({ error: err.message }) }],\n isError: true,\n };\n }\n },\n );\n}\n\nexport async function cliSearch(query: string, opts: { dev: boolean }): Promise<void> {\n const result = await searchMarketplace({ query }, opts);\n console.log(JSON.stringify({ success: true, count: result.total, resources: result.resources }, null, 2));\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { Keypair, Connection, PublicKey } from \"@solana/web3.js\";\nimport { getAssociatedTokenAddress } from \"@solana/spl-token\";\nimport bs58 from \"bs58\";\nimport { DATA_DIR, WALLET_FILE, getApiBase } from \"../config.js\";\n\nexport interface WalletInfo {\n solanaPrivateKey: string;\n solanaAddress: string;\n evmPrivateKey?: string;\n evmAddress?: string;\n createdAt: string;\n}\n\nexport interface LoadedWallet {\n info: WalletInfo;\n solanaKeypair: Keypair;\n}\n\nconst USDC_MINT = new PublicKey(\"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\");\n\nexport async function loadOrCreateWallet(): Promise<LoadedWallet | null> {\n // Env var override takes priority\n const envKey = process.env.DEXTER_PRIVATE_KEY || process.env.SOLANA_PRIVATE_KEY;\n if (envKey) {\n const keypair = keypairFromString(envKey);\n return {\n info: {\n solanaPrivateKey: bs58.encode(keypair.secretKey),\n solanaAddress: keypair.publicKey.toBase58(),\n createdAt: new Date().toISOString(),\n },\n solanaKeypair: keypair,\n };\n }\n\n if (existsSync(WALLET_FILE)) {\n try {\n const raw = readFileSync(WALLET_FILE, \"utf-8\");\n const data = JSON.parse(raw) as WalletInfo;\n const keypair = keypairFromString(data.solanaPrivateKey);\n return { info: data, solanaKeypair: keypair };\n } catch (err) {\n console.error(`Failed to load wallet from ${WALLET_FILE}:`, err);\n return null;\n }\n }\n\n // Generate new wallet\n const keypair = Keypair.generate();\n const info: WalletInfo = {\n solanaPrivateKey: bs58.encode(keypair.secretKey),\n solanaAddress: keypair.publicKey.toBase58(),\n createdAt: new Date().toISOString(),\n };\n\n mkdirSync(DATA_DIR, { recursive: true, mode: 0o700 });\n writeFileSync(WALLET_FILE, JSON.stringify(info, null, 2), { mode: 0o600 });\n\n console.error(`[dexter-mcp] New wallet created: ${info.solanaAddress}`);\n console.error(`[dexter-mcp] Saved to ${WALLET_FILE}`);\n console.error(`[dexter-mcp] Deposit USDC (Solana) to this address to start paying for x402 APIs.`);\n\n return { info, solanaKeypair: keypair };\n}\n\nfunction keypairFromString(key: string): Keypair {\n try {\n // Try base58\n return Keypair.fromSecretKey(bs58.decode(key));\n } catch {\n // Try JSON array\n try {\n const arr = JSON.parse(key);\n if (Array.isArray(arr)) {\n return Keypair.fromSecretKey(Uint8Array.from(arr));\n }\n } catch {}\n throw new Error(\"Invalid private key format. Expected base58 string or JSON byte array.\");\n }\n}\n\nexport async function getSolanaBalance(\n address: string,\n rpcUrl = \"https://api.dexter.cash/api/solana/rpc\",\n): Promise<{ sol: number; usdc: number }> {\n const connection = new Connection(rpcUrl, \"confirmed\");\n const pubkey = new PublicKey(address);\n\n const [solBalance, usdcBalance] = await Promise.all([\n connection.getBalance(pubkey).catch(() => 0),\n getUsdcBalance(connection, pubkey),\n ]);\n\n return {\n sol: solBalance / 1e9,\n usdc: usdcBalance,\n };\n}\n\nasync function getUsdcBalance(connection: Connection, owner: PublicKey): Promise<number> {\n try {\n const ata = await getAssociatedTokenAddress(USDC_MINT, owner);\n const info = await connection.getTokenAccountBalance(ata);\n return Number(info.value.uiAmount ?? 0);\n } catch {\n return 0;\n }\n}\n\nexport async function showWalletInfo(opts: { dev: boolean }): Promise<void> {\n const wallet = await loadOrCreateWallet();\n if (!wallet) {\n console.log(JSON.stringify({ error: \"Failed to load wallet\" }));\n process.exit(1);\n }\n\n const balance = await getSolanaBalance(wallet.info.solanaAddress);\n\n console.log(JSON.stringify({\n address: wallet.info.solanaAddress,\n network: \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\",\n balances: {\n sol: balance.sol,\n usdc: balance.usdc,\n },\n walletFile: WALLET_FILE,\n tip: balance.usdc === 0\n ? `Deposit USDC (Solana) to ${wallet.info.solanaAddress} to start paying for x402 APIs.`\n : undefined,\n }, null, 2));\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { LoadedWallet } from \"../wallet/index.js\";\nimport { getApiBase } from \"../config.js\";\n\ninterface FetchOpts {\n dev: boolean;\n}\n\nasync function parseResponse(res: Response): Promise<unknown> {\n const contentType = res.headers.get(\"content-type\") || \"\";\n if (contentType.includes(\"json\")) {\n try { return await res.json(); } catch { return await res.text(); }\n }\n return await res.text();\n}\n\nfunction extractSettlement(res: Response): unknown {\n const header = res.headers.get(\"payment-response\") || res.headers.get(\"PAYMENT-RESPONSE\");\n if (!header) return null;\n try { return JSON.parse(atob(header)); } catch {\n try { return JSON.parse(header); } catch { return null; }\n }\n}\n\nfunction parse402(body: unknown): { requirements: Record<string, unknown> | null; firstAccept: Record<string, unknown> | null } {\n const obj = body as Record<string, unknown> | null;\n if (!obj?.accepts || !Array.isArray(obj.accepts)) return { requirements: null, firstAccept: null };\n return {\n requirements: { accepts: obj.accepts, x402Version: obj.x402Version ?? 2, resource: obj.resource },\n firstAccept: obj.accepts[0] as Record<string, unknown> || null,\n };\n}\n\nasync function createQrSession(\n accept: Record<string, unknown>,\n resourceUrl: string,\n dev: boolean,\n): Promise<Record<string, unknown>> {\n const sessionRes = await fetch(`${getApiBase(dev)}/v2/pay/session`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n payTo: accept.payTo,\n amount: String(accept.amount || accept.maxAmountRequired),\n asset: accept.asset,\n feePayer: (accept.extra as Record<string, unknown>)?.feePayer || \"\",\n resourceUrl,\n }),\n });\n return await sessionRes.json() as Record<string, unknown>;\n}\n\nasync function pollSessionStatus(nonce: string, dev: boolean, maxAttempts = 60): Promise<Record<string, unknown>> {\n for (let i = 0; i < maxAttempts; i++) {\n await new Promise((r) => setTimeout(r, 2000));\n const res = await fetch(`${getApiBase(dev)}/v2/pay/status/${nonce}`);\n const status = await res.json() as Record<string, unknown>;\n if (status.state === \"paid\" || status.state === \"expired\") return status;\n }\n return { state: \"timeout\" };\n}\n\nasync function x402Fetch(\n params: { url: string; method: string; body?: string; headers?: Record<string, string> },\n wallet: LoadedWallet | null,\n opts: FetchOpts,\n): Promise<Record<string, unknown>> {\n const requestHeaders: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...(params.headers || {}),\n };\n const fetchOpts: RequestInit = {\n method: params.method || \"GET\",\n headers: requestHeaders,\n };\n if (params.body && params.method !== \"GET\") {\n fetchOpts.body = params.body;\n }\n\n const probeRes = await fetch(params.url, fetchOpts);\n\n if (probeRes.status !== 402) {\n return { status: probeRes.status, data: await parseResponse(probeRes) };\n }\n\n let body402: unknown = null;\n try { body402 = await probeRes.json(); } catch {\n try { body402 = await probeRes.text(); } catch {}\n }\n\n const { requirements, firstAccept } = parse402(body402);\n\n // Mode 1: Local wallet auto-pay\n if (wallet) {\n try {\n const { wrapFetch } = await import(\"@dexterai/x402/client\");\n const x402FetchFn = wrapFetch(fetch, {\n walletPrivateKey: wallet.info.solanaPrivateKey,\n });\n\n const paidRes = await x402FetchFn(params.url, fetchOpts);\n const data = await parseResponse(paidRes);\n const settlement = extractSettlement(paidRes);\n\n return {\n status: paidRes.status,\n data,\n payment: settlement ? { settled: true, details: settlement } : { settled: false },\n };\n } catch (err: any) {\n return { status: 402, error: `Payment failed: ${err.message}`, requirements };\n }\n }\n\n // Mode 2: QR pay (no local wallet)\n if (firstAccept && String(firstAccept.network || \"\").startsWith(\"solana\")) {\n try {\n const session = await createQrSession(firstAccept, params.url, opts.dev);\n\n if (!session.ok) {\n return { status: 402, error: \"Failed to create payment session\", requirements };\n }\n\n return {\n status: 402,\n mode: \"qr\",\n message: \"Scan the QR code with Phantom or Solflare to pay, then this tool will automatically complete the request.\",\n qr: {\n solanaPayUrl: session.solanaPayUrl,\n nonce: session.nonce,\n expiresAt: session.expiresAt,\n },\n pollUrl: `${getApiBase(opts.dev)}/v2/pay/status/${session.nonce}`,\n requirements,\n };\n } catch {\n // Fall through to manual mode\n }\n }\n\n // Fallback: return raw requirements\n return {\n status: 402,\n message: \"Payment required. Set DEXTER_PRIVATE_KEY for auto-pay, or use a Solana wallet to pay manually.\",\n requirements,\n };\n}\n\nexport function registerFetchTool(\n server: McpServer,\n wallet: LoadedWallet | null,\n opts: FetchOpts,\n): void {\n const hasWallet = wallet !== null;\n\n server.tool(\n \"x402_fetch\",\n hasWallet\n ? \"Call any x402-protected API with automatic payment. \" +\n \"Signs and pays using your local wallet. Returns the API response directly.\"\n : \"Call any x402-protected API. Returns payment requirements. \" +\n \"Configure DEXTER_PRIVATE_KEY to enable automatic payment.\",\n {\n url: z.string().url().describe(\"The x402 resource URL to call\"),\n method: z\n .enum([\"GET\", \"POST\", \"PUT\", \"DELETE\"])\n .default(\"GET\")\n .describe(\"HTTP method\"),\n body: z.string().optional().describe(\"JSON request body for POST/PUT\"),\n },\n async (args) => {\n try {\n const result = await x402Fetch(\n { url: args.url, method: args.method, body: args.body },\n wallet,\n opts,\n );\n return {\n content: [\n { type: \"text\" as const, text: JSON.stringify(result, null, 2) },\n ],\n };\n } catch (err: any) {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify({ error: err.message }) }],\n isError: true,\n };\n }\n },\n );\n}\n\nexport async function cliFetch(\n url: string,\n opts: { method: string; body?: string; dev: boolean },\n): Promise<void> {\n const { loadOrCreateWallet } = await import(\"../wallet/index.js\");\n const wallet = await loadOrCreateWallet();\n const result = await x402Fetch(\n { url, method: opts.method, body: opts.body },\n wallet,\n opts,\n );\n console.log(JSON.stringify(result, null, 2));\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\ninterface CheckOpts {\n dev: boolean;\n}\n\nasync function checkEndpoint(url: string, method: string): Promise<Record<string, unknown>> {\n const res = await fetch(url, {\n method,\n headers: { \"Content-Type\": \"application/json\" },\n body: method !== \"GET\" ? \"{}\" : undefined,\n });\n\n if (res.status !== 402) {\n return {\n requiresPayment: false,\n statusCode: res.status,\n free: true,\n };\n }\n\n let body: Record<string, unknown> | null = null;\n try {\n body = await res.json() as Record<string, unknown>;\n } catch {}\n\n const accepts = body?.accepts as Array<Record<string, unknown>> | undefined;\n if (!accepts?.length) {\n return {\n requiresPayment: true,\n statusCode: 402,\n error: \"No payment options found in 402 response\",\n };\n }\n\n const paymentOptions = accepts.map((a) => {\n const amount = Number(a.amount || a.maxAmountRequired || 0);\n const decimals = Number(a.extra && typeof a.extra === \"object\" && \"decimals\" in a.extra\n ? (a.extra as Record<string, unknown>).decimals\n : 6);\n return {\n price: amount / Math.pow(10, decimals),\n priceFormatted: `$${(amount / Math.pow(10, decimals)).toFixed(decimals > 2 ? 4 : 2)}`,\n network: a.network,\n scheme: a.scheme,\n asset: a.asset,\n payTo: a.payTo,\n };\n });\n\n return {\n requiresPayment: true,\n statusCode: 402,\n x402Version: body?.x402Version ?? 2,\n paymentOptions,\n resource: body?.resource,\n };\n}\n\nexport function registerCheckTool(server: McpServer, opts: CheckOpts): void {\n server.tool(\n \"x402_check\",\n \"Check if an endpoint requires x402 payment and see its pricing. \" +\n \"Does NOT make a payment — just probes for requirements.\",\n {\n url: z.string().url().describe(\"The URL to check\"),\n method: z\n .enum([\"GET\", \"POST\", \"PUT\", \"DELETE\"])\n .default(\"GET\")\n .describe(\"HTTP method to probe with\"),\n },\n async (args) => {\n try {\n const result = await checkEndpoint(args.url, args.method);\n return {\n content: [\n { type: \"text\" as const, text: JSON.stringify(result, null, 2) },\n ],\n };\n } catch (err: any) {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify({ error: err.message }) }],\n isError: true,\n };\n }\n },\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { LoadedWallet } from \"../wallet/index.js\";\nimport { getSolanaBalance } from \"../wallet/index.js\";\nimport { WALLET_FILE } from \"../config.js\";\n\ninterface WalletToolOpts {\n dev: boolean;\n}\n\nexport function registerWalletTool(\n server: McpServer,\n wallet: LoadedWallet | null,\n opts: WalletToolOpts,\n): void {\n server.tool(\n \"x402_wallet\",\n \"Show wallet address, USDC balance, and deposit instructions. \" +\n \"The wallet is used to automatically pay for x402 API calls.\",\n {},\n async () => {\n if (!wallet) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n error: \"No wallet configured\",\n tip: \"Set DEXTER_PRIVATE_KEY env var or run `npx @dexterai/mcp wallet` to create one.\",\n }, null, 2),\n },\n ],\n };\n }\n\n try {\n const balance = await getSolanaBalance(wallet.info.solanaAddress);\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify(\n {\n address: wallet.info.solanaAddress,\n network: \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\",\n networkName: \"Solana Mainnet\",\n balances: {\n sol: balance.sol,\n usdc: balance.usdc,\n },\n walletFile: WALLET_FILE,\n tip:\n balance.usdc === 0\n ? `Deposit USDC (Solana) to ${wallet.info.solanaAddress} to start paying for x402 APIs.`\n : undefined,\n },\n null,\n 2,\n ),\n },\n ],\n };\n } catch (err: any) {\n return {\n content: [\n { type: \"text\" as const, text: JSON.stringify({ error: err.message }) },\n ],\n isError: true,\n };\n }\n },\n );\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { VERSION } from \"../config.js\";\nimport { registerSearchTool } from \"../tools/search.js\";\nimport { registerFetchTool } from \"../tools/fetch.js\";\nimport { registerCheckTool } from \"../tools/check.js\";\nimport { registerWalletTool } from \"../tools/wallet-tool.js\";\nimport { loadOrCreateWallet } from \"../wallet/index.js\";\n\nexport interface ServerOptions {\n transport: \"stdio\" | \"http\";\n dev: boolean;\n}\n\nexport async function startServer(opts: ServerOptions): Promise<void> {\n const wallet = await loadOrCreateWallet();\n\n const server = new McpServer({\n name: \"Dexter x402 Gateway\",\n version: VERSION,\n });\n\n registerSearchTool(server, opts);\n registerFetchTool(server, wallet, opts);\n registerCheckTool(server, opts);\n registerWalletTool(server, wallet, opts);\n\n if (opts.transport === \"stdio\") {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.on(\"SIGINT\", async () => {\n await server.close();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n await server.close();\n process.exit(0);\n });\n } else {\n // HTTP transport will be added for QR mode later\n console.error(\"HTTP transport not yet implemented. Use --transport=stdio\");\n process.exit(1);\n }\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport type ClientId =\n | \"cursor\"\n | \"claude-code\"\n | \"codex\"\n | \"vscode\"\n | \"windsurf\"\n | \"gemini-cli\";\n\ninterface ClientMeta {\n name: string;\n description: string;\n}\n\nexport const CLIENTS: Record<ClientId, ClientMeta> = {\n cursor: {\n name: \"Cursor\",\n description: \"Cursor AI code editor\",\n },\n \"claude-code\": {\n name: \"Claude Code\",\n description: \"Anthropic Claude Code CLI\",\n },\n codex: {\n name: \"Codex\",\n description: \"OpenAI Codex CLI\",\n },\n vscode: {\n name: \"VS Code\",\n description: \"Visual Studio Code with MCP support\",\n },\n windsurf: {\n name: \"Windsurf\",\n description: \"Codeium Windsurf editor\",\n },\n \"gemini-cli\": {\n name: \"Gemini CLI\",\n description: \"Google Gemini CLI\",\n },\n};\n\ninterface ClientConfig {\n configPath: string;\n sectionKey: string;\n entry: Record<string, unknown>;\n manual?: boolean;\n}\n\nconst SERVER_CMD = {\n command: \"npx\",\n args: [\"-y\", \"@dexterai/mcp@latest\"],\n};\n\nconst SERVER_CMD_DEV = {\n command: \"node\",\n args: [process.cwd() + \"/dist/index.js\", \"--dev\"],\n};\n\nfunction getConfigDir(): string {\n const platform = process.platform;\n if (platform === \"win32\") {\n return process.env.APPDATA || join(homedir(), \"AppData\", \"Roaming\");\n }\n if (platform === \"darwin\") {\n return join(homedir(), \"Library\", \"Application Support\");\n }\n return process.env.XDG_CONFIG_HOME || join(homedir(), \".config\");\n}\n\nexport function getClientConfig(client: ClientId, dev: boolean): ClientConfig {\n const cmd = dev ? SERVER_CMD_DEV : SERVER_CMD;\n\n switch (client) {\n case \"cursor\":\n return {\n configPath: join(homedir(), \".cursor\", \"mcp.json\"),\n sectionKey: \"mcpServers\",\n entry: cmd,\n };\n\n case \"claude-code\":\n return {\n configPath: join(homedir(), \".claude.json\"),\n sectionKey: \"mcpServers\",\n entry: cmd,\n };\n\n case \"codex\": {\n const codexHome = process.env.CODEX_HOME || join(homedir(), \".codex\");\n return {\n configPath: join(codexHome, \"config.toml\"),\n sectionKey: \"mcp_servers\",\n entry: cmd,\n manual: true, // TOML requires different handling\n };\n }\n\n case \"vscode\": {\n const configDir = getConfigDir();\n const vscodeDirs = [\"Code\", \"Code - Insiders\"];\n const dir = vscodeDirs.find((d) => {\n try {\n return require(\"node:fs\").existsSync(join(configDir, d));\n } catch { return false; }\n }) || \"Code\";\n return {\n configPath: join(configDir, dir, \"User\", \"mcp.json\"),\n sectionKey: \"mcpServers\",\n entry: cmd,\n };\n }\n\n case \"windsurf\":\n return {\n configPath: join(homedir(), \".codeium\", \"windsurf\", \"mcp_config.json\"),\n sectionKey: \"mcpServers\",\n entry: cmd,\n };\n\n case \"gemini-cli\":\n return {\n configPath: join(homedir(), \".gemini\", \"settings.json\"),\n sectionKey: \"mcpServers\",\n entry: cmd,\n };\n }\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { loadOrCreateWallet } from \"../../wallet/index.js\";\nimport { getClientConfig, CLIENTS, type ClientId } from \"./clients.js\";\n\ninterface InstallOpts {\n client?: string;\n yes: boolean;\n dev: boolean;\n}\n\nexport async function runInstall(opts: InstallOpts): Promise<void> {\n // Step 1: ensure wallet exists\n console.log(\"Setting up wallet...\");\n const wallet = await loadOrCreateWallet();\n if (!wallet) {\n console.error(\"Failed to create wallet. Exiting.\");\n process.exit(1);\n }\n console.log(`Wallet: ${wallet.info.solanaAddress}\\n`);\n\n // Step 2: pick client\n let clientId = opts.client as ClientId | undefined;\n\n if (!clientId) {\n if (opts.yes) {\n console.error(\"--client is required when using --yes\");\n process.exit(1);\n }\n\n console.log(\"Select an AI client to install into:\\n\");\n const ids = Object.keys(CLIENTS) as ClientId[];\n ids.forEach((id, i) => console.log(` ${i + 1}. ${CLIENTS[id].name}`));\n console.log();\n\n const readline = await import(\"node:readline\");\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n rl.question(\"Choice (number): \", resolve);\n });\n rl.close();\n\n const idx = parseInt(answer, 10) - 1;\n if (idx < 0 || idx >= ids.length) {\n console.error(\"Invalid choice.\");\n process.exit(1);\n }\n clientId = ids[idx];\n }\n\n if (!CLIENTS[clientId]) {\n console.error(`Unknown client: ${clientId}`);\n console.error(`Available: ${Object.keys(CLIENTS).join(\", \")}`);\n process.exit(1);\n }\n\n // Step 3: write config\n const config = getClientConfig(clientId, opts.dev);\n\n if (config.manual) {\n console.log(`\\n${CLIENTS[clientId].name} requires manual configuration.\\n`);\n console.log(\"Add this to your MCP config:\\n\");\n console.log(JSON.stringify(config.entry, null, 2));\n console.log(`\\nConfig file: ${config.configPath}`);\n return;\n }\n\n console.log(`\\nInstalling into ${CLIENTS[clientId].name}...`);\n\n mkdirSync(dirname(config.configPath), { recursive: true });\n\n let existing: Record<string, unknown> = {};\n if (existsSync(config.configPath)) {\n try {\n existing = JSON.parse(readFileSync(config.configPath, \"utf-8\"));\n } catch {\n existing = {};\n }\n }\n\n const section = (existing[config.sectionKey] as Record<string, unknown>) || {};\n section[\"dexter-x402\"] = config.entry;\n existing[config.sectionKey] = section;\n\n writeFileSync(config.configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n\n console.log(`Written to ${config.configPath}`);\n console.log(`\\nDexter x402 Gateway installed for ${CLIENTS[clientId].name}.`);\n console.log(`Wallet: ${wallet.info.solanaAddress}`);\n console.log(`\\nDeposit USDC (Solana) to start paying for x402 APIs.`);\n}\n","import yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\n\nvoid yargs(hideBin(process.argv))\n .scriptName(\"@dexterai/mcp\")\n .usage(\"$0 [command] [options]\")\n .option(\"dev\", {\n type: \"boolean\",\n description: \"Use localhost endpoints instead of production\",\n default: false,\n })\n .command(\n [\"$0\", \"server\"],\n \"Start the MCP server (default)\",\n (y) =>\n y.option(\"transport\", {\n choices: [\"stdio\", \"http\"] as const,\n default: \"stdio\" as const,\n description: \"Transport mode\",\n }),\n async (args) => {\n const { startServer } = await import(\"./server/index.js\");\n await startServer({\n transport: args.transport,\n dev: args.dev,\n });\n },\n )\n .command(\n \"install\",\n \"Install Dexter MCP into an AI client (Cursor, Claude, Codex, etc.)\",\n (y) =>\n y\n .option(\"client\", {\n type: \"string\",\n description: \"Client to install into\",\n })\n .option(\"yes\", {\n alias: \"y\",\n type: \"boolean\",\n description: \"Skip prompts\",\n default: false,\n }),\n async (args) => {\n const { runInstall } = await import(\"./cli/install/index.js\");\n await runInstall({ client: args.client, yes: args.yes, dev: args.dev });\n },\n )\n .command(\n \"wallet\",\n \"Show wallet address and balances\",\n () => {},\n async (args) => {\n const { showWalletInfo } = await import(\"./wallet/index.js\");\n await showWalletInfo({ dev: args.dev });\n },\n )\n .command(\n \"search <query>\",\n \"Search the Dexter x402 marketplace\",\n (y) =>\n y.positional(\"query\", { type: \"string\", demandOption: true }),\n async (args) => {\n const { cliSearch } = await import(\"./tools/search.js\");\n await cliSearch(args.query!, { dev: args.dev });\n },\n )\n .command(\n \"fetch <url>\",\n \"Fetch an x402-protected resource with automatic payment\",\n (y) =>\n y\n .positional(\"url\", { type: \"string\", demandOption: true })\n .option(\"method\", {\n choices: [\"GET\", \"POST\", \"PUT\", \"DELETE\"] as const,\n default: \"GET\" as const,\n })\n .option(\"body\", { type: \"string\", description: \"JSON request body\" }),\n async (args) => {\n const { cliFetch } = await import(\"./tools/fetch.js\");\n await cliFetch(args.url!, {\n method: args.method,\n body: args.body,\n dev: args.dev,\n });\n },\n )\n .strict()\n .help()\n .parse();\n"],"mappings":"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","names":["keypair","z","loadOrCreateWallet","z","homedir","join","existsSync","readFileSync","writeFileSync","mkdirSync","startServer","runInstall","showWalletInfo","cliSearch","cliFetch"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dexterai/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "x402 gateway for AI agents — search, pay, and call any x402 API through Dexter",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"@dexterai/mcp": "dist/index.js",
|
|
9
|
+
"dexter-mcp": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsx src/index.ts",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"release": "npm version patch && npm publish --access public"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@dexterai/x402": "^1.4.0",
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.17.4",
|
|
26
|
+
"@solana/web3.js": "^1.98.0",
|
|
27
|
+
"@solana/spl-token": "^0.4.9",
|
|
28
|
+
"viem": "^2.0.0",
|
|
29
|
+
"yargs": "^17.7.2",
|
|
30
|
+
"zod": "^3.23.0",
|
|
31
|
+
"bs58": "^6.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^22.10.0",
|
|
35
|
+
"@types/yargs": "^17.0.33",
|
|
36
|
+
"tsup": "^8.3.5",
|
|
37
|
+
"tsx": "^4.19.0",
|
|
38
|
+
"typescript": "^5.7.2"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"x402",
|
|
45
|
+
"mcp",
|
|
46
|
+
"dexter",
|
|
47
|
+
"solana",
|
|
48
|
+
"base",
|
|
49
|
+
"evm",
|
|
50
|
+
"payments",
|
|
51
|
+
"ai-agents"
|
|
52
|
+
],
|
|
53
|
+
"author": "Dexter",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "https://github.com/Dexter-DAO/dexter-mcp"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://dexter.cash/mcp"
|
|
60
|
+
}
|