Files
hanzo.ai/scripts/wellknown.mjs
zeekay 4449d9234a agents: auth.md, an MCP server card, a skills index, content signals
robots.txt states its Content Signals in both groups: search, ai-input
and ai-train, which the groups already allow. /auth.md says how an
agent gets a credential today (wallet sign-in, device flow, an API key)
and that the auth.md registration protocol is not implemented.

/.well-known/mcp/server-card.json describes the MCP endpoint at
api.hanzo.ai/v1/mcp (SEP-2127, protocol 2026-07-28), and the API
catalog names it. skill.mjs writes /skill.md again as an Agent Skill
with its index (Agent Skills Discovery 0.2.0), the digest taken from
the bytes it writes. wellknown.mjs copies subdirectories and refuses a
server card that does not point at the API host.
2026-10-26 20:41:36 -07:00

101 lines
3.9 KiB
JavaScript

/**
* Put /.well-known into the export, and refuse to finish the build without it.
*
* `public/.well-known/security.txt` is committed, the deploy was current, and
* the URL still answered with the app shell — because this site is
* `output: 'export'` and a static export plus a SPA fallback answers EVERY path
* with index.html. So a missing file does not 404. It returns 200, with HTML,
* and looks fine to anything that checks a status code.
*
* That is the whole reason this script exists rather than a copy step: the
* failure it guards against is invisible from the outside. RFC 9116 fixes the
* address at /.well-known/security.txt, so if the file is not there the file
* does not exist, no matter what the server returns.
*
* Loud, never partial, like noindex.mjs beside it: it copies, then reads back
* what it wrote, and exits non-zero if the bytes are not there. A build that
* quietly ships without a disclosure address is worse than a build that stops.
*/
import { readdirSync, mkdirSync, copyFileSync, readFileSync, existsSync } from "node:fs";
import { dirname, join } from "node:path";
const SRC = "public/.well-known";
const DST = "out/.well-known";
if (!existsSync(SRC)) {
console.error(`[well-known] ${SRC} does not exist — nothing to publish.`);
process.exit(1);
}
// Every file under SRC, by its path below it: a document may sit a directory
// down (/.well-known/mcp/server-card.json).
const names = readdirSync(SRC, { recursive: true, withFileTypes: true })
.filter((e) => e.isFile() && !e.name.startsWith("."))
.map((e) => join(e.parentPath, e.name).slice(SRC.length + 1))
.sort();
if (names.length === 0) {
console.error(`[well-known] ${SRC} is empty.`);
process.exit(1);
}
for (const name of names) {
mkdirSync(dirname(join(DST, name)), { recursive: true });
copyFileSync(join(SRC, name), join(DST, name));
}
// Read back rather than trust the copy. The point is to prove the bytes are at
// the address, which is the one thing nobody could confirm from outside.
let bad = 0;
for (const name of names) {
const out = join(DST, name);
if (!existsSync(out)) {
console.error(`[well-known] ${out} missing after copy`);
bad++;
continue;
}
const body = readFileSync(out, "utf8");
if (name === "security.txt" && !/^\s*(#|Contact:)/m.test(body)) {
console.error(`[well-known] ${out} does not look like RFC 9116 — no Contact: field`);
bad++;
}
// RFC 9727's API catalog is a linkset document; anything else is not one.
if (name === "api-catalog") {
let linkset = null;
try {
linkset = JSON.parse(body).linkset;
} catch {}
if (!Array.isArray(linkset) || !linkset.length) {
console.error(`[well-known] ${out} is not an RFC 9727 linkset ({"linkset": [...]})`);
bad++;
}
}
// An MCP Server Card (SEP-2127): the fields a client needs to connect, and a
// streamable-http remote on the API host. Anything else is not one.
if (name === "mcp/server-card.json") {
let card = null;
try {
card = JSON.parse(body);
} catch {}
const remote = card?.remotes?.[0];
if (
card?.$schema !== "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json" ||
!/^[a-zA-Z0-9.-]+\/[a-zA-Z0-9._-]+$/.test(card?.name ?? "") ||
!card?.version ||
!(card?.description?.length > 0 && card.description.length <= 100) ||
remote?.type !== "streamable-http" ||
!/^https:\/\/api\.hanzo\.ai\//.test(remote?.url ?? "")
) {
console.error(`[well-known] ${out} is not an MCP Server Card for the API host`);
bad++;
}
}
// Apple Pay's domain proof is hex, and Square checks it byte for byte: anything else is not it.
if (name === "apple-developer-merchantid-domain-association" && !/^[0-9A-Fa-f]+\s*$/.test(body)) {
console.error(`[well-known] ${out} is not the hex domain association Apple Pay asks for`);
bad++;
}
}
if (bad) process.exit(1);
console.log(`[well-known] published ${names.length} file(s): ${names.join(", ")}`);