diff --git a/public/.well-known/agent-skills/index.json b/public/.well-known/agent-skills/index.json index 599b7111..3a6c3fb0 100644 --- a/public/.well-known/agent-skills/index.json +++ b/public/.well-known/agent-skills/index.json @@ -6,7 +6,7 @@ "type": "skill-md", "description": "TypeScript SDK for the Payment HTTP Authentication Scheme. Handles 402 Payment Required flows with Tempo, Stripe, and other payment methods. Use when integrating payments or mppx into a client or server application.", "url": "/.well-known/agent-skills/mppx/SKILL.md", - "digest": "sha256:9520d231fd7ae64ce54c8176e870a36d758c272748ceeadc7a3a8d23c2b8a5ce" + "digest": "sha256:45039f241e97036627d9cf2b2864408fa6b6afb70faf7f0001ffc8eb5ceaece6" } ] } diff --git a/public/.well-known/agent-skills/mppx/SKILL.md b/public/.well-known/agent-skills/mppx/SKILL.md index a282fc68..22955079 100644 --- a/public/.well-known/agent-skills/mppx/SKILL.md +++ b/public/.well-known/agent-skills/mppx/SKILL.md @@ -7,6 +7,32 @@ description: TypeScript SDK for the Payment HTTP Authentication Scheme. Handles TypeScript SDK for the "Payment" HTTP Authentication Scheme. Full 402 flow: challenge → credential → receipt. +## What I can accomplish + +- Add `402` payment handling to a client with `Fetch.polyfill` or `Fetch.from`. +- Protect HTTP routes with server-side MPP Challenges and Receipt responses. +- Accept one-time Tempo stablecoin payments with `tempo.charge`. +- Accept metered Tempo stablecoin payments with `tempo.session`. +- Accept one-time card payments with `stripe.charge`. +- Verify Credentials directly for custom transports or background workflows. +- Wrap MCP clients and servers so tool calls can require payment. + +## Required inputs + +- Client integrations need a signing account and one or more client payment methods. +- Server integrations need a recipient, currency, amount, and `MPP_SECRET_KEY`. +- Tempo examples use chain ID `4217` unless a page explicitly covers Moderato testnet. +- Stripe examples need a configured Stripe account and Shared Payment Token flow. +- MCP integrations need the MCP client or server object to wrap. + +## Constraints + +- Keep `MPP_SECRET_KEY` server-side and out of logs. +- Never commit private keys or wallet seeds. +- Treat runtime `402` Challenges as authoritative for current payment terms. +- Return `id` and `opaque` unchanged when responding to a Challenge. +- Use `USDC.e` for Tempo bridged USDC examples, not generic USDC. + ## Client ```ts @@ -82,7 +108,9 @@ npx mppx example.com -v # verbose output ## References -- Docs: https://mpp.dev/sdk/typescript -- Repo: https://github.com/wevm/mppx -- Spec: https://github.com/tempoxyz/payment-auth-spec -- Tempo: https://docs.tempo.xyz +- [TypeScript SDK docs](https://mpp.dev/sdk/typescript) +- [Client quickstart](https://mpp.dev/quickstart/client) +- [Server quickstart](https://mpp.dev/quickstart/server) +- [mppx repository](https://github.com/wevm/mppx) +- [IETF Specification](https://paymentauth.org) +- [Tempo docs](https://docs.tempo.xyz) diff --git a/public/.well-known/api-catalog b/public/.well-known/api-catalog index a022c315..2917b179 100644 --- a/public/.well-known/api-catalog +++ b/public/.well-known/api-catalog @@ -24,6 +24,11 @@ "href": "https://mpp.dev/guides/building-with-an-llm", "title": "Build with an LLM", "type": "text/html" + }, + { + "href": "https://mpp.dev/AGENTS.md", + "title": "Agent instructions", + "type": "text/markdown" } ], "status": [ @@ -38,6 +43,11 @@ "href": "https://mpp.dev/.well-known/agent-skills/index.json", "title": "Agent Skills discovery index", "type": "application/json" + }, + { + "href": "https://mpp.dev/agent-permissions.json", + "title": "Agent permissions", + "type": "application/json" } ] } diff --git a/public/AGENTS.md b/public/AGENTS.md new file mode 100644 index 00000000..4a175957 --- /dev/null +++ b/public/AGENTS.md @@ -0,0 +1,34 @@ +# MPP documentation + +Use this site to integrate MPP, the Machine Payments Protocol for HTTP `402` payment flows. + +## Start here + +- Read [`llms.txt`](https://mpp.dev/llms.txt) for a concise page index. +- Read [`llms-full.txt`](https://mpp.dev/llms-full.txt) when you need the full documentation in one request. +- Use [`/.well-known/mcp.json`](https://mpp.dev/.well-known/mcp.json) to connect to the MPP documentation MCP server. +- Use [`/.well-known/agent-skills/index.json`](https://mpp.dev/.well-known/agent-skills/index.json) to discover agent skills. + +## Main tasks + +- Add payments to an API with [`mppx`](https://mpp.dev/quickstart/server). +- Connect a coding agent to paid APIs with [`mppx`](https://mpp.dev/quickstart/agent). +- Use the TypeScript SDK reference at [`/sdk/typescript`](https://mpp.dev/sdk/typescript). +- Browse paid services at [`/services`](https://mpp.dev/services). + +## Project structure + +- Documentation pages live under `/assets/md/` as Markdown mirrors. +- Agent skills live under `/.well-known/agent-skills/`. +- API discovery files live under `/.well-known/` and `/api/`. +- Service catalog files live under `/services/`. + +## Protocol terms + +MPP uses a Challenge, Credential, and Receipt flow: + +- Servers return Challenges in `WWW-Authenticate`. +- Clients return Credentials in `Authorization`. +- Servers return Receipts in `Payment-Receipt`. + +Prefer Tempo stablecoin examples unless a page is specifically about another payment method. diff --git a/public/agent-permissions.json b/public/agent-permissions.json new file mode 100644 index 00000000..c9da129a --- /dev/null +++ b/public/agent-permissions.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://mpp.dev/schemas/agent-permissions.schema.json", + "agentUserAgents": ["ChatGPT-User", "ClaudeBot", "GPTBot", "PerplexityBot"], + "allowedActions": [ + "read-documentation", + "read-discovery", + "read-openapi", + "read-skill" + ], + "allowedPaths": [ + "/", + "/.well-known/*", + "/AGENTS.md", + "/agent-permissions.json", + "/api/api-catalog", + "/api/openapi.json", + "/assets/md/*", + "/llms-full.txt", + "/llms.txt", + "/services/llms.txt" + ], + "disallowedActions": ["execute-payment", "submit-credential", "write-data"], + "humanRequiredActions": [ + "execute-payment", + "send-transaction", + "store-private-key" + ], + "permissions": { + "read": true, + "write": false + }, + "rateLimits": { + "burst": 60, + "period": "1m" + } +} diff --git a/public/agent.html b/public/agent.html new file mode 100644 index 00000000..5f8ec41a --- /dev/null +++ b/public/agent.html @@ -0,0 +1,96 @@ + + + + + + MPP agent index + + + + + + +
+
+

