/** * agents.md — GET /agents.md * * The canonical AI-agent discovery file (the convention Shopify made standard * in 2026): a machine-readable guide telling shopping agents and AI assistants * what this site sells, where its machine surfaces live, and how a purchase * works. /llms.txt mirrors the same information in the older convention — * keep the two consistent. */ import { getServerClient } from '@/core/lib/brainerce.server'; import { getCanonicalSiteUrl } from '@/core/lib/site-url'; export const revalidate = 3600; interface CategoryNodeLike { name: string; slug?: string | null; children?: CategoryNodeLike[]; } export async function GET() { const baseUrl = await getCanonicalSiteUrl(); // Machine-readable product feed served by the Brainerce API per channel — // the same artifact Google/Microsoft/Perplexity ingest; agents can read the // whole catalog in one structured fetch instead of crawling page by page. const apiUrl = (process.env.BRAINERCE_API_URL || 'https://api.brainerce.com').replace(/\/+$/, ''); const channelId = process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || process.env.NEXT_PUBLIC_BRAINERCE_CONNECTION_ID || ''; const client = await getServerClient(); const [info, categoriesRes] = await Promise.all([ client.getStoreInfo().catch(() => null), client.getCategories().catch(() => null), ]); const topCategories: CategoryNodeLike[] = ( (categoriesRes?.categories as CategoryNodeLike[] | undefined) ?? [] ).filter((c) => c.slug); const lines: string[] = [ `# ${info?.name ?? 'Store'} — guide for AI agents`, '', ...(info?.metaDescription ? [`> ${info.metaDescription}`, ''] : []), 'This is an e-commerce storefront. Product data, prices, and availability', 'are rendered server-side into every page (no JavaScript execution needed)', 'and marked up with schema.org Product / Offer / BreadcrumbList JSON-LD.', '', '## Machine-readable surfaces', '', `- Sitemap: ${baseUrl}/sitemap.xml (products, categories, articles, pages)`, `- AI summary: ${baseUrl}/llms.txt`, `- Blog RSS: ${baseUrl}/blog/rss.xml`, ...(channelId ? [ `- Product feed (Google Shopping XML): ${apiUrl}/api/feeds/${channelId}/google-shopping.xml`, `- Product feed (JSONL): ${apiUrl}/api/feeds/${channelId}/openai-products.jsonl`, `- MCP endpoint (catalog tools, stateless JSON-RPC): ${apiUrl}/api/mcp/storefront/${channelId}`, ' Tools: search_products, get_product, list_categories, get_store_info. Read-only;', ' to buy, send the shopper to the product `url` — checkout happens on this site.', ] : []), `- IndexNow key: ${baseUrl}/indexnow-key.txt`, '', '## Key pages', '', `- [All products](${baseUrl}/products) — full catalog, filterable`, ...topCategories .slice(0, 20) .map((c) => `- [${c.name}](${baseUrl}/category/${c.slug}) — category page`), `- [Blog](${baseUrl}/blog) — guides and articles`, `- [FAQ](${baseUrl}/faq)`, `- [Contact](${baseUrl}/contact)`, '', '## Buying', '', `- Currency: ${info?.currency ?? 'see product pages'}`, '- Checkout happens on this site (cart → checkout). Carts and checkout', ' are session-based; agents should hand the shopper the product URL.', ...(info?.contactEmail ? ['', `## Contact`, '', `- Email: ${info.contactEmail}`] : []), '', ]; return new Response(lines.join('\n'), { headers: { 'Content-Type': 'text/markdown; charset=utf-8' }, }); }