import test from 'node:test';
import assert from 'node:assert/strict';
import { wrapFetchWithPaymentFromConfig } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm';
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
import { validateDiscoveryExtension } from '@x402/extensions/bazaar';
import { makeApp, seededStore, BASE, PAY_TO, mockNet } from './helpers.js';
import { headAsGet } from '../src/head.js';
import { handle as stdioHandle, TOOLS as STDIO_TOOLS } from '../src/mcp-stdio.js';
import { toQuery } from '../src/lite.js';
const decode = (h) => JSON.parse(Buffer.from(h, 'base64').toString('utf8'));
const v2pay = (fetchApp) => wrapFetchWithPaymentFromConfig(fetchApp, { schemes: [{ network: 'eip155:8453', client: new ExactEvmScheme(privateKeyToAccount(generatePrivateKey())) }] });
const json = (body) => ({ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
const ROUTES = [['lite', '3000', 'need=web+search'], ['pick', '10000', 'need=web+search'], ['market', '20000', 'need=token+safety']];
const mcp = (fetchApp, body) => fetchApp(`${BASE}/mcp`, { method: 'POST', headers: { 'content-type': 'application/json', accept: 'application/json, text/event-stream' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, ...body }) });
test('unpaid: every paid route answers 402 on GET, POST, empty POST and bare probes (v2 header <= 8192 B + valid Bazaar, v1 body); no upstream call', async () => {
const net = mockNet(); const { fetchApp } = await makeApp({ net }); const before = net.calls.length;
for (const [tier, amount, qs] of ROUTES) {
for (const [u, init] of [[`${BASE}/v1/${tier}?${qs}`], [`${BASE}/v1/${tier}`], [`${BASE}/v1/${tier}`, json(Object.fromEntries(new URLSearchParams(qs)))], [`${BASE}/v1/${tier}`, { method: 'POST' }], [`${BASE}/v1/${tier}`, json({})]]) {
const r = await fetchApp(u, init);
assert.equal(r.status, 402, `${init?.method || 'GET'} ${u}`);
const req = decode(r.headers.get('PAYMENT-REQUIRED')); const a = req.accepts[0];
assert.deepEqual([req.x402Version, a.scheme, a.network, a.amount, a.payTo, a.asset], [2, 'exact', 'eip155:8453', amount, PAY_TO, '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913']);
assert.equal(req.resource.url, `${BASE}/v1/${tier}`);
assert.ok(validateDiscoveryExtension(req.extensions.bazaar).valid, 'bazaar declaration valid');
assert.ok(r.headers.get('PAYMENT-REQUIRED').length <= 8192, 'header size');
const b = await r.json(); assert.equal(b.x402Version, 1); assert.equal(b.accepts[0].maxAmountRequired, amount); assert.equal(b.accepts[0].network, 'base');
}
}
assert.equal(net.calls.length, before);
});
test('HEAD = 402 + PAYMENT-REQUIRED with no body and never settles', async () => {
const { app, facilitator } = await makeApp();
const h = headAsGet((req) => app.fetch(req));
for (const [tier, amount, qs] of ROUTES) {
const r = await h(new Request(`${BASE}/v1/${tier}?${qs}`, { method: 'HEAD', headers: { 'payment-signature': 'junk', 'x-payment': 'junk' } }));
assert.equal(r.status, 402); assert.equal(decode(r.headers.get('payment-required')).accepts[0].amount, amount); assert.equal(await r.text(), '');
}
assert.equal(facilitator.verified.length, 0);
});
test('bad input: 400 before payment (never a 402, never charged)', async () => {
const { fetchApp, facilitator } = await makeApp();
for (const tier of ['lite', 'pick', 'preview']) for (const q of ['need=x', `need=${'a'.repeat(201)}`, 'need=the+and+of', 'need=web&max_price=-1', 'need=web&max_price=abc', 'need=web&network=tron']) {
assert.equal((await fetchApp(`${BASE}/v1/${tier}?${q}`)).status, 400, `${tier} ${q}`);
}
for (const q of ['need=web&min_payers=-2', 'need=web&min_payers=1.5']) assert.equal((await fetchApp(`${BASE}/v1/pick?${q}`)).status, 400, q);
for (const q of ['limit=0', 'limit=99', 'network=doge']) assert.equal((await fetchApp(`${BASE}/v1/market?${q}`)).status, 400, q);
assert.equal((await fetchApp(`${BASE}/v1/pick`, json({ need: 5 }))).status, 400);
assert.equal((await fetchApp(`${BASE}/v1/lite`, json({ foo: 1 }))).status, 400);
assert.equal(facilitator.verified.length, 0);
});
test('paid v2 lite + pick + market: ranked results, live checks, best verified pick, settled once each at the right price', async () => {
const { fetchApp, facilitator } = await makeApp();
const pay = v2pay(fetchApp);
const l = await pay(`${BASE}/v1/lite?need=web+search`); assert.equal(l.status, 200); const lj = await l.json();
assert.equal(lj.tier, 'lite'); assert.equal(lj.results[0].service, 'SearchCo'); assert.ok(lj.results.length <= 5); assert.equal(lj.confidence, 'high');
assert.ok(lj.results.every((x) => !x.resource.includes('testnet')), 'testnet-only resources excluded');
assert.equal(lj.results[0].live_check, undefined, 'no live checks on lite'); assert.ok(l.headers.get('payment-response'));
assert.deepEqual(lj.results[0].indexed_on, ['cdp', 'payai']); assert.ok(lj.results[0].networks.includes('solana'));
const p = await pay(`${BASE}/v1/pick`, json({ need: 'web search', max_price: 0.02 })); assert.equal(p.status, 200); const pj = await p.json();
assert.equal(pj.best_pick.service, 'SearchCo'); assert.equal(pj.best_pick.price_usd, 0.01);
const dead = pj.results.find((x) => x.service === 'DeadSearch'); assert.equal(dead.verified_live, false); assert.ok(pj.sources_failed.includes('live_check:deadsearch.example.com'));
const ours = pj.results.find((x) => x.ours); if (ours) assert.equal(ours.verified_live ?? null, null);
assert.equal(pj.cheapest_verified?.service, 'newsearch.example.com'); assert.equal(pj.cheapest_verified.price_usd, 0.001); assert.ok(pj.stats.resources >= 3);
assert.ok(!/0x(11){20}/.test(JSON.stringify(pj)), 'seller payTo never returned');
const m = await pay(`${BASE}/v1/market?need=web+search`); assert.equal(m.status, 200); const mj = await m.json();
assert.equal(mj.top_sellers[0].host, 'search.example.com'); assert.ok(mj.stats.price_usd.median > 0); assert.ok(mj.stats.price_with_buyers_usd.payer_weighted_median > 0);
const all = await pay(`${BASE}/v1/market`); assert.equal(all.status, 200); assert.ok((await all.json()).stats.resources >= 7);
assert.deepEqual(facilitator.settled.map((x) => x.amount), ['3000', '10000', '20000', '20000']);
});
test('NO FAIL-OPEN: a candidate whose live check fails or hangs is never verified; none verified = best_pick null + low confidence', async () => {
const net = mockNet({ live: { 'https://search.example.com/v1/search': 'hang', 'https://cheapsearch.example.com/search': '200', 'https://newsearch.example.com/q': 'down' } });
const { fetchApp, facilitator } = await makeApp({ net });
const r = await v2pay(fetchApp)(`${BASE}/v1/pick?need=web+search`); assert.equal(r.status, 200); const j = await r.json();
assert.equal(j.best_pick, null); assert.equal(j.confidence, 'low'); assert.match(j.pick_note, /Not verified/);
for (const x of j.results.slice(0, 5)) assert.notEqual(x.verified_live, true, x.service);
assert.ok(j.sources_failed.includes('live_check:search.example.com')); assert.ok(j.sources_failed.includes('live_check:deadsearch.example.com'));
assert.equal(j.results.find((x) => x.service === 'CheapSearch').live_check.status, 'no_402');
// price changed vs the listing: not picked
const net2 = mockNet({ live: { 'https://search.example.com/v1/search': 'price' } });
const j2 = await (await v2pay((await makeApp({ net: net2 })).fetchApp)(`${BASE}/v1/pick?need=web+search`)).json();
assert.notEqual(j2.best_pick?.service, 'SearchCo'); assert.equal(j2.results[0].live_check.price_matches_index, false);
assert.equal(facilitator.settled.length, 1);
});
test('FAIL-CLOSED: no index / index > 72 h / store down = 503 never settled; 12-72 h = stale flag + lower confidence; no matches = 404 never settled', async () => {
const empty = await makeApp({ seed: false });
for (const [tier, , qs] of ROUTES) {
const r = await v2pay(empty.fetchApp)(`${BASE}/v1/${tier}?${qs}`);
assert.equal(r.status, 503, tier); const j = await r.json(); assert.equal(j.charged, false); assert.ok(j.sources_failed.includes('index_not_ready'));
}
assert.equal(empty.facilitator.settled.length, 0);
const old = await makeApp({ store: (await seededStore({ ageMs: 80 * 3600_000 })).store });
const o = await v2pay(old.fetchApp)(`${BASE}/v1/lite?need=web+search`); assert.equal(o.status, 503); assert.ok((await o.json()).sources_failed.includes('index_stale'));
const stale = await makeApp({ store: (await seededStore({ ageMs: 30 * 3600_000 })).store });
const s = await (await v2pay(stale.fetchApp)(`${BASE}/v1/lite?need=web+search`)).json(); assert.equal(s.confidence, 'low'); assert.equal(s.index.stale, true); assert.ok(s.sources_failed.includes('index_stale'));
const down = await makeApp({ store: { query: async () => { throw new Error('boom'); } } });
const d = await v2pay(down.fetchApp)(`${BASE}/v1/pick?need=web+search`); assert.equal(d.status, 503); assert.ok((await d.json()).sources_failed.includes('index_store'));
const n = await v2pay(stale.fetchApp)(`${BASE}/v1/lite?need=zebra+quantum+knitting`); assert.equal(n.status, 404); assert.equal((await n.json()).error, 'no_matches');
assert.equal(old.facilitator.settled.length + down.facilitator.settled.length + stale.facilitator.settled.length, 1);
});
test('free preview: counts + top 3 names only (no URLs), rate limit', async () => {
let n = 0; const limiter = { limit: async () => ({ success: ++n <= 2 }) };
const { fetchApp } = await makeApp({ limiter });
const r = await fetchApp(`${BASE}/v1/preview?need=web+search`);
assert.equal(r.status, 200); const j = await r.json();
assert.ok(j.matches >= 3); assert.equal(j.top[0].service, 'SearchCo'); assert.ok(!/https:\/\/[a-z]+\.example\.com/.test(JSON.stringify(j.top)), 'no URLs in preview');
assert.equal(j.full.prices.pick, '$0.01');
assert.equal((await fetchApp(`${BASE}/v1/preview?need=pdf`)).status, 200);
assert.equal((await fetchApp(`${BASE}/v1/preview?need=pdf`)).status, 429);
});
test('remote MCP: initialize, tools/list, tools/call (preview tier)', async () => {
const { fetchApp } = await makeApp();
const init = await (await mcp(fetchApp, { method: 'initialize', params: { protocolVersion: '2025-06-18' } })).json();
assert.equal(init.result.serverInfo.name, 'toolpick');
const list = await (await mcp(fetchApp, { method: 'tools/list' })).json();
assert.deepEqual(list.result.tools.map((t) => t.name), ['find_paid_api']);
const call = await (await mcp(fetchApp, { method: 'tools/call', params: { name: 'find_paid_api', arguments: { need: 'solana token safety' } } })).json();
assert.equal(call.result.isError, false); assert.match(call.result.content[0].text, /SolSafe/);
const bad = await (await mcp(fetchApp, { method: 'tools/call', params: { name: 'find_paid_api', arguments: { need: '!' } } })).json();
assert.equal(bad.result.isError, true);
});
test('discovery: info JSON, landing HTML, openapi (paid + preview only), /.well-known/x402, favicon PNG, health', async () => {
const { fetchApp } = await makeApp();
const info = await (await fetchApp(`${BASE}/`, { headers: { accept: 'application/json' } })).json();
assert.equal(info.name, 'ToolPick'); assert.deepEqual(info.paid.map((p) => [p.path, p.price]), [['/v1/lite', '$0.003'], ['/v1/pick', '$0.01'], ['/v1/market', '$0.02']]);
assert.match(info.policy.custody, /never pays/);
const html = await (await fetchApp(`${BASE}/`, { headers: { accept: 'text/html' } })).text();
assert.match(html, /Tool<span>Pick/); assert.match(html, /\/logo\.png/); assert.ok(!/<svg/.test(html), 'no code-drawn logo');
const oa = await (await fetchApp(`${BASE}/openapi.json`)).json();
assert.deepEqual(Object.keys(oa.paths).sort(), ['/v1/lite', '/v1/market', '/v1/pick', '/v1/preview']);
assert.equal(oa.paths['/v1/pick'].get['x-payment-info'].price.amount, '0.01'); assert.equal(oa.servers[0].url, BASE);
const wk = await (await fetchApp(`${BASE}/.well-known/x402`)).json();
assert.deepEqual(wk.resources, ['lite', 'pick', 'market'].map((t) => `${BASE}/v1/${t}`));
for (const p of ['/favicon.ico', '/favicon.png', '/logo.png']) { const r = await fetchApp(`${BASE}${p}`); assert.equal(r.headers.get('content-type'), 'image/png'); const b = new Uint8Array(await r.arrayBuffer()); assert.deepEqual([...b.slice(1, 4)], [80, 78, 71]); }
assert.equal((await fetchApp(`${BASE}/health`)).status, 200);
const hs = await (await fetchApp(`${BASE}/health?sources=1`)).json(); assert.equal(hs.sources.cdp_discovery.status, 200); assert.equal(hs.sources.payai_discovery.status, 200);
});
test('stdio MCP: validates locally, uses the hosted preview, no key = free mode, market is paid-only', async () => {
assert.deepEqual(STDIO_TOOLS.map((t) => t.name), ['find_paid_api', 'x402_market_report']);
assert.equal(toQuery({ need: ' web search ' }).need, 'web search'); assert.ok(toQuery({}).error); assert.ok(toQuery({ need: 'web', network: 'tron' }).error);
const seen = [];
const fetchImpl = async (u) => { seen.push(String(u)); return Response.json({ need: 'pdf to text', matches: 12, hosts: 7, top: [{ service: 'PdfCo', price_usd: 0.02, payers_30d: 2, networks: ['base'] }], confidence: 'high' }); };
const r = await stdioHandle({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'find_paid_api', arguments: { need: 'pdf to text' } } }, { fetchImpl, env: {} });
assert.match(r.result.content[0].text, /ToolPick "pdf to text": 12 matching/);
assert.equal(seen[0], 'https://api.youraitools.co/toolpick/v1/preview?need=pdf+to+text');
const s = await stdioHandle({ jsonrpc: '2.0', id: 2, method: 'tools/call', params: { name: 'x402_market_report', arguments: {} } }, { fetchImpl, env: {} });
assert.equal(s.result.isError, true); assert.match(s.result.content[0].text, /paid only/);
});
test('route descriptions fit facilitator limits (<=500 chars, CDP rejects longer)', async () => {
const { ROUTES } = await import('../src/tool.js');
for (const r of ROUTES) assert.ok(r.desc.length <= 500, `${r.tier} desc ${r.desc.length}`);
});
test('empty need on lite and pick is a 400 before the paywall (GET ?need=, POST {"need":""}); bare probes still 402; market unaffected', async () => {
const net = mockNet(); const { fetchApp } = await makeApp({ net }); const before = net.calls.length;
for (const tier of ['lite', 'pick']) {
for (const [u, init] of [[`${BASE}/v1/${tier}?need=`], [`${BASE}/v1/${tier}?q=`], [`${BASE}/v1/${tier}`, json({ need: '' })]]) {
const r = await fetchApp(u, init);
assert.equal(r.status, 400, `${init?.method || 'GET'} ${u}`);
assert.equal(r.headers.get('PAYMENT-REQUIRED'), null);
assert.match(JSON.stringify(await r.json()), /need/);
}
assert.equal((await fetchApp(`${BASE}/v1/${tier}`)).status, 402);
}
assert.equal((await fetchApp(`${BASE}/v1/market?need=`)).status, 402);
assert.equal(net.calls.length, before, 'no upstream call');
});
test('engine label is toolpick-0.1.2 on /health and the root page; free MCP answer names all 4 networks', async () => {
const net = mockNet(); const { fetchApp } = await makeApp({ net });
assert.equal((await (await fetchApp(`${BASE}/health`)).json()).engine, 'toolpick-0.1.2');
const root = await fetchApp(`${BASE}/`, { headers: { accept: 'application/json' } });
assert.match(await root.text(), /toolpick-0\.1\.2/);
const r = await mcp(fetchApp, { jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'find_paid_api', arguments: { need: 'web search' } } });
const text = (await r.json()).result.content[0].text;
assert.match(text, /Base, Polygon, Arbitrum One or Solana/);
assert.doesNotMatch(text, /Base or Solana\./);
});
agentgate-source: code mirror on Cloudflare Pages. All repos