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
15 changes: 2 additions & 13 deletions app/api/v1/paid/[product]/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import { getSeedProduct, paymentRequirement } from '@/lib/seed-products';
import { verifyPyrimidPaymentTx } from '@/lib/payment-verification';
import { getMcpServerAudit } from '@/lib/mcp-server-audit';

function paymentRequired(req: NextRequest, product: NonNullable<ReturnType<typeof getSeedProduct>>) {
const requirement = paymentRequirement(product, req.url);
Expand Down Expand Up @@ -64,19 +65,7 @@ function payload(productId: string, req: NextRequest, proof: string) {
}
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',
],
},
};
return getMcpServerAudit(url);
}
case 'x402-integration-plan': {
const service = query.service || 'agent-api';
Expand Down
168 changes: 168 additions & 0 deletions lib/mcp-server-audit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
type ToolRecommendation = {
name: string;
why_paid: string;
suggested_price_usdc: string;
request_shape: string;
success_metric: string;
};

type CatalogMetadata = {
vendor_id: string;
product_id: string;
category: string;
tags: string[];
affiliate_bps: number;
endpoint: string;
output_schema: Record<string, unknown>;
};

type McpServerProfile = {
submitted_url: string;
normalized_url: string;
host: string;
path: string;
inferred_name: string;
likely_surface: 'github-repo' | 'mcp-endpoint' | 'api-docs' | 'unknown';
};

const DEFAULT_URL = 'https://example.com/mcp';

function slugify(value: string) {
return value
.toLowerCase()
.replace(/^https?:\/\//, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 60) || 'mcp-server';
}

function safeUrl(rawUrl: string) {
const trimmed = rawUrl.trim() || DEFAULT_URL;
try {
return new URL(trimmed);
} catch {
try {
return new URL(`https://${trimmed.replace(/^\/+/, '')}`);
} catch {
return new URL(DEFAULT_URL);
}
}
}

function inferSurface(url: URL): McpServerProfile['likely_surface'] {
const path = url.pathname.toLowerCase();
const host = url.hostname.toLowerCase();
if (host.includes('github.com')) return 'github-repo';
if (path.includes('mcp')) return 'mcp-endpoint';
if (path.includes('docs') || path.includes('openapi') || path.includes('swagger')) return 'api-docs';
return 'unknown';
}

function inferName(url: URL) {
const pieces = url.pathname.split('/').filter(Boolean);
const repoLike = pieces.length >= 2 && url.hostname.toLowerCase().includes('github.com');
if (repoLike) return slugify(`${pieces[0]}-${pieces[1]}`);
return slugify(pieces.at(-1) || url.hostname.replace(/^www\./, ''));
}

function profileServer(rawUrl: string): McpServerProfile {
const url = safeUrl(rawUrl);
return {
submitted_url: rawUrl || DEFAULT_URL,
normalized_url: url.toString(),
host: url.hostname,
path: url.pathname || '/',
inferred_name: inferName(url),
likely_surface: inferSurface(url),
};
}

function toolRecommendations(profile: McpServerProfile): ToolRecommendation[] {
const productPrefix = profile.inferred_name;
return [
{
name: 'premium_search',
why_paid: 'Search often consumes hosted index, model, or retrieval cost and has clear per-call value for buyer agents.',
suggested_price_usdc: '$0.02-$0.10 per call',
request_shape: `GET /api/paid/${productPrefix}/search?q={query}`,
success_metric: 'Paid calls where the response includes cited results or structured matches.',
},
{
name: 'deep_enrich',
why_paid: 'Enrichment adds synthesized fields that save downstream agent work and can justify higher pricing than raw lookup.',
suggested_price_usdc: '$0.05-$0.25 per call',
request_shape: `POST /api/paid/${productPrefix}/enrich { "id": "...", "fields": [...] }`,
success_metric: 'Buyer agents reuse returned metadata without a follow-up manual research pass.',
},
{
name: 'export_report',
why_paid: 'Exports bundle multiple tool calls into a durable artifact, making value easy to verify and invoice.',
suggested_price_usdc: '$0.10-$0.50 per export',
request_shape: `POST /api/paid/${productPrefix}/export { "format": "json|csv|md" }`,
success_metric: 'Returned file/report includes schema, provenance, and a stable receipt ID.',
},
];
}

export function getMcpServerAudit(rawUrl: string) {
const profile = profileServer(rawUrl);
const productId = `${profile.inferred_name}-premium-search`;
const paidRoute = `GET /api/v1/paid/${productId}?q={query}`;

const catalogMetadata: CatalogMetadata = {
vendor_id: profile.inferred_name,
product_id: productId,
category: 'devtools',
tags: ['mcp', 'paid-tools', 'x402', 'agent-api', profile.likely_surface],
affiliate_bps: 2500,
endpoint: `https://${profile.host}/api/v1/paid/${productId}`,
output_schema: {
type: 'object',
required: ['result', 'receipt', 'routed_by'],
properties: {
result: { type: 'object', description: 'Tool-specific paid result payload.' },
receipt: { type: 'object', properties: { payment_tx: { type: 'string' }, product_id: { const: productId } } },
routed_by: { const: 'pyrimid' },
},
},
};

return {
audit: {
target: profile,
monetization_summary: `${profile.inferred_name} should start with one low-friction paid search/enrichment route, then list it in Pyrimid with affiliate routing so other agents can distribute it.`,
recommended_paid_tools: toolRecommendations(profile),
pricing: {
starting_range: '$0.02-$0.25 per call',
first_product: productId,
rationale: 'Use small per-call pricing until buyer-agent demand is proven; raise price only for deep enrichment, exports, or expensive upstream data/model calls.',
},
x402_route_shape: {
route: paidRoute,
unpaid_response: {
status: 402,
headers: ['X-PAYMENT-REQUIRED', 'X-Pyrimid-Vendor', 'X-Pyrimid-Product'],
body_fields: ['error', 'message', 'accepts', 'docs', 'catalog'],
},
paid_response_fields: ['product_id', 'vendor_id', 'payment_tx', 'result', 'routed_by', 'links'],
},
catalog_metadata: catalogMetadata,
mcp_card_updates: [
'Advertise the paid tool name, input schema, output schema, and price in the MCP server card or llms.txt.',
'Keep free discovery tools separate from paid high-value tools so agents can inspect before buying.',
'Include Pyrimid catalog URL and proof URL in tool annotations for buyer-agent verification.',
],
risk_notes: [
'Do not gate health checks, server metadata, or docs behind payment; only gate high-value compute/data calls.',
'Return deterministic schemas and bounded errors so buyer agents can budget retries safely.',
'Disclose upstream data/model costs and avoid promises that require private keys, custody, or off-platform payment setup.',
],
next_steps: [
'Choose one paid tool and publish its 402 response shape.',
'Register catalog metadata in Pyrimid with product_id, endpoint, price, output_schema, and affiliate_bps.',
'Add a short MCP/llms.txt example showing how an agent previews, pays, retries, and verifies the result.',
],
confidence: profile.likely_surface === 'unknown' ? 'medium' : 'high',
},
};
}
20 changes: 18 additions & 2 deletions lib/seed-products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,31 @@ export const SEED_PRODUCTS: Omit<SeedProduct, 'indexed_at'>[] = [
vendor_name: 'Pyrimid Growth',
vendor_erc8004: false,
product_id: 'mcp-server-audit',
description: 'Paid MCP monetization audit: tells an MCP server how to add paid tools, x402 pricing, and affiliate routing.',
description: 'Paid MCP monetization audit: inspects a submitted MCP URL/repo and returns paid tools, pricing, x402 route shape, catalog metadata, and risk notes.',
category: 'devtools',
tags: ['mcp', 'audit', 'monetization', 'paid-tools', 'x402', 'developer-tools'],
price_usdc: 100000,
price_display: '$0.10',
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: {
target: { type: 'object' },
recommended_paid_tools: { type: 'array' },
pricing: { type: 'object' },
x402_route_shape: { type: 'object' },
catalog_metadata: { type: 'object' },
risk_notes: { type: 'array' },
},
},
routed_by: { const: 'pyrimid' },
},
},
monthly_volume: 0,
monthly_buyers: 0,
network: 'base',
Expand Down