From ccad2c801652b4351610f818e2ccd0b5e86bb5fb Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 21 Aug 2026 20:47:45 +0000 Subject: [PATCH 1/3] Add machine-readable discovery surfaces for AI agents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The site already served llms.txt, per-page markdown mirrors, canonicals, a sitemap and an RSS feed, but an agent arriving cold had no way to find the security contact, the HTTP APIs behind the product, or the MCP server, and nothing it fetched was readable cross-origin. - /.well-known/security.txt (RFC 9116), /.well-known/api-catalog (RFC 9727, linking both published OpenAPI specs to their docs pages), and /.well-known/mcp.json describing the stdio `agent-relay mcp` server. The App Router skips dot-prefixed directories, so these live under app/well-known/ and next.config rewrites the canonical paths onto them; the internal path carries X-Robots-Tag: noindex and a robots.txt disallow. - CORS (Access-Control-Allow-Origin: *) on every machine-readable endpoint — llms.txt, the markdown mirrors, feed.xml, sitemap.xml, robots.txt and the well-known documents — plus X-Content-Type-Options: nosniff site-wide. - Site-wide Organization / WebSite / SoftwareApplication JSON-LD in the root layout, cross-referenced by @id so any single page resolves its publisher. - for llms.txt, llms-full.txt and the feed on every page, so the plain-text mirrors are discoverable from any entry point. - robots.txt names the AI crawlers and agent fetchers that read their own user-agent (Perplexity, Google-Extended, Applebot, Meta, Amazon, CCBot, Mistral, Cohere and others) alongside the OpenAI and Anthropic entries. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VV1b4tXaTcM2EqLLwKpL4u --- web/app/layout.tsx | 70 ++++++++++- web/app/robots.ts | 50 +++++--- web/app/well-known/api-catalog/route.ts | 14 +++ web/app/well-known/mcp.json/route.ts | 14 +++ web/app/well-known/security.txt/route.ts | 18 +++ web/lib/agent-discovery.ts | 143 +++++++++++++++++++++++ web/lib/test/agent-discovery.test.ts | 45 +++++++ web/next.config.mjs | 48 ++++++++ 8 files changed, 388 insertions(+), 14 deletions(-) create mode 100644 web/app/well-known/api-catalog/route.ts create mode 100644 web/app/well-known/mcp.json/route.ts create mode 100644 web/app/well-known/security.txt/route.ts create mode 100644 web/lib/agent-discovery.ts create mode 100644 web/lib/test/agent-discovery.test.ts diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 1d8b277..0dd8e93 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -4,7 +4,7 @@ import { Geist_Mono, Inter, Sora } from 'next/font/google'; import type { ReactNode } from 'react'; import { defaultOgImage } from '../lib/og-meta'; -import { POSTHOG_HOST, SITE_URL } from '../lib/site'; +import { absoluteUrl, POSTHOG_HOST, SITE_EMAIL, SITE_NAME, SITE_URL } from '../lib/site'; import { WebsitePostHogPageView } from './PostHogPageView'; import './globals.css'; @@ -62,6 +62,53 @@ export const metadata: Metadata = { }, }; +// Site-wide structured data. Individual pages add their own Article/Service/ +// CollectionPage nodes and reference these two by @id, so an agent reading any +// single page still resolves the publisher and the site root. +const siteStructuredData = { + '@context': 'https://schema.org', + '@graph': [ + { + '@type': 'Organization', + '@id': `${SITE_URL}/#organization`, + name: SITE_NAME, + url: SITE_URL, + email: SITE_EMAIL, + logo: { + '@type': 'ImageObject', + url: absoluteUrl('/agent-relay-logo-white.svg'), + }, + description: + 'Agent Relay is the messaging layer for AI agents: channels, DMs, durable delivery, event listeners, and typed actions for any agent runtime.', + sameAs: [ + 'https://github.com/agentworkforce/relay', + 'https://twitter.com/agent_relay', + 'https://discord.gg/RJGE7CHV', + ], + }, + { + '@type': 'WebSite', + '@id': `${SITE_URL}/#website`, + name: SITE_NAME, + url: SITE_URL, + inLanguage: 'en-US', + publisher: { '@id': `${SITE_URL}/#organization` }, + }, + { + '@type': 'SoftwareApplication', + '@id': `${SITE_URL}/#software`, + name: SITE_NAME, + url: SITE_URL, + applicationCategory: 'DeveloperApplication', + operatingSystem: 'Any', + publisher: { '@id': `${SITE_URL}/#organization` }, + description: + 'Add channels, DMs, durable delivery, event listeners, and Zod-typed actions to any agent runtime.', + softwareHelp: { '@type': 'CreativeWork', url: absoluteUrl('/docs') }, + }, + ], +}; + export default function RootLayout({ children }: { children: ReactNode }) { const postHogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY; const content = postHogKey ? ( @@ -83,7 +130,28 @@ export default function RootLayout({ children }: { children: ReactNode }) { return ( + + {/* Machine-readable mirrors of this site, advertised on every page so an + agent that lands anywhere can find the plain-text and feed formats. */} + + + + +