nanocrawl-mcp-server 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.
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * NanoCrawl MCP Server — agent-side buyer for pay-per-page web browsing.
4
+ *
5
+ * Tools exposed to AI agents:
6
+ * browse(url) — pay for and retrieve web content
7
+ * peek(url) — check price without paying
8
+ * get_balance() — check wallet + Gateway funds
9
+ * get_receipts() — list past payments
10
+ * set_budget(max) — set spending cap
11
+ *
12
+ * Uses Circle Nanopayments (x402) for gas-free USDC micropayments on Arc Testnet.
13
+ * Transport: stdio (JSON-RPC over stdin/stdout for Claude Code integration).
14
+ */
15
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,484 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * NanoCrawl MCP Server — agent-side buyer for pay-per-page web browsing.
4
+ *
5
+ * Tools exposed to AI agents:
6
+ * browse(url) — pay for and retrieve web content
7
+ * peek(url) — check price without paying
8
+ * get_balance() — check wallet + Gateway funds
9
+ * get_receipts() — list past payments
10
+ * set_budget(max) — set spending cap
11
+ *
12
+ * Uses Circle Nanopayments (x402) for gas-free USDC micropayments on Arc Testnet.
13
+ * Transport: stdio (JSON-RPC over stdin/stdout for Claude Code integration).
14
+ */
15
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17
+ import { z } from "zod";
18
+ import { randomBytes } from "crypto";
19
+ // @ts-ignore — SDK subpath export types don't resolve under all tsconfig modes
20
+ import { GatewayClient } from "@circle-fin/x402-batching/client";
21
+ // ── Configuration ──────────────────────────────────────────────────────────
22
+ const BUYER_KEY = process.env.NANOCRAWL_BUYER_PRIVATE_KEY;
23
+ const CHAIN = "arcTestnet";
24
+ const AUTO_DEPOSIT_AMOUNT = process.env.NANOCRAWL_AUTO_DEPOSIT ?? "1";
25
+ const MIN_GATEWAY_BALANCE_USDC = 0.0001;
26
+ const USDC_DECIMALS = 6;
27
+ if (!BUYER_KEY) {
28
+ process.stderr.write("[nanocrawl] FATAL: NANOCRAWL_BUYER_PRIVATE_KEY is required.\n" +
29
+ "[nanocrawl] Set it in your environment or MCP server config.\n");
30
+ process.exit(1);
31
+ }
32
+ const receipts = [];
33
+ const contentCache = new Map(); // URL → cached result (idempotency)
34
+ let totalSpendUsdc = 0;
35
+ let budgetCapUsdc = Number.isFinite(parseFloat(process.env.NANOCRAWL_BUDGET ?? ""))
36
+ ? parseFloat(process.env.NANOCRAWL_BUDGET)
37
+ : Infinity;
38
+ // ── GatewayClient ──────────────────────────────────────────────────────────
39
+ const privateKey = (BUYER_KEY.startsWith("0x") ? BUYER_KEY : `0x${BUYER_KEY}`);
40
+ const client = new GatewayClient({ chain: CHAIN, privateKey });
41
+ // ── Helpers ────────────────────────────────────────────────────────────────
42
+ function log(msg) {
43
+ // Must use stderr — stdout is the JSON-RPC transport.
44
+ process.stderr.write(`[nanocrawl] ${msg}\n`);
45
+ }
46
+ async function ensureGatewayBalance() {
47
+ const balances = await client.getBalances();
48
+ const available = parseFloat(balances?.gateway?.formattedAvailable ?? "0");
49
+ if (available >= MIN_GATEWAY_BALANCE_USDC)
50
+ return;
51
+ const walletBalance = parseFloat(balances?.wallet?.formatted ?? "0");
52
+ const depositAmt = parseFloat(AUTO_DEPOSIT_AMOUNT);
53
+ if (walletBalance < depositAmt) {
54
+ throw new Error(`Insufficient funds. Wallet: ${walletBalance} USDC, Gateway: ${available} USDC. ` +
55
+ `Fund at https://faucet.circle.com (select Arc Testnet).`);
56
+ }
57
+ log(`Gateway balance low (${available} USDC), depositing ${AUTO_DEPOSIT_AMOUNT} USDC...`);
58
+ const deposit = await client.deposit(AUTO_DEPOSIT_AMOUNT);
59
+ log(`Deposit tx: ${deposit.depositTxHash}`);
60
+ log(`Deposited ${deposit.formattedAmount} USDC into Gateway`);
61
+ }
62
+ const domainMetaCache = new Map();
63
+ function parseRobotsTxt(text) {
64
+ const get = (key) => text.match(new RegExp(`${key}:\\s*(.+)`, "i"))?.[1]?.trim();
65
+ const payTo = get("Payment-PayTo");
66
+ const network = get("Payment-Network");
67
+ const asset = get("Payment-Asset");
68
+ const vc = get("Payment-VerifyingContract");
69
+ const feeStr = get("Crawl-fee");
70
+ if (!payTo || !network || !asset || !vc || !feeStr)
71
+ return null;
72
+ const crawlFeeUsdc = parseFloat(feeStr);
73
+ if (isNaN(crawlFeeUsdc))
74
+ return null;
75
+ return {
76
+ network,
77
+ chainId: parseInt(network.split(":")[1], 10),
78
+ asset,
79
+ payTo,
80
+ verifyingContract: vc,
81
+ maxTimeoutSeconds: parseInt(get("Payment-MaxTimeoutSeconds") ?? "345600", 10),
82
+ crawlFeeUsdc,
83
+ };
84
+ }
85
+ async function getDomainMeta(url) {
86
+ const origin = new URL(url).origin;
87
+ if (domainMetaCache.has(origin))
88
+ return domainMetaCache.get(origin);
89
+ try {
90
+ const res = await fetch(`${origin}/robots.txt`, {
91
+ headers: { "User-Agent": "NanoCrawl/1.0" },
92
+ });
93
+ if (!res.ok)
94
+ return null;
95
+ const meta = parseRobotsTxt(await res.text());
96
+ if (meta) {
97
+ domainMetaCache.set(origin, meta);
98
+ log(`Cached payment metadata for ${origin} (${meta.crawlFeeUsdc} USDC/page)`);
99
+ }
100
+ return meta;
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ async function proactiveBrowse(url) {
107
+ const meta = await getDomainMeta(url);
108
+ if (!meta)
109
+ return null;
110
+ const amountUnits = Math.round(meta.crawlFeeUsdc * 10 ** USDC_DECIMALS).toString();
111
+ const nonce = `0x${randomBytes(32).toString("hex")}`;
112
+ const validBefore = BigInt(Math.floor(Date.now() / 1000) + 5 * 24 * 60 * 60);
113
+ // Sign EIP-3009 TransferWithAuthorization using EIP-712 (GatewayWalletBatched domain)
114
+ const authorization = {
115
+ from: client.address,
116
+ to: meta.payTo,
117
+ value: amountUnits,
118
+ validAfter: "0",
119
+ validBefore: validBefore.toString(),
120
+ nonce,
121
+ };
122
+ const signature = await client.account.signTypedData({
123
+ domain: {
124
+ name: "GatewayWalletBatched",
125
+ version: "1",
126
+ chainId: meta.chainId,
127
+ verifyingContract: meta.verifyingContract,
128
+ },
129
+ types: {
130
+ TransferWithAuthorization: [
131
+ { name: "from", type: "address" },
132
+ { name: "to", type: "address" },
133
+ { name: "value", type: "uint256" },
134
+ { name: "validAfter", type: "uint256" },
135
+ { name: "validBefore", type: "uint256" },
136
+ { name: "nonce", type: "bytes32" },
137
+ ],
138
+ },
139
+ primaryType: "TransferWithAuthorization",
140
+ message: {
141
+ from: client.address,
142
+ to: meta.payTo,
143
+ value: BigInt(amountUnits),
144
+ validAfter: 0n,
145
+ validBefore,
146
+ nonce,
147
+ },
148
+ });
149
+ // Build x402 v2 PAYMENT-SIGNATURE payload
150
+ const paymentPayload = {
151
+ x402Version: 2,
152
+ resource: { url, mimeType: "application/json", description: "NanoCrawl paid content" },
153
+ accepted: {
154
+ scheme: "exact",
155
+ network: meta.network,
156
+ asset: meta.asset,
157
+ amount: amountUnits,
158
+ payTo: meta.payTo,
159
+ maxTimeoutSeconds: meta.maxTimeoutSeconds,
160
+ extra: {
161
+ name: "GatewayWalletBatched",
162
+ version: "1",
163
+ verifyingContract: meta.verifyingContract,
164
+ },
165
+ },
166
+ payload: { signature, authorization },
167
+ };
168
+ const encoded = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
169
+ // Single request — payment attached upfront, no 402 round-trip
170
+ const res = await fetch(url, {
171
+ headers: {
172
+ "User-Agent": "NanoCrawl/1.0 (AI agent; +https://nanocrawl.vercel.app)",
173
+ "X-NanoCrawl-Capable": "true",
174
+ "Payment-Signature": encoded,
175
+ },
176
+ redirect: "follow",
177
+ });
178
+ if (res.status !== 200)
179
+ return null;
180
+ // Extract settlement ID from PAYMENT-RESPONSE header
181
+ let transaction = "proactive";
182
+ const prHeader = res.headers.get("payment-response");
183
+ if (prHeader) {
184
+ try {
185
+ const pr = JSON.parse(Buffer.from(prHeader, "base64").toString("utf-8"));
186
+ transaction = pr.transaction ?? "proactive";
187
+ }
188
+ catch { /* use default */ }
189
+ }
190
+ const data = await res.json().catch(() => res.text());
191
+ return { data, formattedAmount: meta.crawlFeeUsdc.toFixed(6), transaction, status: 200 };
192
+ }
193
+ // ── MCP Server Setup ───────────────────────────────────────────────────────
194
+ const server = new McpServer({ name: "nanocrawl", version: "0.1.0" }, {
195
+ instructions: "NanoCrawl MCP server — pay-per-page web browsing for AI agents via " +
196
+ "Circle Nanopayments. Use browse() to fetch paid content, peek() to " +
197
+ "check prices, get_balance() for funds, set_budget() for spending limits.",
198
+ });
199
+ // ── Tool: browse ───────────────────────────────────────────────────────────
200
+ // GatewayClient.pay() handles the full x402 flow internally:
201
+ // 1. GET url → receives 402 + PAYMENT-REQUIRED header
202
+ // 2. Parses payment requirements (price, payTo, network, verifyingContract)
203
+ // 3. Signs EIP-3009 authorization off-chain (zero gas)
204
+ // 4. Retries request with PAYMENT-SIGNATURE header
205
+ // 5. Returns content + settlement metadata
206
+ server.registerTool("browse", {
207
+ description: "Pay for and retrieve content from a URL that charges AI crawlers " +
208
+ "via x402/Circle Nanopayments. Handles payment automatically.",
209
+ inputSchema: {
210
+ url: z.string().url().describe("The URL to browse and pay for"),
211
+ },
212
+ }, async ({ url }) => {
213
+ // Idempotency: return cached content if we already paid for this URL
214
+ const cached = contentCache.get(url);
215
+ if (cached) {
216
+ log(`Cache hit for ${url} (paid ${cached.amountUsdc} USDC earlier)`);
217
+ return {
218
+ content: [
219
+ {
220
+ type: "text",
221
+ text: `[Cached — already paid ${cached.amountUsdc} USDC | TX: ${cached.transaction}]\n\n` +
222
+ cached.content,
223
+ },
224
+ ],
225
+ };
226
+ }
227
+ if (totalSpendUsdc >= budgetCapUsdc) {
228
+ return {
229
+ content: [
230
+ {
231
+ type: "text",
232
+ text: `Budget exhausted. Spent: ${totalSpendUsdc.toFixed(6)} USDC, ` +
233
+ `Cap: ${budgetCapUsdc.toFixed(6)} USDC. Use set_budget to increase.`,
234
+ },
235
+ ],
236
+ isError: true,
237
+ };
238
+ }
239
+ try {
240
+ await ensureGatewayBalance();
241
+ // Try proactive flow first (single request, no 402 round-trip)
242
+ let result;
243
+ let flowType;
244
+ const proactive = await proactiveBrowse(url).catch(() => null);
245
+ if (proactive) {
246
+ result = proactive;
247
+ flowType = "proactive";
248
+ }
249
+ else {
250
+ // Fall back to standard 2-request flow
251
+ result = await client.pay(url);
252
+ flowType = "standard";
253
+ // Cache domain metadata for future proactive calls
254
+ getDomainMeta(url).catch(() => { });
255
+ }
256
+ const amountUsdc = parseFloat(result.formattedAmount);
257
+ totalSpendUsdc += amountUsdc;
258
+ const content = typeof result.data === "string"
259
+ ? result.data
260
+ : JSON.stringify(result.data, null, 2);
261
+ const receipt = {
262
+ url,
263
+ amountUsdc: result.formattedAmount,
264
+ transaction: result.transaction,
265
+ timestamp: new Date().toISOString(),
266
+ content,
267
+ };
268
+ receipts.push(receipt);
269
+ contentCache.set(url, receipt);
270
+ log(`[${flowType}] Paid ${result.formattedAmount} USDC for ${url} (tx: ${result.transaction})`);
271
+ const budgetNote = budgetCapUsdc !== Infinity
272
+ ? `\nBudget: ${totalSpendUsdc.toFixed(6)} / ${budgetCapUsdc.toFixed(6)} USDC`
273
+ : "";
274
+ return {
275
+ content: [
276
+ {
277
+ type: "text",
278
+ text: `[${flowType}] Paid ${result.formattedAmount} USDC | TX: ${result.transaction}${budgetNote}\n\n` +
279
+ content,
280
+ },
281
+ ],
282
+ };
283
+ }
284
+ catch (err) {
285
+ const msg = err instanceof Error ? err.message : String(err);
286
+ return {
287
+ content: [{ type: "text", text: `Failed to browse ${url}: ${msg}` }],
288
+ isError: true,
289
+ };
290
+ }
291
+ });
292
+ // ── Tool: peek ─────────────────────────────────────────────────────────────
293
+ // Manual HTTP — we want the 402 response, NOT to pay.
294
+ // Node.js fetch() naturally lacks accept-language and sec-fetch-dest headers,
295
+ // so the publisher's classifier (web/lib/classify.ts) will identify us as a
296
+ // crawler and return 402 with pricing info.
297
+ server.registerTool("peek", {
298
+ description: "Check the price of a URL without paying. Returns pricing info " +
299
+ "if the site charges AI crawlers via x402.",
300
+ inputSchema: {
301
+ url: z.string().url().describe("The URL to check pricing for"),
302
+ },
303
+ }, async ({ url }) => {
304
+ try {
305
+ const res = await fetch(url, {
306
+ headers: {
307
+ "User-Agent": "NanoCrawl/1.0 (AI agent; +https://nanocrawl.vercel.app)",
308
+ "X-NanoCrawl-Capable": "true",
309
+ },
310
+ redirect: "follow",
311
+ });
312
+ if (res.status === 402) {
313
+ // Try PAYMENT-REQUIRED header (base64-encoded JSON), then response body
314
+ let requirements = null;
315
+ const header = res.headers.get("payment-required");
316
+ if (header) {
317
+ try {
318
+ requirements = JSON.parse(Buffer.from(header, "base64").toString("utf-8"));
319
+ }
320
+ catch {
321
+ // Fall through to body parsing
322
+ }
323
+ }
324
+ if (!requirements) {
325
+ requirements = (await res.json().catch(() => null));
326
+ }
327
+ const accepts = requirements?.accepts;
328
+ const accept = accepts?.[0];
329
+ if (!accept) {
330
+ return {
331
+ content: [
332
+ {
333
+ type: "text",
334
+ text: "402 received but could not parse payment options.",
335
+ },
336
+ ],
337
+ isError: true,
338
+ };
339
+ }
340
+ const amountUnits = parseInt(String(accept.amount), 10);
341
+ const amountUsdc = amountUnits / 10 ** USDC_DECIMALS;
342
+ const extra = accept.extra;
343
+ return {
344
+ content: [
345
+ {
346
+ type: "text",
347
+ text: [
348
+ `Price: ${amountUsdc} USDC`,
349
+ `Network: ${accept.network}`,
350
+ `Pay to: ${accept.payTo}`,
351
+ `Scheme: ${extra?.name ?? accept.scheme}`,
352
+ ].join("\n"),
353
+ },
354
+ ],
355
+ };
356
+ }
357
+ if (res.status === 200) {
358
+ return {
359
+ content: [
360
+ { type: "text", text: "This URL is free — no payment required." },
361
+ ],
362
+ };
363
+ }
364
+ return {
365
+ content: [
366
+ {
367
+ type: "text",
368
+ text: `HTTP ${res.status} — not a paid resource.`,
369
+ },
370
+ ],
371
+ };
372
+ }
373
+ catch (err) {
374
+ const msg = err instanceof Error ? err.message : String(err);
375
+ return {
376
+ content: [{ type: "text", text: `Failed to peek ${url}: ${msg}` }],
377
+ isError: true,
378
+ };
379
+ }
380
+ });
381
+ // ── Tool: get_balance ──────────────────────────────────────────────────────
382
+ server.registerTool("get_balance", {
383
+ description: "Check USDC balances (on-chain wallet and Circle Gateway) plus session spending.",
384
+ inputSchema: {},
385
+ }, async () => {
386
+ try {
387
+ const b = await client.getBalances();
388
+ const remaining = budgetCapUsdc === Infinity
389
+ ? "unlimited"
390
+ : `${Math.max(0, budgetCapUsdc - totalSpendUsdc).toFixed(6)} USDC`;
391
+ return {
392
+ content: [
393
+ {
394
+ type: "text",
395
+ text: [
396
+ `Wallet (on-chain): ${b?.wallet?.formatted ?? "?"} USDC`,
397
+ `Gateway available: ${b?.gateway?.formattedAvailable ?? "?"} USDC`,
398
+ `Gateway total: ${b?.gateway?.formattedTotal ?? "?"} USDC`,
399
+ `Session spend: ${totalSpendUsdc.toFixed(6)} USDC`,
400
+ `Budget remaining: ${remaining}`,
401
+ `Address: ${client.address}`,
402
+ `Chain: ${CHAIN}`,
403
+ ].join("\n"),
404
+ },
405
+ ],
406
+ };
407
+ }
408
+ catch (err) {
409
+ const msg = err instanceof Error ? err.message : String(err);
410
+ return {
411
+ content: [{ type: "text", text: `Failed to get balance: ${msg}` }],
412
+ isError: true,
413
+ };
414
+ }
415
+ });
416
+ // ── Tool: get_receipts ─────────────────────────────────────────────────────
417
+ server.registerTool("get_receipts", {
418
+ description: "List all payments made this session — URLs, amounts, transaction IDs.",
419
+ inputSchema: {},
420
+ }, async () => {
421
+ if (receipts.length === 0) {
422
+ return {
423
+ content: [{ type: "text", text: "No payments made this session." }],
424
+ };
425
+ }
426
+ const lines = receipts.map((r, i) => `${i + 1}. ${r.url}\n ${r.amountUsdc} USDC | TX: ${r.transaction} | ${r.timestamp}`);
427
+ return {
428
+ content: [
429
+ {
430
+ type: "text",
431
+ text: `${receipts.length} payments | ${totalSpendUsdc.toFixed(6)} USDC total\n\n` +
432
+ lines.join("\n"),
433
+ },
434
+ ],
435
+ };
436
+ });
437
+ // ── Tool: set_budget ───────────────────────────────────────────────────────
438
+ server.registerTool("set_budget", {
439
+ description: "Set a USDC spending cap for this session. browse() will refuse once the cap is reached.",
440
+ inputSchema: {
441
+ max_usd: z
442
+ .number()
443
+ .positive()
444
+ .describe("Maximum USDC to spend (e.g. 0.05 for 5 cents)"),
445
+ },
446
+ }, async ({ max_usd }) => {
447
+ budgetCapUsdc = max_usd;
448
+ const remaining = Math.max(0, budgetCapUsdc - totalSpendUsdc);
449
+ return {
450
+ content: [
451
+ {
452
+ type: "text",
453
+ text: `Budget: ${budgetCapUsdc} USDC | ` +
454
+ `Spent: ${totalSpendUsdc.toFixed(6)} | ` +
455
+ `Remaining: ${remaining.toFixed(6)} USDC`,
456
+ },
457
+ ],
458
+ };
459
+ });
460
+ // ── Startup ────────────────────────────────────────────────────────────────
461
+ async function main() {
462
+ log("Starting NanoCrawl MCP server...");
463
+ log(`Address: ${client.address}`);
464
+ log(`Chain: ${CHAIN}`);
465
+ // Check balances and auto-deposit if needed
466
+ try {
467
+ const b = await client.getBalances();
468
+ log(`Wallet: ${b?.wallet?.formatted ?? "?"} USDC`);
469
+ log(`Gateway: ${b?.gateway?.formattedAvailable ?? "?"} USDC`);
470
+ await ensureGatewayBalance();
471
+ }
472
+ catch (err) {
473
+ log(`Warning: initial balance check failed — ${err instanceof Error ? err.message : err}`);
474
+ log("Payments will attempt deposit on first browse() call.");
475
+ }
476
+ const transport = new StdioServerTransport();
477
+ await server.connect(transport);
478
+ log("Connected via stdio — ready for tool calls");
479
+ }
480
+ main().catch((err) => {
481
+ log(`Fatal: ${err}`);
482
+ process.exit(1);
483
+ });
484
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tests the proactive payment flow — single HTTP request, no 402 round-trip.
3
+ *
4
+ * Flow: parse robots.txt → sign EIP-3009 locally → send with Payment-Signature → get content
5
+ *
6
+ * Usage:
7
+ * NANOCRAWL_BUYER_PRIVATE_KEY=0x... npx tsx src/test-proactive.ts
8
+ */
9
+ export {};
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Tests the proactive payment flow — single HTTP request, no 402 round-trip.
3
+ *
4
+ * Flow: parse robots.txt → sign EIP-3009 locally → send with Payment-Signature → get content
5
+ *
6
+ * Usage:
7
+ * NANOCRAWL_BUYER_PRIVATE_KEY=0x... npx tsx src/test-proactive.ts
8
+ */
9
+ import { randomBytes } from "crypto";
10
+ // @ts-ignore
11
+ import { GatewayClient } from "@circle-fin/x402-batching/client";
12
+ const BUYER_KEY = process.env.NANOCRAWL_BUYER_PRIVATE_KEY;
13
+ const TARGET = process.env.NANOCRAWL_TARGET ?? "https://nanocrawl.vercel.app";
14
+ const USDC_DECIMALS = 6;
15
+ const client = new GatewayClient({
16
+ chain: "arcTestnet",
17
+ privateKey: (BUYER_KEY.startsWith("0x") ? BUYER_KEY : `0x${BUYER_KEY}`),
18
+ });
19
+ function parseRobotsTxt(text) {
20
+ const get = (key) => text.match(new RegExp(`${key}:\\s*(.+)`, "i"))?.[1]?.trim();
21
+ return {
22
+ payTo: get("Payment-PayTo"),
23
+ network: get("Payment-Network"),
24
+ asset: get("Payment-Asset"),
25
+ verifyingContract: get("Payment-VerifyingContract"),
26
+ crawlFeeUsdc: parseFloat(get("Crawl-fee") ?? "0"),
27
+ maxTimeoutSeconds: parseInt(get("Payment-MaxTimeoutSeconds") ?? "345600", 10),
28
+ };
29
+ }
30
+ async function main() {
31
+ console.log(`Address: ${client.address}`);
32
+ console.log(`Target: ${TARGET}\n`);
33
+ // ── Step 1: Read robots.txt ────────────────────────────────────────────
34
+ console.log("1. Reading robots.txt for payment metadata...");
35
+ const robotsRes = await fetch(`${TARGET}/robots.txt`);
36
+ const robotsTxt = await robotsRes.text();
37
+ const meta = parseRobotsTxt(robotsTxt);
38
+ console.log(` PayTo: ${meta.payTo}`);
39
+ console.log(` Price: ${meta.crawlFeeUsdc} USDC`);
40
+ console.log(` Network: ${meta.network}`);
41
+ // ── Step 2: Standard flow (2 requests) ───────────────��─────────────────
42
+ console.log("\n2. STANDARD flow — browse /products/2 (402 → sign → retry)...");
43
+ const t1 = Date.now();
44
+ const standardResult = await client.pay(`${TARGET}/products/2`);
45
+ const standardMs = Date.now() - t1;
46
+ console.log(` Status: ${standardResult.status}`);
47
+ console.log(` Paid: ${standardResult.formattedAmount} USDC`);
48
+ console.log(` Time: ${standardMs}ms (2 HTTP requests)`);
49
+ // ── Step 3: Proactive flow (1 request) ─────────────────────────────────
50
+ console.log("\n3. PROACTIVE flow — browse /products/3 (sign locally → single request)...");
51
+ const amountUnits = Math.round(meta.crawlFeeUsdc * 10 ** USDC_DECIMALS).toString();
52
+ const nonce = `0x${randomBytes(32).toString("hex")}`;
53
+ const chainId = parseInt(meta.network.split(":")[1], 10);
54
+ const validBefore = BigInt(Math.floor(Date.now() / 1000) + 5 * 24 * 60 * 60);
55
+ // Sign EIP-3009 authorization locally
56
+ const signature = await client.account.signTypedData({
57
+ domain: {
58
+ name: "GatewayWalletBatched",
59
+ version: "1",
60
+ chainId,
61
+ verifyingContract: meta.verifyingContract,
62
+ },
63
+ types: {
64
+ TransferWithAuthorization: [
65
+ { name: "from", type: "address" },
66
+ { name: "to", type: "address" },
67
+ { name: "value", type: "uint256" },
68
+ { name: "validAfter", type: "uint256" },
69
+ { name: "validBefore", type: "uint256" },
70
+ { name: "nonce", type: "bytes32" },
71
+ ],
72
+ },
73
+ primaryType: "TransferWithAuthorization",
74
+ message: {
75
+ from: client.address,
76
+ to: meta.payTo,
77
+ value: BigInt(amountUnits),
78
+ validAfter: 0n,
79
+ validBefore,
80
+ nonce,
81
+ },
82
+ });
83
+ const paymentPayload = {
84
+ x402Version: 2,
85
+ resource: {
86
+ url: `${TARGET}/products/3`,
87
+ mimeType: "application/json",
88
+ description: "NanoCrawl paid content",
89
+ },
90
+ accepted: {
91
+ scheme: "exact",
92
+ network: meta.network,
93
+ asset: meta.asset,
94
+ amount: amountUnits,
95
+ payTo: meta.payTo,
96
+ maxTimeoutSeconds: meta.maxTimeoutSeconds,
97
+ extra: {
98
+ name: "GatewayWalletBatched",
99
+ version: "1",
100
+ verifyingContract: meta.verifyingContract,
101
+ },
102
+ },
103
+ payload: {
104
+ signature,
105
+ authorization: {
106
+ from: client.address,
107
+ to: meta.payTo,
108
+ value: amountUnits,
109
+ validAfter: "0",
110
+ validBefore: validBefore.toString(),
111
+ nonce,
112
+ },
113
+ },
114
+ };
115
+ const encoded = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
116
+ const t2 = Date.now();
117
+ const proactiveRes = await fetch(`${TARGET}/products/3`, {
118
+ headers: {
119
+ "User-Agent": "NanoCrawl/1.0 (AI agent)",
120
+ "X-NanoCrawl-Capable": "true",
121
+ "Payment-Signature": encoded,
122
+ },
123
+ });
124
+ const proactiveMs = Date.now() - t2;
125
+ console.log(` Status: ${proactiveRes.status}`);
126
+ if (proactiveRes.status === 200) {
127
+ const prHeader = proactiveRes.headers.get("payment-response");
128
+ let tx = "?";
129
+ if (prHeader) {
130
+ try {
131
+ const pr = JSON.parse(Buffer.from(prHeader, "base64").toString("utf-8"));
132
+ tx = pr.transaction ?? "?";
133
+ }
134
+ catch { }
135
+ }
136
+ const data = await proactiveRes.json();
137
+ console.log(` Paid: ${meta.crawlFeeUsdc} USDC`);
138
+ console.log(` TX: ${tx}`);
139
+ console.log(` Time: ${proactiveMs}ms (1 HTTP request)`);
140
+ console.log(` Content: ${data.name ?? JSON.stringify(data).slice(0, 80)}`);
141
+ }
142
+ else {
143
+ const body = await proactiveRes.text();
144
+ console.log(` FAILED: ${body.slice(0, 200)}`);
145
+ }
146
+ // ── Summary ────────────────────────────────────────────────────────────
147
+ console.log("\n4. Comparison:");
148
+ console.log(` Standard: ${standardMs}ms (2 HTTP requests — GET → 402 → sign → retry)`);
149
+ console.log(` Proactive: ${proactiveMs}ms (1 HTTP request — sign locally → send)`);
150
+ if (proactiveRes.status === 200) {
151
+ const savings = Math.round((1 - proactiveMs / standardMs) * 100);
152
+ console.log(` Savings: ${savings > 0 ? savings + "%" : "N/A"} faster`);
153
+ console.log("\n At scale (100k pages): proactive saves 100,000 HTTP round-trips.");
154
+ }
155
+ }
156
+ main().catch((err) => {
157
+ console.error(`Failed: ${err instanceof Error ? err.message : err}`);
158
+ process.exit(1);
159
+ });
160
+ //# sourceMappingURL=test-proactive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-proactive.js","sourceRoot":"","sources":["../src/test-proactive.ts"],"names":[],"mappings":"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"}
package/dist/test.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Integration test — verifies the full payment flow against the live seller.
3
+ *
4
+ * Usage:
5
+ * NANOCRAWL_BUYER_PRIVATE_KEY=0x... npx tsx src/test.ts
6
+ */
7
+ export {};
package/dist/test.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Integration test — verifies the full payment flow against the live seller.
3
+ *
4
+ * Usage:
5
+ * NANOCRAWL_BUYER_PRIVATE_KEY=0x... npx tsx src/test.ts
6
+ */
7
+ // @ts-ignore
8
+ import { GatewayClient } from "@circle-fin/x402-batching/client";
9
+ const BUYER_KEY = process.env.NANOCRAWL_BUYER_PRIVATE_KEY;
10
+ const TARGET = process.env.NANOCRAWL_TARGET ?? "https://nanocrawl.vercel.app";
11
+ if (!BUYER_KEY) {
12
+ console.error("Set NANOCRAWL_BUYER_PRIVATE_KEY to run this test.");
13
+ process.exit(1);
14
+ }
15
+ const privateKey = (BUYER_KEY.startsWith("0x") ? BUYER_KEY : `0x${BUYER_KEY}`);
16
+ const client = new GatewayClient({ chain: "arcTestnet", privateKey });
17
+ async function main() {
18
+ console.log(`Address: ${client.address}`);
19
+ console.log(`Target: ${TARGET}\n`);
20
+ // ── Check balances ─────────────────────────────────────────────────────
21
+ console.log("1. Checking balances...");
22
+ const balances = await client.getBalances();
23
+ console.log(` Wallet: ${balances?.wallet?.formatted ?? "?"} USDC`);
24
+ console.log(` Gateway: ${balances?.gateway?.formattedAvailable ?? "?"} USDC`);
25
+ // ── Deposit if needed ──────────────────────────────────────────────────
26
+ const available = parseFloat(balances?.gateway?.formattedAvailable ?? "0");
27
+ if (available < 0.001) {
28
+ console.log("\n2. Gateway empty — depositing 1 USDC...");
29
+ const deposit = await client.deposit("1");
30
+ console.log(` Deposit tx: ${deposit.depositTxHash}`);
31
+ console.log(` Amount: ${deposit.formattedAmount} USDC`);
32
+ }
33
+ else {
34
+ console.log("\n2. Gateway funded — skipping deposit.");
35
+ }
36
+ // ── Peek (raw 402) ────────────────────────────────────────────────────
37
+ console.log("\n3. Peeking at /products/1 (expecting 402)...");
38
+ const peekRes = await fetch(`${TARGET}/products/1`, {
39
+ headers: {
40
+ "User-Agent": "NanoCrawl/1.0 (AI agent)",
41
+ "X-NanoCrawl-Capable": "true",
42
+ },
43
+ });
44
+ console.log(` Status: ${peekRes.status}`);
45
+ if (peekRes.status === 402) {
46
+ const body = await peekRes.json();
47
+ const accept = body.accepts?.[0];
48
+ const price = parseInt(accept?.amount ?? "0", 10) / 1_000_000;
49
+ console.log(` Price: ${price} USDC`);
50
+ console.log(` PayTo: ${accept?.payTo}`);
51
+ }
52
+ // ── Browse (pay + get content) ─────────────────────────────────────────
53
+ console.log("\n4. Browsing /products/1 (paying via GatewayClient.pay())...");
54
+ const result = await client.pay(`${TARGET}/products/1`);
55
+ console.log(` Status: ${result.status}`);
56
+ console.log(` Paid: ${result.formattedAmount} USDC`);
57
+ console.log(` TX: ${result.transaction}`);
58
+ const content = typeof result.data === "string"
59
+ ? result.data.slice(0, 200)
60
+ : JSON.stringify(result.data).slice(0, 200);
61
+ console.log(` Content: ${content}...`);
62
+ // ── Summary ────────────────────────────────────────────────────────────
63
+ console.log("\n5. Checking final balances...");
64
+ const after = await client.getBalances();
65
+ console.log(` Gateway: ${after?.gateway?.formattedAvailable ?? "?"} USDC`);
66
+ console.log("\nAll checks passed.");
67
+ }
68
+ main().catch((err) => {
69
+ console.error(`\nFailed: ${err instanceof Error ? err.message : err}`);
70
+ process.exit(1);
71
+ });
72
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "nanocrawl-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for AI agents to pay-per-page browse the web via Circle Nanopayments",
5
+ "type": "module",
6
+ "bin": {
7
+ "@nanocrawl/mcp-server": "./dist/index.js",
8
+ "nanocrawl-mcp": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "prepublishOnly": "npm run build",
16
+ "start": "node dist/index.js",
17
+ "dev": "tsx src/index.ts",
18
+ "test": "tsx src/test.ts"
19
+ },
20
+ "dependencies": {
21
+ "@circle-fin/x402-batching": "^2.0.4",
22
+ "@modelcontextprotocol/sdk": "^1.29.0",
23
+ "@x402/core": "^2.3.0",
24
+ "@x402/evm": "^2.9.0",
25
+ "viem": "^2.21.0",
26
+ "zod": "^3.23.8"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.0.0",
30
+ "tsx": "^4.0.0",
31
+ "typescript": "^5.7.0"
32
+ }
33
+ }