diff --git a/app/api/v1/paid/[product]/route.ts b/app/api/v1/paid/[product]/route.ts index 65076eb..62f2818 100644 --- a/app/api/v1/paid/[product]/route.ts +++ b/app/api/v1/paid/[product]/route.ts @@ -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>) { const requirement = paymentRequirement(product, req.url); @@ -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'; diff --git a/lib/mcp-server-audit.ts b/lib/mcp-server-audit.ts new file mode 100644 index 0000000..2dbb3ef --- /dev/null +++ b/lib/mcp-server-audit.ts @@ -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; +}; + +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', + }, + }; +} diff --git a/lib/seed-products.ts b/lib/seed-products.ts index c47b524..5256c72 100644 --- a/lib/seed-products.ts +++ b/lib/seed-products.ts @@ -136,7 +136,7 @@ export const SEED_PRODUCTS: Omit[] = [ 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, @@ -144,7 +144,23 @@ 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: { + 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',