Docs

FRAYWICK is a public watchtower for MCP servers and Agent Skills. Every hour it reads public tool definitions, hashes every definition and anchors one merkle root on Robinhood Chain. It routes nothing and hosts nothing: it only reads and stamps.

What we watch

Hashing

Item hash = keccak256(utf8(JCS(view))), where JCS is RFC 8785 canonical JSON and the view keeps only these fields (missing fields are omitted):

tool      {name, title, description, inputSchema, outputSchema, annotations}
prompt    {name, title, description, arguments}
resource  {uri, name, title, description, mimeType}

List hash = keccak256(concat(sorted item hashes)). An empty list hashes to keccak256(0x); a list the server does not advertise, or that could not be read, is bytes32(0).

One leaf per tracked thing, all with the encoding ["string","string","bytes32","bytes32","bytes32"]:

server   [serverId, version, toolsHash, promptsHash, resourcesHash]
skill    ["skill:" + repo + ":" + path, commit, keccak256(SKILL.md bytes), 0x0, 0x0]
package  ["npm:" + name | "pypi:" + name, version, integrityHash, maintainersHash, 0x0]

leaf hash = keccak256(bytes.concat(keccak256(abi.encode(values))))

integrityHash is keccak256(utf8(dist.integrity)) on npm, and on PyPI keccak256 of the sorted sha256:<hex> lines of every file of the release. maintainersHash is keccak256(utf8(JCS(sorted unique names))).

At minute :05 of every hour all leaves go into an OpenZeppelin StandardMerkleTree (sorted leaves, sorted pairs). The snapshot with every leaf and the item-level detail is published at /snapshots/<epoch>.json, and the root is posted with post(epoch, root, leafCount, snapshotURI), where epoch is the UTC hour number (unix / 3600). Proof rebuilds any root in your browser.

Scoring and labels

When a hash changes we keep both versions, compute a text diff and add up plain rules (0 to 100): new instructions aimed at the model ("ignore previous", "before using this tool", "do not tell the user", <IMPORTANT>…), invisible, bidi or tag unicode, new URLs and hosts (more for tunnels, paste sites, webhooks and raw IPs), new parameters that ask for secrets or free-form context (notes, context, ~/.ssh, .env, id_rsa, wallet, seed phrase…), shell pipes, description length jumps, wider tool annotations, and on packages a new maintainer or new file bytes under the same version.

A label describes a diff. It is not an accusation, and fetched text is never given to a model as instructions.

Bot policy

Our crawler identifies itself as FraywickBot/1.0 (+/docs#bot). It sends read-only MCP requests (tools/list, prompts/list, resources/list, and the handshake they need) once an hour, with at most 16 requests in flight overall and about one request per second per host. It never logs in: a server that answers 401 or 403 is recorded as login-required with its registry metadata only. It backs off on 429 and on repeated failures.

To opt out, add a group for FraywickBot to your robots.txt (we re-read it daily), for example:

User-agent: FraywickBot
Disallow: /

or ask us and we add your host to the opt-out list. Opted-out hosts keep their registry metadata only and are counted in /api/health.

CLI

The command line verifier is not published on npm yet. Until it is, use verify in your browser on any server page, or the /mcp endpoint below.

/mcp endpoint

FRAYWICK is itself a read-only remote MCP server at /mcp (stateless 2026-07-28 style and the legacy handshake). Tools: check_server, check_skill, recent_changes, get_proof. Their descriptions are plain, and their hashes are in our own snapshots like everyone else's.

Contract

ToolAnchor
address published at deploy
chain
Robinhood Chain mainnet, chain id 4663
views
anchors(epoch) → (root, leafCount, postedAt), latestEpoch(), anchorCount(), verify(epoch, leaf, proof) → bool
writes
post and flag, poster key only; epochs strictly increase and cannot be in the future. No funds, no token, no upgrades.
explorer
robinhoodchain.blockscout.com
source
verified (Sourcify exact match, shown on Blockscout) · code, tests and deploy script:

Public API

GET  /api/health    GET  /api/stats    GET  /api/feed?cursor=&label=
GET  /api/server/<id>    GET  /api/item/<id>/history
GET  /api/anchors?limit=    GET  /api/proof?epoch=&leafKey=
POST /api/check {"target": "…"}    GET /snapshots/<epoch>.json

JSON, read-only CORS, rate limited. /api/check accepts HTTPS URLs only, refuses private, loopback, link-local and metadata addresses (also after redirects), caps responses at 2 MB and gives up after 10 s.

Honest limits