Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 292 additions & 19 deletions app/api/v1/paid/[product]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,296 @@ function paymentRequired(req: NextRequest, product: NonNullable<ReturnType<typeo
);
}

function safeUrl(raw: string) {
try {
return new URL(raw);
} catch {
return null;
}
}

function slugFromUrl(raw: string) {
const parsed = safeUrl(raw);
const source = parsed?.hostname || raw.replace(/^https?:\/\//, '').split('/')[0] || 'mcp-server';
return source
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')
.slice(0, 48) || 'mcp-server';
}

function classifyMcpTarget(raw: string) {
const parsed = safeUrl(raw);
const text = raw.toLowerCase();
const isRepo = /github\.com|gitlab\.com|bitbucket\.org|\.git(?:$|[?#])/.test(text);
const likelyMcp = /\/mcp|\/sse|jsonrpc|modelcontextprotocol|server\.json|agents\.txt|llms\.txt/.test(text);

return {
kind: isRepo ? 'repository' : 'endpoint',
host: parsed?.hostname || null,
path: parsed?.pathname || null,
likely_mcp_surface: likelyMcp,
discovery_hints: [
`${parsed?.origin || raw}/.well-known/agent.json`,
`${parsed?.origin || raw}/agents.txt`,
`${parsed?.origin || raw}/llms.txt`,
`${parsed?.origin || raw}/server.json`,
],
};
}

function mcpServerAudit(rawUrl: string) {
const url = rawUrl || 'https://example.com/mcp';
const slug = slugFromUrl(url);
const target = classifyMcpTarget(url);
const paidToolBase = `/api/v1/paid/${slug}`;
const readinessScore = target.kind === 'repository'
? 72
: target.likely_mcp_surface
? 82
: 58;

return {
url,
target,
readiness_score: readinessScore,
findings: [
{
severity: target.likely_mcp_surface ? 'info' : 'medium',
area: 'discovery',
finding: target.likely_mcp_surface
? 'Input already looks like an MCP or agent-readable surface.'
: 'Input does not expose an obvious MCP path, so discovery files should be checked first.',
fix: 'Publish agents.txt, llms.txt, server.json, and a stable catalog entry before asking buyer agents to pay.',
},
{
severity: 'high',
area: 'payment-replay',
finding: 'Paid tools need resource-bound payment checks so one proof cannot unlock unrelated outputs.',
fix: 'Bind vendorId, productId, amount, asset, network, and resource URL in verification before returning paid data.',
},
{
severity: 'medium',
area: 'conversion',
finding: 'Buyer agents need a free preview with schema and sample output before they can choose a paid call.',
fix: 'Expose a preview_* tool that returns price, schema, and a redacted sample of the paid response.',
},
],
monetization_summary: {
best_first_product: 'paid_capability_audit',
reason: 'A compact paid audit is easy for buyer agents to evaluate, has low marginal cost, and demonstrates the 402 retry flow without changing every MCP tool at once.',
suggested_starting_price_usdc: 0.1,
suggested_affiliate_bps: 2000,
},
recommended_paid_tools: [
{
tool: 'paid_capability_audit',
why: 'Returns schema quality, missing discovery files, pricing candidates, and payment-readiness gaps.',
route: `${paidToolBase}/capability-audit`,
price_usdc: 0.1,
output_schema: {
type: 'object',
required: ['target', 'findings', 'recommended_tools', 'risk_notes'],
properties: {
target: { type: 'string' },
findings: { type: 'array', items: { type: 'object' } },
recommended_tools: { type: 'array', items: { type: 'object' } },
risk_notes: { type: 'array', items: { type: 'string' } },
},
},
},
{
tool: 'paid_schema_enrichment',
why: 'Turns a submitted MCP/server card into improved descriptions, examples, and buyer-agent metadata.',
route: `${paidToolBase}/schema-enrichment`,
price_usdc: 0.05,
output_schema: {
type: 'object',
properties: {
improved_tool_descriptions: { type: 'array' },
catalog_patch: { type: 'object' },
},
},
},
{
tool: 'paid_export_or_analysis',
why: 'Good fit for servers with expensive data, scraping, enrichment, or model calls.',
route: `${paidToolBase}/run`,
price_usdc: 0.25,
output_schema: {
type: 'object',
properties: {
result: { type: 'object' },
routed_by: { const: 'pyrimid' },
},
},
},
],
pricing: {
starter: '$0.05 for metadata/schema enrichment',
standard: '$0.10 for a paid audit or compact data lookup',
premium: '$0.25+ for expensive enrichment, scraping, model calls, or compliance checks',
rule_of_thumb: 'Price at 3x-10x marginal compute/data cost and keep a free preview tool for conversion.',
},
x402_route_shape: {
unpaid_request: {
method: 'GET',
endpoint: `${paidToolBase}/capability-audit?url=${encodeURIComponent(url)}`,
response_status: 402,
response_body: {
error: 'payment_required',
accepts: [
{
x402Version: 2,
scheme: 'exact',
network: 'base',
asset: 'USDC',
maxAmountRequired: '0.10',
resource: `${paidToolBase}/capability-audit?url=${encodeURIComponent(url)}`,
vendorId: slug,
productId: 'paid_capability_audit',
affiliateBps: 2000,
protocol: 'pyrimid',
},
],
},
},
paid_retry: {
required_header: 'X-PAYMENT or X-PAYMENT-TX',
success_status: 200,
verification: 'Verify tx/proof covers the exact resource, amount, productId, vendorId, and Base USDC asset before returning paid output.',
},
},
catalog_metadata: {
vendor_id: slug,
product_id: 'paid_capability_audit',
description: `Paid MCP monetization audit for ${url}`,
category: 'developer-tools',
tags: ['mcp', 'x402', 'paid-tools', 'audit', 'pyrimid'],
price_usdc: 100000,
affiliate_bps: 2000,
endpoint: `https://your-service.example${paidToolBase}/capability-audit`,
network: 'base',
asset: 'USDC',
},
risk_notes: [
'Do not put private API keys, raw secrets, or wallet signing actions behind buyer-supplied prompts.',
'Keep unpaid previews small enough to prove value without leaking the full paid result.',
'Bind every payment proof to a resource URL to prevent replay across tools.',
'Publish stable agents.txt, llms.txt, server.json, and catalog metadata so buyer agents can compare price and schema before paying.',
],
validation_checks: [
'Unpaid call returns HTTP 402 with accepts[0].network=base and accepts[0].asset=USDC.',
'Paid retry with a valid proof returns HTTP 200 and includes routed_by=pyrimid.',
'Catalog metadata price_usdc matches the 402 amount.',
'Affiliate bps is explicit and within expected policy range.',
'Output schema contains enough structure for agents to consume without scraping prose.',
],
next_actions: [
'Add a free preview_* MCP tool that returns schema, price, and sample output.',
'Gate one high-value buy_* tool with x402/Pyrimid first.',
'Publish catalog metadata and submit the product to Pyrimid discovery.',
'Log productId, vendorId, resource, and tx hash for support and affiliate reconciliation.',
],
};
}

const VENDOR_LEAD_SEGMENTS = {
mcp: {
summary: 'Best fit: remote MCP servers with expensive data access, enrichment, search, export, or analysis tools.',
discovery_queries: [
'site:github.com "Model Context Protocol" "tools"',
'site:github.com "/mcp" "server.json"',
'site:github.com "agents.txt" "llms.txt" "x402"',
],
leads: [
{
target: 'Remote MCP servers with data-heavy tools',
score: 94,
signals: ['already have agent-facing schemas', 'natural preview/paid split', 'JSON output is buyer-agent friendly'],
suggested_paid_product: 'paid_export_or_analysis',
pitch: 'Keep discovery free, then gate high-value exports or analysis with x402 through Pyrimid.',
},
{
target: 'MCP directories and registries',
score: 86,
signals: ['own discovery traffic', 'can route buyers to vendors', 'affiliate bps creates direct upside'],
suggested_paid_product: 'paid_listing_enrichment',
pitch: 'Add Pyrimid metadata so registry traffic can convert into paid agent purchases.',
},
],
},
'agent-frameworks': {
summary: 'Best fit: frameworks with plugin or tool marketplaces that can ship a paid-tool starter once.',
discovery_queries: [
'site:github.com agent framework marketplace tools',
'site:github.com agent framework "plugin" "payments"',
'site:github.com "x402" "agent framework"',
],
leads: [
{
target: 'Agent frameworks with plugin marketplaces',
score: 88,
signals: ['developers need monetization templates', 'SDK integration can be documented once', 'examples drive vendor adoption'],
suggested_paid_product: 'framework_paid_tool_starter',
pitch: 'Ship a starter that returns HTTP 402, verifies Base USDC, and publishes Pyrimid catalog metadata.',
},
],
},
'api-tools': {
summary: 'Best fit: API wrappers where each call consumes paid data, scraping, search, or model inference.',
discovery_queries: [
'site:github.com "api wrapper" "AI" "pricing"',
'site:github.com "paid API" "agents.txt"',
'site:github.com "x402" "API"',
],
leads: [
{
target: 'AI API services with per-call cost',
score: 91,
signals: ['usage-based value is already clear', 'one route can launch before a full marketplace', 'buyer agents can retry after 402'],
suggested_paid_product: 'paid_api_lookup',
pitch: 'Convert one expensive API call into a $0.05-$0.25 x402 product and publish agent discovery files.',
},
],
},
};

function vendorLeadDiscovery(rawSegment: string) {
const segment = rawSegment || 'mcp';
const selected = VENDOR_LEAD_SEGMENTS[segment as keyof typeof VENDOR_LEAD_SEGMENTS] || VENDOR_LEAD_SEGMENTS.mcp;

return {
segment,
summary: selected.summary,
scoring_fields: [
'has agent-readable schema or docs',
'has a costly or high-value output worth gating',
'can publish a stable HTTPS paid endpoint',
'can support Base USDC/x402 without KYC or manual invoicing',
'benefits from affiliate distribution',
],
leads: selected.leads.map((lead, index) => ({
rank: index + 1,
...lead,
catalog_fit: {
network: 'base',
asset: 'USDC',
starting_price_usdc: index === 0 ? 0.1 : 0.05,
affiliate_bps: 2000,
},
next_action: 'Verify a live endpoint, draft a free preview response, then submit a Pyrimid catalog entry for one paid tool.',
})),
discovery_queries: selected.discovery_queries,
reject_signals: [
'requires social spam, KYC, manual invoice, or off-platform negotiation',
'cannot expose a stable HTTPS endpoint',
'only sells subscriptions with no per-call value',
'does not publish enough schema for buyer agents to evaluate',
],
};
}

function payload(productId: string, req: NextRequest, proof: string) {
const query = Object.fromEntries(req.nextUrl.searchParams.entries());

Expand Down Expand Up @@ -53,29 +343,12 @@ function payload(productId: string, req: NextRequest, proof: string) {
}
case 'vendor-lead-discovery': {
const segment = query.segment || '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.' },
],
};
return vendorLeadDiscovery(segment);
}
case 'mcp-server-audit': {
const url = query.url || 'https://example.com/mcp';
return {
audit: {
url,
recommended_paid_tools: ['search', 'enrich', 'export', 'analyze'],
pricing: '$0.01-$0.25 per call depending on compute/data cost',
integration_steps: [
'Add 402 response with x402 accepts[] metadata',
'Register vendor/product in Pyrimid catalog',
'Expose tool schema in MCP server card',
'Add affiliateBps for distribution agents',
],
},
audit: mcpServerAudit(url),
};
}
case 'x402-integration-plan': {
Expand Down
52 changes: 50 additions & 2 deletions lib/seed-products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,33 @@ export const SEED_PRODUCTS: Omit<SeedProduct, 'indexed_at'>[] = [
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' },
summary: { type: 'string' },
scoring_fields: { type: 'array' },
leads: {
type: 'array',
items: {
type: 'object',
properties: {
rank: { type: 'number' },
target: { type: 'string' },
score: { type: 'number' },
signals: { type: 'array' },
suggested_paid_product: { type: 'string' },
pitch: { type: 'string' },
catalog_fit: { type: 'object' },
next_action: { type: 'string' },
},
},
},
discovery_queries: { type: 'array' },
reject_signals: { type: 'array' },
routed_by: { const: 'pyrimid' },
},
},
monthly_volume: 0,
monthly_buyers: 0,
network: 'base',
Expand All @@ -144,7 +170,29 @@ export const SEED_PRODUCTS: Omit<SeedProduct, 'indexed_at'>[] = [
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' },
target: { type: 'object' },
readiness_score: { type: 'number' },
findings: { type: 'array' },
monetization_summary: { type: 'object' },
recommended_paid_tools: { type: 'array' },
pricing: { type: 'object' },
x402_route_shape: { type: 'object' },
catalog_metadata: { type: 'object' },
risk_notes: { type: 'array' },
validation_checks: { type: 'array' },
next_actions: { type: 'array' },
},
},
routed_by: { const: 'pyrimid' },
},
},
monthly_volume: 0,
monthly_buyers: 0,
network: 'base',
Expand Down