From d4d93191c4ca768485bd0df8415ce3c22cffea77 Mon Sep 17 00:00:00 2001 From: MalyisGreat Date: Wed, 27 May 2026 15:34:19 -0500 Subject: [PATCH 1/6] Improve Pyrimid seed paid endpoint outputs --- app/api/v1/paid/[product]/route.ts | 201 +++++++++++++++++++++++++++-- 1 file changed, 193 insertions(+), 8 deletions(-) diff --git a/app/api/v1/paid/[product]/route.ts b/app/api/v1/paid/[product]/route.ts index 65076eb..c36ca46 100644 --- a/app/api/v1/paid/[product]/route.ts +++ b/app/api/v1/paid/[product]/route.ts @@ -52,23 +52,181 @@ function payload(productId: string, req: NextRequest, proof: string) { }; } case 'vendor-lead-discovery': { - const segment = query.segment || 'mcp'; + const segment = String(query.segment || 'mcp').toLowerCase(); + const leadSets: Record>> = { + mcp: [ + { + target: 'Hosted MCP servers with metered data tools', + fit_score: 94, + reason: 'They already expose machine-readable tools, so adding a paid HTTP 402 gate does not change the agent workflow.', + discovery_queries: ['site:smithery.ai "pricing"', 'site:mcpmarket.com "API"', '"mcp server" "data enrichment"'], + suggested_paid_tool: 'premium_search', + price_usdc: '0.05-0.25', + affiliate_bps: 2500, + outreach_hook: 'Your MCP server already has agent distribution; Pyrimid can turn the highest-cost tool into a paid endpoint with USDC settlement.', + }, + { + target: 'MCP wrappers around paid SaaS APIs', + fit_score: 89, + reason: 'The vendor has direct marginal API cost and a clear buyer value story for per-call pricing.', + discovery_queries: ['"MCP" "API key" "enrich"', '"MCP server" "search API"', '"MCP" "credits"'], + suggested_paid_tool: 'enrich_company', + price_usdc: '0.10-0.50', + affiliate_bps: 2000, + outreach_hook: 'Move API-key friction behind x402 so agents can buy one result without creating a vendor account first.', + }, + { + target: 'Open-source MCP tools with hosted demos', + fit_score: 82, + reason: 'They can keep the local tool free while selling hosted execution, queue priority, or fresh data.', + discovery_queries: ['"mcp" "hosted" "demo"', '"mcp server" "deploy"', '"MCP" "cloud" "tool"'], + suggested_paid_tool: 'hosted_run', + price_usdc: '0.02-0.15', + affiliate_bps: 1500, + outreach_hook: 'Keep open source free and monetize the hosted path agents can call reliably in production.', + }, + ], + 'agent-frameworks': [ + { + target: 'Agent frameworks with plugin or tool marketplaces', + fit_score: 91, + reason: 'A default commerce resolver lets every downstream agent recommend and buy tools without custom payment code.', + discovery_queries: ['"agent framework" "marketplace"', '"AI agent" "plugin marketplace"', '"tool registry" "agent"'], + suggested_paid_tool: 'catalog_recommendation', + price_usdc: '0.01-0.10', + affiliate_bps: 3000, + outreach_hook: 'Pyrimid can be the paid-tool layer your framework does not have to build.', + }, + { + target: 'Vertical agents with recurring user questions', + fit_score: 84, + reason: 'Agents that repeatedly call data or research tools can route buyers into paid calls at the moment of intent.', + discovery_queries: ['"AI agent" "research API"', '"agent" "lead generation"', '"agent" "market data"'], + suggested_paid_tool: 'paid_recommendation', + price_usdc: '0.05-0.25', + affiliate_bps: 3500, + outreach_hook: 'Let the agent earn when it recommends a paid result instead of handing off to a subscription page.', + }, + ], + 'api-tools': [ + { + target: 'AI data APIs with free demos and paid plans', + fit_score: 93, + reason: 'They already sell per-seat or credit-based access; x402 adds one-off agent purchases.', + discovery_queries: ['"AI API" "free tier" "pricing"', '"data API" "credits"', '"enrichment API" "pricing"'], + suggested_paid_tool: 'single_lookup', + price_usdc: '0.05-1.00', + affiliate_bps: 2000, + outreach_hook: 'Add a no-login paid endpoint for agents that only need one result.', + }, + { + target: 'Scraping and enrichment services', + fit_score: 87, + reason: 'Their compute and proxy costs map naturally to per-call USDC pricing and clear output schemas.', + discovery_queries: ['"web scraping API" "per request"', '"lead enrichment" "API"', '"SERP API" "pricing"'], + suggested_paid_tool: 'fresh_record', + price_usdc: '0.02-0.40', + affiliate_bps: 1500, + outreach_hook: 'Agent buyers want one clean JSON result, not a dashboard subscription.', + }, + ], + }; + const leads = leadSets[segment] || leadSets.mcp; return { segment, - leads: [ - { segment: 'mcp', target: 'MCP servers with paid/data-heavy tools', pitch: 'Add optional x402 payment gate + Pyrimid catalog listing.' }, - { segment: 'agent-frameworks', target: 'Agent frameworks with marketplace/plugin systems', pitch: 'Let builders sell tools to agents with Base USDC settlement.' }, - { segment: 'api-tools', target: 'AI API services with per-call cost', pitch: 'Turn API calls into agent-purchasable products.' }, - ], + lead_count: leads.length, + scoring_model: { + high_fit: 'Existing tool/API surface + per-call value + machine-readable output + low account friction.', + reject_if: ['requires private user data', 'no API or tool endpoint', 'unclear marginal value', 'no public pricing signal'], + }, + leads, + next_actions: leads.map((lead) => ({ + target: lead.target, + action: `Run discovery query: ${(lead.discovery_queries as string[])[0]}`, + submit_to_catalog: { + vendor_id_hint: String(lead.target).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''), + product_id_hint: lead.suggested_paid_tool, + affiliate_bps: lead.affiliate_bps, + }, + })), }; } case 'mcp-server-audit': { const url = query.url || 'https://example.com/mcp'; + const normalizedUrl = normalizeUrl(url); + const host = safeHost(normalizedUrl); + const serverKind = inferMcpServerKind(normalizedUrl); return { audit: { url, - recommended_paid_tools: ['search', 'enrich', 'export', 'analyze'], - pricing: '$0.01-$0.25 per call depending on compute/data cost', + normalized_url: normalizedUrl, + server_kind: serverKind, + recommended_paid_tools: [ + { + name: 'premium_search', + route: '/api/paid/search', + price_usdc: '0.03-0.10', + value: 'Fresh indexed search or higher result limits.', + output_schema: { results: 'array', citations: 'array', freshness: 'string' }, + }, + { + name: 'enrich', + route: '/api/paid/enrich', + price_usdc: '0.10-0.50', + value: 'Expensive third-party API calls, enrichment, or entity matching.', + output_schema: { entity: 'object', confidence: 'number', sources: 'array' }, + }, + { + name: 'export', + route: '/api/paid/export', + price_usdc: '0.05-0.25', + value: 'Structured file generation, bulk export, or normalized JSON download.', + output_schema: { download_url: 'string', row_count: 'number', expires_at: 'string' }, + }, + { + name: 'analyze', + route: '/api/paid/analyze', + price_usdc: '0.15-1.00', + value: 'LLM, crawling, browser, or compute-heavy analysis that should not be free.', + output_schema: { summary: 'string', recommendations: 'array', risk_notes: 'array' }, + }, + ], + pricing: { + default_range: '$0.01-$0.25 per call', + raise_price_when: ['external API cost is non-trivial', 'browser automation is required', 'fresh data must be fetched', 'LLM tokens are used'], + keep_free_when: ['static metadata', 'health checks', 'tool discovery', 'documentation'], + }, + route_shape: { + unpaid_response: { + status: 402, + body: { + error: 'payment_required', + accepts: [ + { + scheme: 'exact', + network: 'base', + asset: 'USDC', + maxAmountRequired: '0.10', + resource: `https://${host}/api/paid/analyze`, + mimeType: 'application/json', + }, + ], + }, + }, + paid_retry_headers: ['X-PAYMENT', 'X-PAYMENT-TX'], + }, + catalog_metadata: { + vendor_id_hint: host.replace(/[^a-z0-9]+/gi, '-').toLowerCase(), + product_id_hint: `${serverKind}-paid-tool`, + categories: ['mcp-tools', 'agent-commerce', 'developer-tools'], + tags: ['mcp', 'x402', 'base-usdc', serverKind], + affiliate_bps_recommendation: 1500, + }, + risk_notes: [ + 'Do not put authentication-only tools behind x402 if they still require a separate vendor account.', + 'Avoid charging for static MCP metadata; charge for fresh data, compute, or premium limits.', + 'Return deterministic JSON schemas so buyer agents can evaluate the purchase automatically.', + ], integration_steps: [ 'Add 402 response with x402 accepts[] metadata', 'Register vendor/product in Pyrimid catalog', @@ -95,6 +253,33 @@ function payload(productId: string, req: NextRequest, proof: string) { } } +function normalizeUrl(input: string) { + try { + const url = new URL(input); + url.hash = ''; + return url.toString().replace(/\/$/, ''); + } catch { + return 'https://example.com/mcp'; + } +} + +function safeHost(input: string) { + try { + return new URL(input).host || 'example.com'; + } catch { + return 'example.com'; + } +} + +function inferMcpServerKind(input: string) { + const text = input.toLowerCase(); + if (text.includes('search')) return 'search'; + if (text.includes('data') || text.includes('enrich')) return 'data'; + if (text.includes('audit') || text.includes('security')) return 'audit'; + if (text.includes('agent')) return 'agent-discovery'; + return 'general'; +} + export async function GET(req: NextRequest, context: { params: Promise<{ product: string }> }) { const { product: productId } = await context.params; const product = getSeedProduct(productId); From 8afc68b04ac4623529b00320eb2e25804d546e61 Mon Sep 17 00:00:00 2001 From: MalyisGreat Date: Wed, 27 May 2026 15:51:29 -0500 Subject: [PATCH 2/6] Improve paid seed endpoint discovery logic --- app/api/v1/paid/[product]/route.ts | 299 +++++++++++++++++++++++++++-- 1 file changed, 284 insertions(+), 15 deletions(-) diff --git a/app/api/v1/paid/[product]/route.ts b/app/api/v1/paid/[product]/route.ts index c36ca46..44aaf1e 100644 --- a/app/api/v1/paid/[product]/route.ts +++ b/app/api/v1/paid/[product]/route.ts @@ -24,7 +24,29 @@ function paymentRequired(req: NextRequest, product: NonNullable>> = { + const leadSets: Record = { mcp: [ { target: 'Hosted MCP servers with metered data tools', @@ -131,24 +153,39 @@ function payload(productId: string, req: NextRequest, proof: string) { }, ], }; - const leads = leadSets[segment] || leadSets.mcp; + const leadProfiles = leadSets[segment] || leadSets.mcp; + const discovery = await discoverVendorLeads(leadProfiles, query); + const leads = discovery.leads.length > 0 ? discovery.leads : leadProfiles.map((lead) => ({ + ...lead, + source: 'segment_profile', + evidence: ['static segment profile'], + catalog_metadata_hint: catalogMetadataForLead(lead), + })); return { segment, lead_count: leads.length, + discovery: { + source: 'github_repository_search', + searched_queries: discovery.searched_queries, + errors: discovery.errors, + fallback_used: discovery.leads.length === 0, + generated_at: new Date().toISOString(), + }, scoring_model: { - high_fit: 'Existing tool/API surface + per-call value + machine-readable output + low account friction.', + high_fit: 'Existing GitHub/API/MCP surface + per-call value + machine-readable output + low account friction.', reject_if: ['requires private user data', 'no API or tool endpoint', 'unclear marginal value', 'no public pricing signal'], }, leads, - next_actions: leads.map((lead) => ({ - target: lead.target, - action: `Run discovery query: ${(lead.discovery_queries as string[])[0]}`, - submit_to_catalog: { - vendor_id_hint: String(lead.target).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''), - product_id_hint: lead.suggested_paid_tool, - affiliate_bps: lead.affiliate_bps, - }, - })), + strategy_templates: leadProfiles, + next_actions: leads.map((lead) => { + const leadUrl = 'url' in lead ? lead.url : ''; + const discoveryQuery = 'discovery_query' in lead ? lead.discovery_query : lead.discovery_queries[0]; + return { + target: lead.target, + action: leadUrl ? `Review ${leadUrl} and contact the maintainer with the outreach hook.` : `Run discovery query: ${discoveryQuery}`, + submit_to_catalog: lead.catalog_metadata_hint, + }; + }), }; } case 'mcp-server-audit': { @@ -156,11 +193,13 @@ function payload(productId: string, req: NextRequest, proof: string) { const normalizedUrl = normalizeUrl(url); const host = safeHost(normalizedUrl); const serverKind = inferMcpServerKind(normalizedUrl); + const targetInspection = await inspectMcpTarget(normalizedUrl); return { audit: { url, normalized_url: normalizedUrl, server_kind: serverKind, + inspection: targetInspection, recommended_paid_tools: [ { name: 'premium_search', @@ -219,7 +258,7 @@ function payload(productId: string, req: NextRequest, proof: string) { vendor_id_hint: host.replace(/[^a-z0-9]+/gi, '-').toLowerCase(), product_id_hint: `${serverKind}-paid-tool`, categories: ['mcp-tools', 'agent-commerce', 'developer-tools'], - tags: ['mcp', 'x402', 'base-usdc', serverKind], + tags: ['mcp', 'x402', 'base-usdc', serverKind, ...targetInspection.detected_features.slice(0, 4)], affiliate_bps_recommendation: 1500, }, risk_notes: [ @@ -253,6 +292,236 @@ function payload(productId: string, req: NextRequest, proof: string) { } } +function githubHeaders() { + const headers: Record = { + Accept: 'application/vnd.github+json', + 'User-Agent': 'pyrimid-seed-paid-endpoint', + }; + if (process.env.GITHUB_TOKEN) { + headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`; + } + return headers; +} + +async function discoverVendorLeads(leadProfiles: LeadProfile[], query: Record) { + const requestedLimit = Number.parseInt(query.limit || '6', 10); + const limit = Number.isFinite(requestedLimit) ? Math.min(Math.max(requestedLimit, 1), 10) : 6; + const customQuery = query.q?.trim(); + const queryPlan = customQuery + ? [{ search: customQuery, profile: leadProfiles[0] }] + : leadProfiles.flatMap((profile) => profile.discovery_queries.slice(0, 2).map((search) => ({ search, profile }))).slice(0, 5); + + const leads = new Map>(); + const errors: Array<{ query: string; status?: number; message: string }> = []; + + for (const { search, profile } of queryPlan) { + try { + const url = `https://api.github.com/search/repositories?q=${encodeURIComponent(`${search} archived:false`)}&sort=updated&order=desc&per_page=5`; + const response = await fetch(url, { headers: githubHeaders(), cache: 'no-store' }); + if (!response.ok) { + errors.push({ query: search, status: response.status, message: `GitHub search failed with HTTP ${response.status}` }); + continue; + } + const data = await response.json() as { items?: GitHubRepositoryItem[] }; + for (const item of data.items || []) { + const candidate = candidateFromRepository(item, profile, search); + if (!candidate.repository) continue; + const existing = leads.get(candidate.repository); + if (!existing || candidate.fit_score > existing.fit_score) { + leads.set(candidate.repository, candidate); + } + } + } catch (err) { + errors.push({ query: search, message: err instanceof Error ? err.message : 'Unknown GitHub search error' }); + } + } + + return { + searched_queries: queryPlan.map((item) => item.search), + errors, + leads: Array.from(leads.values()).sort((a, b) => b.fit_score - a.fit_score).slice(0, limit), + }; +} + +function candidateFromRepository(item: GitHubRepositoryItem, profile: LeadProfile, discoveryQuery: string) { + const repository = item.full_name || ''; + const description = item.description || ''; + const topics = item.topics || []; + const evidenceText = `${repository} ${description} ${topics.join(' ')}`.toLowerCase(); + const evidence = [ + repository ? `repository:${repository}` : '', + item.stargazers_count !== undefined ? `stars:${item.stargazers_count}` : '', + item.language ? `language:${item.language}` : '', + topics.length ? `topics:${topics.join(',')}` : '', + ].filter(Boolean); + + return { + source: 'github_search', + target: repository, + repository, + url: item.html_url || '', + description, + stars: item.stargazers_count || 0, + language: item.language || 'unknown', + topics, + updated_at: item.updated_at, + fit_score: scoreRepositoryLead(profile.fit_score, evidenceText, item.stargazers_count || 0), + reason: profile.reason, + discovery_query: discoveryQuery, + suggested_paid_tool: profile.suggested_paid_tool, + price_usdc: profile.price_usdc, + affiliate_bps: profile.affiliate_bps, + outreach_hook: profile.outreach_hook, + catalog_metadata_hint: catalogMetadataForLead(profile, repository || description), + evidence, + }; +} + +function scoreRepositoryLead(baseScore: number, evidenceText: string, stars: number) { + let score = baseScore; + if (evidenceText.includes('mcp')) score += 5; + if (evidenceText.includes('x402') || evidenceText.includes('payment')) score += 4; + if (evidenceText.includes('api') || evidenceText.includes('tool')) score += 3; + if (evidenceText.includes('pricing') || evidenceText.includes('paid')) score += 3; + if (stars > 500) score += 4; + else if (stars > 100) score += 2; + else if (stars < 5) score -= 6; + return Math.min(Math.max(score, 40), 98); +} + +function catalogMetadataForLead(lead: LeadProfile, seed?: string) { + const vendorSeed = seed || lead.target; + return { + vendor_id_hint: vendorSeed.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 60), + product_id_hint: lead.suggested_paid_tool, + affiliate_bps: lead.affiliate_bps, + price_usdc: lead.price_usdc, + }; +} + +async function inspectMcpTarget(normalizedUrl: string) { + const githubRepo = parseGitHubRepo(normalizedUrl); + if (githubRepo) { + return inspectGitHubRepo(githubRepo.owner, githubRepo.repo); + } + + const detected = new Set(); + const missing = new Set(['mcp_manifest', 'llms_txt', 'agents_txt', 'x402_payment_metadata']); + const evidenceUrls: string[] = []; + const errors: string[] = []; + const base = new URL(normalizedUrl); + const probes = [ + { feature: 'mcp_manifest', url: new URL('/.well-known/mcp.json', base).toString() }, + { feature: 'llms_txt', url: new URL('/llms.txt', base).toString() }, + { feature: 'agents_txt', url: new URL('/agents.txt', base).toString() }, + ]; + + for (const probe of probes) { + try { + const response = await fetch(probe.url, { method: 'GET', cache: 'no-store' }); + if (response.ok) { + detected.add(probe.feature); + missing.delete(probe.feature); + evidenceUrls.push(probe.url); + } + } catch (err) { + errors.push(`${probe.feature}: ${err instanceof Error ? err.message : 'probe failed'}`); + } + } + + return { + inspected_as: 'url', + detected_features: Array.from(detected), + missing_features: Array.from(missing), + evidence_urls: evidenceUrls, + errors, + }; +} + +function parseGitHubRepo(input: string) { + try { + const url = new URL(input); + if (url.hostname !== 'github.com') return null; + const [owner, repo] = url.pathname.split('/').filter(Boolean); + if (!owner || !repo) return null; + return { owner, repo: repo.replace(/\.git$/, '') }; + } catch { + return null; + } +} + +async function inspectGitHubRepo(owner: string, repo: string) { + const detected = new Set(); + const missing = new Set(['mcp_manifest', 'llms_txt', 'agents_txt', 'x402_payment_metadata']); + const evidenceUrls: string[] = []; + const errors: string[] = []; + let metadata: Record = {}; + + try { + const repoResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers: githubHeaders(), cache: 'no-store' }); + if (repoResponse.ok) { + const data = await repoResponse.json() as GitHubRepositoryItem; + metadata = { + repository: data.full_name, + stars: data.stargazers_count, + language: data.language, + topics: data.topics || [], + updated_at: data.updated_at, + }; + const repoText = `${data.full_name || ''} ${data.description || ''} ${(data.topics || []).join(' ')}`.toLowerCase(); + if (repoText.includes('mcp')) detected.add('mcp_signal'); + if (repoText.includes('x402') || repoText.includes('payment')) { + detected.add('x402_payment_metadata'); + missing.delete('x402_payment_metadata'); + } + } + } catch (err) { + errors.push(`repo_metadata: ${err instanceof Error ? err.message : 'metadata fetch failed'}`); + } + + const contentsUrl = `https://api.github.com/repos/${owner}/${repo}/contents`; + try { + const contentsResponse = await fetch(contentsUrl, { headers: githubHeaders(), cache: 'no-store' }); + if (contentsResponse.ok) { + const contents = await contentsResponse.json() as Array<{ name?: string; html_url?: string }>; + for (const item of contents) { + const name = (item.name || '').toLowerCase(); + if (name === 'llms.txt') { + detected.add('llms_txt'); + missing.delete('llms_txt'); + if (item.html_url) evidenceUrls.push(item.html_url); + } + if (name === 'agents.txt') { + detected.add('agents_txt'); + missing.delete('agents_txt'); + if (item.html_url) evidenceUrls.push(item.html_url); + } + if (name.includes('mcp')) { + detected.add('mcp_manifest'); + missing.delete('mcp_manifest'); + if (item.html_url) evidenceUrls.push(item.html_url); + } + if (name.includes('402') || name.includes('payment')) { + detected.add('x402_payment_metadata'); + missing.delete('x402_payment_metadata'); + if (item.html_url) evidenceUrls.push(item.html_url); + } + } + } + } catch (err) { + errors.push(`repo_contents: ${err instanceof Error ? err.message : 'contents fetch failed'}`); + } + + return { + inspected_as: 'github_repository', + metadata, + detected_features: Array.from(detected), + missing_features: Array.from(missing), + evidence_urls: evidenceUrls, + errors, + }; +} + function normalizeUrl(input: string) { try { const url = new URL(input); @@ -313,7 +582,7 @@ export async function GET(req: NextRequest, context: { params: Promise<{ product payment_tx: verification.txHash, payment_amount: verification.amount?.toString(), buyer: verification.buyer, - ...payload(product.product_id, req, proof), + ...(await payload(product.product_id, req, proof)), routed_by: 'pyrimid', links: { docs: 'https://pyrimid.ai/quickstart', From 4126910822acd570bb7a5159d3908d0a60368e82 Mon Sep 17 00:00:00 2001 From: MalyisGreat Date: Wed, 27 May 2026 16:35:12 -0500 Subject: [PATCH 3/6] Guard paid MCP audit URL probes --- app/api/v1/paid/[product]/route.ts | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/app/api/v1/paid/[product]/route.ts b/app/api/v1/paid/[product]/route.ts index 44aaf1e..b2178c5 100644 --- a/app/api/v1/paid/[product]/route.ts +++ b/app/api/v1/paid/[product]/route.ts @@ -410,6 +410,17 @@ async function inspectMcpTarget(normalizedUrl: string) { const evidenceUrls: string[] = []; const errors: string[] = []; const base = new URL(normalizedUrl); + const unsafeProbe = unsafeProbeReason(base); + if (unsafeProbe) { + return { + inspected_as: 'url', + detected_features: Array.from(detected), + missing_features: Array.from(missing), + evidence_urls: evidenceUrls, + errors: [unsafeProbe], + }; + } + const probes = [ { feature: 'mcp_manifest', url: new URL('/.well-known/mcp.json', base).toString() }, { feature: 'llms_txt', url: new URL('/llms.txt', base).toString() }, @@ -540,6 +551,56 @@ function safeHost(input: string) { } } +function unsafeProbeReason(url: URL) { + if (!['http:', 'https:'].includes(url.protocol)) { + return `network probes skipped for unsupported protocol: ${url.protocol}`; + } + + const hostname = url.hostname.toLowerCase().replace(/^\[|\]$/g, ''); + if ( + hostname === 'localhost' || + hostname === 'metadata.google.internal' || + hostname.endsWith('.local') || + hostname.endsWith('.internal') + ) { + return 'network probes skipped for local or internal hostname'; + } + + if (isPrivateIp(hostname)) { + return 'network probes skipped for private, loopback, or link-local IP'; + } + + return null; +} + +function isPrivateIp(hostname: string) { + const ipv4 = hostname.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/); + if (ipv4) { + const parts = ipv4.slice(1).map(Number); + if (parts.some((part) => part > 255)) return true; + const [first, second] = parts; + return ( + first === 0 || + first === 10 || + first === 127 || + first === 169 && second === 254 || + first === 172 && second >= 16 && second <= 31 || + first === 192 && second === 168 + ); + } + + return ( + hostname === '::1' || + hostname === '::' || + hostname.startsWith('fc') || + hostname.startsWith('fd') || + hostname.startsWith('fe80:') || + hostname.startsWith('::ffff:127.') || + hostname.startsWith('::ffff:10.') || + hostname.startsWith('::ffff:192.168.') + ); +} + function inferMcpServerKind(input: string) { const text = input.toLowerCase(); if (text.includes('search')) return 'search'; From cee237b232bb2b3b9b66ad7c8b5dd86678e650b5 Mon Sep 17 00:00:00 2001 From: MalyisGreat Date: Wed, 27 May 2026 17:17:43 -0500 Subject: [PATCH 4/6] Add timeouts to paid endpoint probes --- app/api/v1/paid/[product]/route.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/api/v1/paid/[product]/route.ts b/app/api/v1/paid/[product]/route.ts index b2178c5..1a2c067 100644 --- a/app/api/v1/paid/[product]/route.ts +++ b/app/api/v1/paid/[product]/route.ts @@ -303,6 +303,10 @@ function githubHeaders() { return headers; } +function fetchWithTimeout(url: string, init: RequestInit = {}, timeoutMs = 3000) { + return fetch(url, { ...init, signal: init.signal || AbortSignal.timeout(timeoutMs) }); +} + async function discoverVendorLeads(leadProfiles: LeadProfile[], query: Record) { const requestedLimit = Number.parseInt(query.limit || '6', 10); const limit = Number.isFinite(requestedLimit) ? Math.min(Math.max(requestedLimit, 1), 10) : 6; @@ -317,7 +321,7 @@ async function discoverVendorLeads(leadProfiles: LeadProfile[], query: Record = {}; try { - const repoResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers: githubHeaders(), cache: 'no-store' }); + const repoResponse = await fetchWithTimeout(`https://api.github.com/repos/${owner}/${repo}`, { headers: githubHeaders(), cache: 'no-store' }); if (repoResponse.ok) { const data = await repoResponse.json() as GitHubRepositoryItem; metadata = { @@ -492,7 +496,7 @@ async function inspectGitHubRepo(owner: string, repo: string) { const contentsUrl = `https://api.github.com/repos/${owner}/${repo}/contents`; try { - const contentsResponse = await fetch(contentsUrl, { headers: githubHeaders(), cache: 'no-store' }); + const contentsResponse = await fetchWithTimeout(contentsUrl, { headers: githubHeaders(), cache: 'no-store' }); if (contentsResponse.ok) { const contents = await contentsResponse.json() as Array<{ name?: string; html_url?: string }>; for (const item of contents) { From 2ad0c32cb2c06c78280101516f4cdb1a5c766a52 Mon Sep 17 00:00:00 2001 From: MalyisGreat Date: Wed, 27 May 2026 17:29:34 -0500 Subject: [PATCH 5/6] Describe improved seed output schemas --- lib/seed-products.ts | 81 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/seed-products.ts b/lib/seed-products.ts index c47b524..8414b38 100644 --- a/lib/seed-products.ts +++ b/lib/seed-products.ts @@ -123,7 +123,43 @@ export const SEED_PRODUCTS: Omit[] = [ affiliate_bps: 4000, endpoint: `${SEED_PRODUCT_BASE}/vendor-lead-discovery?segment=mcp`, method: 'GET', - output_schema: { type: 'object', properties: { leads: { type: 'array' }, routed_by: { const: 'pyrimid' } } }, + output_schema: { + type: 'object', + properties: { + segment: { type: 'string' }, + lead_count: { type: 'number' }, + discovery: { + type: 'object', + properties: { + source: { const: 'github_repository_search' }, + searched_queries: { type: 'array', items: { type: 'string' } }, + errors: { type: 'array' }, + fallback_used: { type: 'boolean' }, + generated_at: { type: 'string' }, + }, + }, + scoring_model: { type: 'object' }, + leads: { + type: 'array', + items: { + type: 'object', + properties: { + target: { type: 'string' }, + fit_score: { type: 'number' }, + reason: { type: 'string' }, + suggested_paid_tool: { type: 'string' }, + price_usdc: { type: 'string' }, + affiliate_bps: { type: 'number' }, + outreach_hook: { type: 'string' }, + catalog_metadata_hint: { type: 'object' }, + }, + }, + }, + strategy_templates: { type: 'array' }, + next_actions: { type: 'array' }, + routed_by: { const: 'pyrimid' }, + }, + }, monthly_volume: 0, monthly_buyers: 0, network: 'base', @@ -144,7 +180,48 @@ export const SEED_PRODUCTS: Omit[] = [ affiliate_bps: 4000, endpoint: `${SEED_PRODUCT_BASE}/mcp-server-audit?url=https://example.com/mcp`, method: 'GET', - output_schema: { type: 'object', properties: { audit: { type: 'object' }, routed_by: { const: 'pyrimid' } } }, + output_schema: { + type: 'object', + properties: { + audit: { + type: 'object', + properties: { + url: { type: 'string' }, + normalized_url: { type: 'string' }, + server_kind: { type: 'string' }, + inspection: { + type: 'object', + properties: { + inspected_as: { type: 'string' }, + detected_features: { type: 'array', items: { type: 'string' } }, + missing_features: { type: 'array', items: { type: 'string' } }, + evidence_urls: { type: 'array', items: { type: 'string' } }, + errors: { type: 'array', items: { type: 'string' } }, + }, + }, + recommended_paid_tools: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + route: { type: 'string' }, + price_usdc: { type: 'string' }, + value: { type: 'string' }, + output_schema: { type: 'object' }, + }, + }, + }, + pricing: { type: 'object' }, + route_shape: { type: 'object' }, + catalog_metadata: { type: 'object' }, + risk_notes: { type: 'array', items: { type: 'string' } }, + integration_steps: { type: 'array', items: { type: 'string' } }, + }, + }, + routed_by: { const: 'pyrimid' }, + }, + }, monthly_volume: 0, monthly_buyers: 0, network: 'base', From 087b1e236f08f3bd1b4eb801095a22f9e398bb20 Mon Sep 17 00:00:00 2001 From: MalyisGreat Date: Wed, 27 May 2026 18:57:48 -0500 Subject: [PATCH 6/6] Tighten paid seed discovery outputs --- app/api/v1/paid/[product]/route.ts | 111 +++++++++++++++++++++++++---- lib/seed-products.ts | 59 ++++++++++++++- 2 files changed, 153 insertions(+), 17 deletions(-) diff --git a/app/api/v1/paid/[product]/route.ts b/app/api/v1/paid/[product]/route.ts index 1a2c067..a27b261 100644 --- a/app/api/v1/paid/[product]/route.ts +++ b/app/api/v1/paid/[product]/route.ts @@ -81,7 +81,11 @@ async function payload(productId: string, req: NextRequest, proof: string) { target: 'Hosted MCP servers with metered data tools', fit_score: 94, reason: 'They already expose machine-readable tools, so adding a paid HTTP 402 gate does not change the agent workflow.', - discovery_queries: ['site:smithery.ai "pricing"', 'site:mcpmarket.com "API"', '"mcp server" "data enrichment"'], + discovery_queries: [ + 'topic:mcp-server pricing archived:false', + 'topic:mcp-server data enrichment archived:false', + 'topic:mcp-server paid tools archived:false', + ], suggested_paid_tool: 'premium_search', price_usdc: '0.05-0.25', affiliate_bps: 2500, @@ -91,7 +95,11 @@ async function payload(productId: string, req: NextRequest, proof: string) { target: 'MCP wrappers around paid SaaS APIs', fit_score: 89, reason: 'The vendor has direct marginal API cost and a clear buyer value story for per-call pricing.', - discovery_queries: ['"MCP" "API key" "enrich"', '"MCP server" "search API"', '"MCP" "credits"'], + discovery_queries: [ + 'topic:mcp-server api key archived:false', + 'topic:mcp-server enrichment archived:false', + 'topic:mcp-server search api archived:false', + ], suggested_paid_tool: 'enrich_company', price_usdc: '0.10-0.50', affiliate_bps: 2000, @@ -101,7 +109,11 @@ async function payload(productId: string, req: NextRequest, proof: string) { target: 'Open-source MCP tools with hosted demos', fit_score: 82, reason: 'They can keep the local tool free while selling hosted execution, queue priority, or fresh data.', - discovery_queries: ['"mcp" "hosted" "demo"', '"mcp server" "deploy"', '"MCP" "cloud" "tool"'], + discovery_queries: [ + 'topic:mcp-server hosted archived:false', + 'topic:mcp-server deploy cloud archived:false', + 'topic:mcp-server cloud tool archived:false', + ], suggested_paid_tool: 'hosted_run', price_usdc: '0.02-0.15', affiliate_bps: 1500, @@ -113,7 +125,11 @@ async function payload(productId: string, req: NextRequest, proof: string) { target: 'Agent frameworks with plugin or tool marketplaces', fit_score: 91, reason: 'A default commerce resolver lets every downstream agent recommend and buy tools without custom payment code.', - discovery_queries: ['"agent framework" "marketplace"', '"AI agent" "plugin marketplace"', '"tool registry" "agent"'], + discovery_queries: [ + 'topic:ai-agents marketplace framework archived:false', + 'topic:ai-agent plugin marketplace archived:false', + 'tool registry agent framework in:name,description archived:false', + ], suggested_paid_tool: 'catalog_recommendation', price_usdc: '0.01-0.10', affiliate_bps: 3000, @@ -123,7 +139,11 @@ async function payload(productId: string, req: NextRequest, proof: string) { target: 'Vertical agents with recurring user questions', fit_score: 84, reason: 'Agents that repeatedly call data or research tools can route buyers into paid calls at the moment of intent.', - discovery_queries: ['"AI agent" "research API"', '"agent" "lead generation"', '"agent" "market data"'], + discovery_queries: [ + 'topic:ai-agent research api archived:false', + 'topic:ai-agent lead generation api archived:false', + 'topic:ai-agent market data api archived:false', + ], suggested_paid_tool: 'paid_recommendation', price_usdc: '0.05-0.25', affiliate_bps: 3500, @@ -135,7 +155,11 @@ async function payload(productId: string, req: NextRequest, proof: string) { target: 'AI data APIs with free demos and paid plans', fit_score: 93, reason: 'They already sell per-seat or credit-based access; x402 adds one-off agent purchases.', - discovery_queries: ['"AI API" "free tier" "pricing"', '"data API" "credits"', '"enrichment API" "pricing"'], + discovery_queries: [ + 'ai api pricing free tier in:name,description archived:false', + 'data api credits pricing in:name,description archived:false', + 'enrichment api pricing in:name,description archived:false', + ], suggested_paid_tool: 'single_lookup', price_usdc: '0.05-1.00', affiliate_bps: 2000, @@ -145,7 +169,11 @@ async function payload(productId: string, req: NextRequest, proof: string) { target: 'Scraping and enrichment services', fit_score: 87, reason: 'Their compute and proxy costs map naturally to per-call USDC pricing and clear output schemas.', - discovery_queries: ['"web scraping API" "per request"', '"lead enrichment" "API"', '"SERP API" "pricing"'], + discovery_queries: [ + 'web scraping api pricing in:name,description archived:false', + 'lead enrichment api pricing in:name,description archived:false', + 'serp api pricing in:name,description archived:false', + ], suggested_paid_tool: 'fresh_record', price_usdc: '0.02-0.40', affiliate_bps: 1500, @@ -206,28 +234,60 @@ async function payload(productId: string, req: NextRequest, proof: string) { route: '/api/paid/search', price_usdc: '0.03-0.10', value: 'Fresh indexed search or higher result limits.', - output_schema: { results: 'array', citations: 'array', freshness: 'string' }, + output_schema: { + type: 'object', + properties: { + results: { type: 'array', items: { type: 'object' } }, + citations: { type: 'array', items: { type: 'string' } }, + freshness: { type: 'string' }, + }, + required: ['results', 'citations', 'freshness'], + }, }, { name: 'enrich', route: '/api/paid/enrich', price_usdc: '0.10-0.50', value: 'Expensive third-party API calls, enrichment, or entity matching.', - output_schema: { entity: 'object', confidence: 'number', sources: 'array' }, + output_schema: { + type: 'object', + properties: { + entity: { type: 'object' }, + confidence: { type: 'number' }, + sources: { type: 'array', items: { type: 'string' } }, + }, + required: ['entity', 'confidence', 'sources'], + }, }, { name: 'export', route: '/api/paid/export', price_usdc: '0.05-0.25', value: 'Structured file generation, bulk export, or normalized JSON download.', - output_schema: { download_url: 'string', row_count: 'number', expires_at: 'string' }, + output_schema: { + type: 'object', + properties: { + download_url: { type: 'string' }, + row_count: { type: 'number' }, + expires_at: { type: 'string' }, + }, + required: ['download_url', 'row_count', 'expires_at'], + }, }, { name: 'analyze', route: '/api/paid/analyze', price_usdc: '0.15-1.00', value: 'LLM, crawling, browser, or compute-heavy analysis that should not be free.', - output_schema: { summary: 'string', recommendations: 'array', risk_notes: 'array' }, + output_schema: { + type: 'object', + properties: { + summary: { type: 'string' }, + recommendations: { type: 'array', items: { type: 'string' } }, + risk_notes: { type: 'array', items: { type: 'string' } }, + }, + required: ['summary', 'recommendations', 'risk_notes'], + }, }, ], pricing: { @@ -320,7 +380,8 @@ async function discoverVendorLeads(leadProfiles: LeadProfile[], query: Record 500) score += 4; else if (stars > 100) score += 2; else if (stars < 5) score -= 6; @@ -538,12 +608,25 @@ async function inspectGitHubRepo(owner: string, repo: string) { } function normalizeUrl(input: string) { + const trimmed = input.trim(); + if (!trimmed) return 'https://example.com/mcp'; + try { - const url = new URL(input); + const url = new URL(trimmed); url.hash = ''; return url.toString().replace(/\/$/, ''); } catch { - return 'https://example.com/mcp'; + const repoLike = trimmed.match(/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/); + if (repoLike) return `https://github.com/${repoLike[1]}/${repoLike[2].replace(/\.git$/, '')}`; + + try { + const withProtocol = trimmed.includes('://') ? trimmed : `https://${trimmed}`; + const url = new URL(withProtocol); + url.hash = ''; + return url.toString().replace(/\/$/, ''); + } catch { + return 'https://example.com/mcp'; + } } } diff --git a/lib/seed-products.ts b/lib/seed-products.ts index 8414b38..680e383 100644 --- a/lib/seed-products.ts +++ b/lib/seed-products.ts @@ -133,10 +133,21 @@ export const SEED_PRODUCTS: Omit[] = [ properties: { source: { const: 'github_repository_search' }, searched_queries: { type: 'array', items: { type: 'string' } }, - errors: { type: 'array' }, + errors: { + type: 'array', + items: { + type: 'object', + properties: { + query: { type: 'string' }, + status: { type: 'number' }, + message: { type: 'string' }, + }, + }, + }, fallback_used: { type: 'boolean' }, generated_at: { type: 'string' }, }, + required: ['source', 'searched_queries', 'errors', 'fallback_used', 'generated_at'], }, scoring_model: { type: 'object' }, leads: { @@ -144,21 +155,59 @@ export const SEED_PRODUCTS: Omit[] = [ items: { type: 'object', properties: { + source: { type: 'string' }, target: { type: 'string' }, + repository: { type: 'string' }, + url: { type: 'string' }, + description: { type: 'string' }, + stars: { type: 'number' }, + language: { type: 'string' }, + topics: { type: 'array', items: { type: 'string' } }, + updated_at: { type: 'string' }, fit_score: { type: 'number' }, reason: { type: 'string' }, + discovery_query: { type: 'string' }, suggested_paid_tool: { type: 'string' }, price_usdc: { type: 'string' }, affiliate_bps: { type: 'number' }, outreach_hook: { type: 'string' }, catalog_metadata_hint: { type: 'object' }, + evidence: { type: 'array', items: { type: 'string' } }, + }, + required: ['target', 'fit_score', 'reason', 'suggested_paid_tool', 'price_usdc', 'affiliate_bps', 'outreach_hook', 'catalog_metadata_hint'], + }, + }, + strategy_templates: { + type: 'array', + items: { + type: 'object', + properties: { + target: { type: 'string' }, + fit_score: { type: 'number' }, + reason: { type: 'string' }, + discovery_queries: { type: 'array', items: { type: 'string' } }, + suggested_paid_tool: { type: 'string' }, + price_usdc: { type: 'string' }, + affiliate_bps: { type: 'number' }, + outreach_hook: { type: 'string' }, + }, + }, + }, + next_actions: { + type: 'array', + items: { + type: 'object', + properties: { + target: { type: 'string' }, + action: { type: 'string' }, + submit_to_catalog: { type: 'object' }, }, + required: ['target', 'action', 'submit_to_catalog'], }, }, - strategy_templates: { type: 'array' }, - next_actions: { type: 'array' }, routed_by: { const: 'pyrimid' }, }, + required: ['segment', 'lead_count', 'discovery', 'scoring_model', 'leads', 'strategy_templates', 'next_actions', 'routed_by'], }, monthly_volume: 0, monthly_buyers: 0, @@ -198,6 +247,7 @@ export const SEED_PRODUCTS: Omit[] = [ evidence_urls: { type: 'array', items: { type: 'string' } }, errors: { type: 'array', items: { type: 'string' } }, }, + required: ['inspected_as', 'detected_features', 'missing_features', 'evidence_urls', 'errors'], }, recommended_paid_tools: { type: 'array', @@ -210,6 +260,7 @@ export const SEED_PRODUCTS: Omit[] = [ value: { type: 'string' }, output_schema: { type: 'object' }, }, + required: ['name', 'route', 'price_usdc', 'value', 'output_schema'], }, }, pricing: { type: 'object' }, @@ -218,9 +269,11 @@ export const SEED_PRODUCTS: Omit[] = [ risk_notes: { type: 'array', items: { type: 'string' } }, integration_steps: { type: 'array', items: { type: 'string' } }, }, + required: ['url', 'normalized_url', 'server_kind', 'inspection', 'recommended_paid_tools', 'pricing', 'route_shape', 'catalog_metadata', 'risk_notes', 'integration_steps'], }, routed_by: { const: 'pyrimid' }, }, + required: ['audit', 'routed_by'], }, monthly_volume: 0, monthly_buyers: 0,