Commita0071d7
feat(agents): serve llms.txt agent map (/llms.txt + /.well-known/llms.txt)
feat(agents): serve llms.txt agent map (/llms.txt + /.well-known/llms.txt) The llmstxt.org convention: a curated, agent-facing map of the platform that any LLM can read to orient itself — points at the MCP endpoint, the OAuth discovery metadata (RFC 8414/9728), the API docs, and the get-started flows. Built from config.appBaseUrl so links are absolute + correct per deployment. Pairs with the discovery metadata shipped in 369ad65 to complete the "agent-ready surface" set. 4 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 files changed+78−1a0071d7aa5490b2f6037302fccef71201c7027d6
2 changed files+78−1
Modifiedsrc/__tests__/status-seo.test.ts+21−0View fileUnifiedSplit
@@ -58,3 +58,24 @@ test("sitemap includes the landing, status, explore URLs", async () => {
5858 expect(body).toContain("/status");
5959 expect(body).toContain("/explore");
6060});
61
62test("/llms.txt maps the platform for AI agents", async () => {
63 const res = await app.request("/llms.txt");
64 expect(res.status).toBe(200);
65 expect(res.headers.get("content-type")).toMatch(/text\/plain/);
66 const body = await res.text();
67 // llmstxt.org shape: H1 title + blockquote summary.
68 expect(body.startsWith("# Gluecron")).toBe(true);
69 expect(body).toContain("\n> ");
70 // Must point agents at the load-bearing surfaces.
71 expect(body).toContain("/mcp");
72 expect(body).toContain("/.well-known/oauth-authorization-server");
73 expect(body).toContain("/api/docs");
74});
75
76test("/.well-known/llms.txt serves the same agent map", async () => {
77 const a = await (await app.request("/llms.txt")).text();
78 const b = await (await app.request("/.well-known/llms.txt")).text();
79 expect(b).toBe(a);
80 expect(b.length).toBeGreaterThan(100);
81});
Modifiedsrc/routes/seo.ts+57−1View fileUnifiedSplit
@@ -1,14 +1,18 @@
11/**
2 * SEO routes: robots.txt + sitemap.xml.
2 * SEO + agent-discovery routes: robots.txt, sitemap.xml, llms.txt.
33 *
44 * robots.txt — allow crawlers on public surfaces, disallow admin/settings
55 * and API write paths.
66 * sitemap.xml — lists the stable public URLs (landing, explore, status,
77 * marketplace, graphql explorer, shortcuts, terms). Public repo URLs are
88 * not enumerated here — those live behind /explore which crawlers follow.
9 * llms.txt — the llmstxt.org convention: a curated map of the platform for
10 * AI agents, pointing at the MCP endpoint, API docs, and OAuth discovery so
11 * any LLM/agent can orient itself and connect.
912 */
1013
1114import { Hono } from "hono";
15import { config } from "../lib/config";
1216
1317const seo = new Hono();
1418
@@ -26,6 +30,7 @@ Disallow: /*.git/
2630
2731Sitemap: /sitemap.xml
2832`;
33// Note: /llms.txt is intentionally allowed (agent-facing map, public).
2934
3035seo.get("/robots.txt", (c) => {
3136 c.header("Content-Type", "text/plain; charset=utf-8");
@@ -33,6 +38,57 @@ seo.get("/robots.txt", (c) => {
3338 return c.body(ROBOTS);
3439});
3540
41/**
42 * llms.txt (llmstxt.org) — a curated, agent-facing map of the platform.
43 * Served at both /llms.txt and /.well-known/llms.txt. Built from the live
44 * base URL so links are always absolute + correct for this deployment.
45 */
46function buildLlmsTxt(): string {
47 const b = config.appBaseUrl;
48 return `# Gluecron
49
50> AI-native git hosting. Git over HTTPS + SSH, issues, pull requests with AI
51> code review and gated auto-merge, CI workflows, package + container
52> registries, and a Model Context Protocol (MCP) server so any LLM/agent can
53> read and drive repositories directly. Self-hosted-friendly and MCP-native.
54
55## For AI agents
56
57- [MCP server](${b}/mcp): Model Context Protocol endpoint (JSON-RPC 2.0 over HTTP). GET for discovery, POST to call tools. Read tools work anonymously on public repos; write tools require an OAuth/PAT bearer token.
58- [OAuth authorization server metadata](${b}/.well-known/oauth-authorization-server): RFC 8414 — how to authenticate (authorize/token endpoints, PKCE S256, scopes).
59- [OAuth protected resource metadata](${b}/.well-known/oauth-protected-resource): RFC 9728 — which authorization server guards the MCP resource.
60
61## API
62
63- [REST + GraphQL API docs](${b}/api/docs): endpoints, authentication, and scopes for programmatic access.
64- [GraphQL explorer](${b}/api/graphql): read queries over repos, users, issues, and search.
65
66## Get started
67
68- [Sign up](${b}/register): create an account.
69- [Explore public repos](${b}/explore): browse what's hosted here.
70- [Import from GitHub](${b}/import): mirror an existing repo in one step.
71- [Install script](${b}/install): one-line setup for the CLI + Claude Desktop MCP config.
72
73## Conventions
74
75- Personal access tokens are prefixed \`glc_\`; OAuth access tokens \`glct_\`. Pass either as \`Authorization: Bearer <token>\`.
76- Repos are addressed as \`${b}/<owner>/<repo>\`; clone over HTTPS at \`${b}/<owner>/<repo>.git\`.
77`;
78}
79
80seo.get("/llms.txt", (c) => {
81 c.header("Content-Type", "text/plain; charset=utf-8");
82 c.header("Cache-Control", "public, max-age=3600");
83 return c.body(buildLlmsTxt());
84});
85
86seo.get("/.well-known/llms.txt", (c) => {
87 c.header("Content-Type", "text/plain; charset=utf-8");
88 c.header("Cache-Control", "public, max-age=3600");
89 return c.body(buildLlmsTxt());
90});
91
3692const STATIC_PATHS = [
3793 "/",
3894 "/explore",
3995