MPP agent index

+

+ Use this page as the compact HTML entry point for agents integrating + MPP, mppx, or HTTP 402 payment flows. It links to the canonical + Markdown, skill, permissions, and API discovery documents. +

+ +
+

Primary files

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FilePurpose
llms.txtShort documentation index grouped for agent retrieval.
llms-full.txtComplete Markdown corpus for long-context agents.
AGENTS.mdProject conventions, key files, and build commands.
agent-permissions.jsonAllowed interactions, human gates, and rate limits.
mppx skillCapabilities, inputs, constraints, and references.
API catalogLinkset discovery for docs, OpenAPI, skills, and metadata.
+
+ +
+

Recommended flow

+

+ Start with llms.txt, fetch the smallest + Markdown page that answers the task, then use the skill and API + catalog only when you need integration details or machine-readable + endpoints. +

+
curl https://mpp.dev/llms.txt
+curl https://mpp.dev/.well-known/agent-skills/mppx/SKILL.md
+curl https://mpp.dev/agent-permissions.json
+
+ +
+

Human-agent bridge

+

+ Documentation pages expose clean Markdown mirrors under + /assets/md/. Use the Copy for AI control in the page UI + or fetch the Markdown directly. +

+ + +

+ View homepage as Markdown +

+
+
+
+ + diff --git a/skills/mppx/SKILL.md b/skills/mppx/SKILL.md index 1b58c3ca..67aebd7c 100644 --- a/skills/mppx/SKILL.md +++ b/skills/mppx/SKILL.md @@ -7,6 +7,32 @@ description: TypeScript SDK for the Payment HTTP Authentication Scheme. Handles TypeScript SDK for the "Payment" HTTP Authentication Scheme. Full 402 flow: challenge → credential → receipt. +## What I can accomplish + +- Add `402` payment handling to a client with `Fetch.polyfill` or `Fetch.from`. +- Protect HTTP routes with server-side MPP Challenges and Receipt responses. +- Accept one-time Tempo stablecoin payments with `tempo.charge`. +- Accept metered Tempo stablecoin payments with `tempo.session`. +- Accept one-time card payments with `stripe.charge`. +- Verify Credentials directly for custom transports or background workflows. +- Wrap MCP clients and servers so tool calls can require payment. + +## Required inputs + +- Client integrations need a signing account and one or more client payment methods. +- Server integrations need a recipient, currency, amount, and `MPP_SECRET_KEY`. +- Tempo examples use chain ID `4217` unless a page explicitly covers Moderato testnet. +- Stripe examples need a configured Stripe account and Shared Payment Token flow. +- MCP integrations need the MCP client or server object to wrap. + +## Constraints + +- Keep `MPP_SECRET_KEY` server-side and out of logs. +- Never commit private keys or wallet seeds. +- Treat runtime `402` Challenges as authoritative for current payment terms. +- Return `id` and `opaque` unchanged when responding to a Challenge. +- Use `USDC.e` for Tempo bridged USDC examples, not generic USDC. + ## Client ```ts @@ -93,7 +119,9 @@ npx mppx example.com -v # verbose output ## References -- Docs: https://mpp.dev/sdk/typescript -- Repo: https://github.com/wevm/mppx -- Spec: https://github.com/tempoxyz/payment-auth-spec -- Tempo: https://docs.tempo.xyz +- [TypeScript SDK docs](https://mpp.dev/sdk/typescript) +- [Client quickstart](https://mpp.dev/quickstart/client) +- [Server quickstart](https://mpp.dev/quickstart/server) +- [mppx repository](https://github.com/wevm/mppx) +- [IETF Specification](https://paymentauth.org) +- [Tempo docs](https://docs.tempo.xyz) diff --git a/src/env.d.ts b/src/env.d.ts index faccc97b..1a88830c 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -5,3 +5,13 @@ declare module "*.svg?raw" { const content: string; export default content; } + +declare module "*.md?raw" { + const content: string; + export default content; +} + +declare module "*.json?raw" { + const content: string; + export default content; +} diff --git a/src/pages/_api/.well-known/agent-skills/mppx/SKILL.md.ts b/src/pages/_api/.well-known/agent-skills/mppx/SKILL.md.ts new file mode 100644 index 00000000..a4aab7a4 --- /dev/null +++ b/src/pages/_api/.well-known/agent-skills/mppx/SKILL.md.ts @@ -0,0 +1,16 @@ +import skill from "../../../../../../public/.well-known/agent-skills/mppx/SKILL.md?raw"; + +const headers = { + "Access-Control-Allow-Origin": "*", + "Cache-Control": "public, max-age=300", + "Content-Type": "text/markdown; charset=utf-8", + "X-Content-Type-Options": "nosniff", +}; + +export function GET() { + return new Response(skill, { headers }); +} + +export function HEAD() { + return new Response(null, { headers }); +} diff --git a/src/pages/_api/.well-known/api-catalog.ts b/src/pages/_api/.well-known/api-catalog.ts new file mode 100644 index 00000000..b51687fe --- /dev/null +++ b/src/pages/_api/.well-known/api-catalog.ts @@ -0,0 +1 @@ +export { GET, HEAD } from "../api/api-catalog"; diff --git a/src/pages/_api/AGENTS.md.ts b/src/pages/_api/AGENTS.md.ts new file mode 100644 index 00000000..f2355b54 --- /dev/null +++ b/src/pages/_api/AGENTS.md.ts @@ -0,0 +1,16 @@ +import agents from "../../../public/AGENTS.md?raw"; + +const headers = { + "Access-Control-Allow-Origin": "*", + "Cache-Control": "public, max-age=300", + "Content-Type": "text/markdown; charset=utf-8", + "X-Content-Type-Options": "nosniff", +}; + +export function GET() { + return new Response(agents, { headers }); +} + +export function HEAD() { + return new Response(null, { headers }); +} diff --git a/src/pages/_api/agent-permissions.json.ts b/src/pages/_api/agent-permissions.json.ts new file mode 100644 index 00000000..9dc5706c --- /dev/null +++ b/src/pages/_api/agent-permissions.json.ts @@ -0,0 +1,16 @@ +import permissions from "../../../public/agent-permissions.json?raw"; + +const headers = { + "Access-Control-Allow-Origin": "*", + "Cache-Control": "public, max-age=300", + "Content-Type": "application/json; charset=utf-8", + "X-Content-Type-Options": "nosniff", +}; + +export function GET() { + return new Response(permissions, { headers }); +} + +export function HEAD() { + return new Response(null, { headers }); +} diff --git a/src/pages/_api/api/api-catalog.ts b/src/pages/_api/api/api-catalog.ts index 1bb6dbd0..a14205b3 100644 --- a/src/pages/_api/api/api-catalog.ts +++ b/src/pages/_api/api/api-catalog.ts @@ -31,6 +31,11 @@ const apiCatalog = { title: "Build with an LLM", type: "text/html", }, + { + href: "https://mpp.dev/AGENTS.md", + title: "Agent instructions", + type: "text/markdown", + }, ], status: [ { @@ -45,6 +50,11 @@ const apiCatalog = { title: "Agent Skills discovery index", type: "application/json", }, + { + href: "https://mpp.dev/agent-permissions.json", + title: "Agent permissions", + type: "application/json", + }, ], }, ], diff --git a/src/pages/_api/api/openapi.json.ts b/src/pages/_api/api/openapi.json.ts index cd99ca87..61734a4a 100644 --- a/src/pages/_api/api/openapi.json.ts +++ b/src/pages/_api/api/openapi.json.ts @@ -3,6 +3,7 @@ import { mppx } from "../../../mppx.server"; const AGENT_SKILLS_INDEX_URL = "https://mpp.dev/.well-known/agent-skills/index.json"; +const AGENT_PERMISSIONS_URL = "https://mpp.dev/agent-permissions.json"; const API_CATALOG_URL = "https://mpp.dev/.well-known/api-catalog"; const MCP_SERVER_CARD_URL = "https://mpp.dev/.well-known/mcp.json"; const OPENAPI_LINK_HEADER_VALUE = [ @@ -58,6 +59,7 @@ function normalizeServiceInfo(document: Record) { if (!isRecord(serviceInfo)) return; serviceInfo.apiCatalog = API_CATALOG_URL; + serviceInfo.agentPermissions = AGENT_PERMISSIONS_URL; serviceInfo.agentSkills = AGENT_SKILLS_INDEX_URL; serviceInfo.mcpServerCard = MCP_SERVER_CARD_URL; diff --git a/src/pages/_root.css b/src/pages/_root.css index 4cecf462..109d440d 100644 --- a/src/pages/_root.css +++ b/src/pages/_root.css @@ -865,9 +865,8 @@ aside[data-v-callout] a:hover { margin-left: 1.5rem !important; } -/* Copy page for AI — align with TOC left edge */ -[data-v-outline] [data-v-copy-for-ai] { - margin-left: 1.5rem !important; +[data-v-copy-for-ai] { + display: none !important; } /* Non-selected code-group / tab icons greyscale */ diff --git a/src/pages/blog/evm-x402-support.mdx b/src/pages/blog/evm-x402-support.mdx index 8503711a..c6a09582 100644 --- a/src/pages/blog/evm-x402-support.mdx +++ b/src/pages/blog/evm-x402-support.mdx @@ -1,5 +1,6 @@ --- layout: minimal +title: "EVM and x402 support" outline: false showAskAi: false showFeedback: false diff --git a/src/pages/blog/go-and-ruby-sdks.mdx b/src/pages/blog/go-and-ruby-sdks.mdx index 32066f1d..0ce54175 100644 --- a/src/pages/blog/go-and-ruby-sdks.mdx +++ b/src/pages/blog/go-and-ruby-sdks.mdx @@ -1,5 +1,6 @@ --- layout: minimal +title: "Go and Ruby SDKs" outline: false showAskAi: false showFeedback: false diff --git a/src/pages/blog/index.mdx b/src/pages/blog/index.mdx index 6daacdff..2d6e29dd 100644 --- a/src/pages/blog/index.mdx +++ b/src/pages/blog/index.mdx @@ -1,5 +1,6 @@ --- layout: minimal +title: "Blog" showAskAi: false showFeedback: false showSearch: false diff --git a/src/pages/blog/multi-method-discovery.mdx b/src/pages/blog/multi-method-discovery.mdx index 0939c147..aaad199c 100644 --- a/src/pages/blog/multi-method-discovery.mdx +++ b/src/pages/blog/multi-method-discovery.mdx @@ -1,5 +1,6 @@ --- layout: minimal +title: "Multi-method discovery" outline: false showAskAi: false showFeedback: false diff --git a/src/pages/blog/payment-hooks.mdx b/src/pages/blog/payment-hooks.mdx index 1b830e38..297d9058 100644 --- a/src/pages/blog/payment-hooks.mdx +++ b/src/pages/blog/payment-hooks.mdx @@ -1,5 +1,6 @@ --- layout: minimal +title: "Payment hooks" outline: false showAskAi: false showFeedback: false diff --git a/src/pages/blog/sessions-improved.mdx b/src/pages/blog/sessions-improved.mdx index 145db1f5..ce17daa3 100644 --- a/src/pages/blog/sessions-improved.mdx +++ b/src/pages/blog/sessions-improved.mdx @@ -1,5 +1,6 @@ --- layout: minimal +title: "An improved sessions experience" outline: false showAskAi: false showFeedback: false diff --git a/src/pages/blog/subscriptions.mdx b/src/pages/blog/subscriptions.mdx index 1a8a3b69..c0740748 100644 --- a/src/pages/blog/subscriptions.mdx +++ b/src/pages/blog/subscriptions.mdx @@ -1,5 +1,6 @@ --- layout: minimal +title: "Subscriptions" outline: false showAskAi: false showFeedback: false diff --git a/src/pages/services.mdx b/src/pages/services.mdx index 827be11a..4e5d1589 100644 --- a/src/pages/services.mdx +++ b/src/pages/services.mdx @@ -1,6 +1,7 @@ --- description: "Browse live MPP-enabled services that accept machine-to-machine payments. Discover APIs you can pay for with stablecoins, cards, or Bitcoin." layout: minimal +title: "Services" showAskAi: false showOutline: false showSearch: false diff --git a/vercel.ts b/vercel.ts index bfb3b31c..35271027 100644 --- a/vercel.ts +++ b/vercel.ts @@ -47,8 +47,11 @@ const CACHE_HEADERS = [ const DOC_SECTIONS = [ "advanced", + "blog", "guides", + "intents", "overview", + "partner-integrations", "payment-methods", "protocol", "quickstart", @@ -58,8 +61,17 @@ const DOC_SECTIONS = [ "use-cases", ]; +const DOC_STANDALONE_PAGES = [ + "/brand", + "/extensions", + "/faq", + "/governance", + "/mpp-vs-x402", +]; + const DISCOVERY_PAGE_SOURCES = [ "/", + ...DOC_STANDALONE_PAGES, ...DOC_SECTIONS.flatMap((section) => [`/${section}`, `/${section}/:path*`]), ]; diff --git a/vite.config.ts b/vite.config.ts index 1cd6750b..c635049d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -140,6 +140,146 @@ function pruneSitemap(): Plugin { }; } +// In dev, Vocs serves /llms.txt from middleware before static files. When a +// production build exists, serve that rewritten artifact so local AEO audits +// exercise the same sectioned llms.txt that ships. +function serveSectionedLlmsTxt(): Plugin { + let outDir: string | undefined; + return { + name: "serve-sectioned-llms-txt", + enforce: "pre", + configResolved(config) { + outDir = path.resolve(config.root, config.build.outDir, "public"); + }, + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + const pathname = new URL(req.url ?? "/", "http://localhost").pathname; + if (pathname !== "/llms.txt" && pathname !== "/llms-full.txt") { + next(); + return; + } + + const filePath = path.join(outDir ?? "", pathname.slice(1)); + const content = await fs.readFile(filePath, "utf8").catch((error) => { + if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; + throw error; + }); + if (!content) { + next(); + return; + } + + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(content); + }); + }, + }; +} + +// Vocs emits a flat llms.txt. Group it into stable sections and trim long +// descriptions so the short index stays below the 5K-token agent budget. +function sectionLlmsTxt(): Plugin { + let outDir: string | undefined; + return { + name: "section-llms-txt", + enforce: "post", + configResolved(config) { + outDir = path.resolve(config.root, config.build.outDir, "public"); + }, + async closeBundle() { + if (!outDir) return; + + const llmsPath = path.join(outDir, "llms.txt"); + const current = await fs.readFile(llmsPath, "utf8").catch((error) => { + if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; + throw error; + }); + if (!current || current.includes("\n## Quickstart\n")) return; + + const lines = current.split("\n"); + const firstEntry = lines.findIndex((line) => line.startsWith("- [")); + if (firstEntry === -1) return; + + const intro = lines.slice(0, firstEntry); + const entries = lines + .slice(firstEntry) + .filter((line) => line.trim()) + .map(trimLlmsEntry); + const groups = new Map(); + for (const entry of entries) { + const group = llmsGroupForEntry(entry); + groups.set(group, [...(groups.get(group) ?? []), entry]); + } + + const orderedGroups = [ + "Quickstart", + "Guides", + "Protocol", + "Payment methods", + "SDKs", + "Services and ecosystem", + "Blog", + "Other", + ]; + + const next = [ + ...intro, + ...orderedGroups.flatMap((group) => { + const items = groups.get(group); + if (!items?.length) return []; + return [`## ${group}`, "", ...items, ""]; + }), + ].join("\n"); + + if (next !== current) await fs.writeFile(llmsPath, next, "utf8"); + }, + }; +} + +// Keep each llms.txt row useful while preventing verbose frontmatter +// descriptions from pushing the whole index over the scanner's token limit. +function trimLlmsEntry(entry: string): string { + const [link, description] = entry.split(": ", 2); + if (!description || description.length <= 90) return entry; + return `${link}: ${description.slice(0, 87).trimEnd()}...`; +} + +// Map generated page paths into coarse documentation areas. These names are +// intentionally broad so new pages fall into predictable agent-facing sections. +function llmsGroupForEntry(entry: string): string { + const pathMatch = entry.match(/\]\(([^)]+)\)/); + const pagePath = pathMatch?.[1] ?? ""; + + if ( + pagePath === "/index" || + pagePath.startsWith("/quickstart") || + pagePath === "/overview" || + pagePath === "/governance" || + pagePath === "/faq" + ) + return "Quickstart"; + if (pagePath.startsWith("/guides")) return "Guides"; + if (pagePath.startsWith("/protocol") || pagePath.startsWith("/advanced")) + return "Protocol"; + if ( + pagePath.startsWith("/payment-methods") || + pagePath.startsWith("/intents") + ) + return "Payment methods"; + if (pagePath.startsWith("/sdk")) return "SDKs"; + if ( + pagePath.startsWith("/services") || + pagePath.startsWith("/use-cases") || + pagePath.startsWith("/extensions") || + pagePath.startsWith("/partner-integrations") || + pagePath === "/brand" || + pagePath === "/mpp-vs-x402" + ) + return "Services and ecosystem"; + if (pagePath.startsWith("/blog")) return "Blog"; + return "Other"; +} + export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ""); for (const key of Object.keys(env)) { @@ -159,9 +299,11 @@ export default defineConfig(({ mode }) => { stubMermaid(), Icons({ compiler: "jsx", jsx: "react" }), react(), + serveSectionedLlmsTxt(), vocs(), contentSignalsRobotsTxt(), pruneSitemap(), + sectionLlmsTxt(), ...(mode !== "production" && mode !== "test" ? [mkcert({ force: true, hosts: ["localhost"] })] : []),