{
  "name": "MCP registry list walk - per-page receipt chain",
  "version": "1.0",
  "path": "/v1/registry-walk",
  "servedBy": "https://penny-surface-contract.penny-mcamp.workers.dev/v1/registry-walk",
  "measured_at": "2026-10-21T23:52:04Z",
  "endpoint": "https://registry.modelcontextprotocol.io/v0/servers",
  "method": {
    "what": "One anonymous client walked the registry's server list endpoint to cursor exhaustion, one page every >=1 second, page size 100, no credentials, no third-party MCP server contacted. Each page's body was saved; each page's cursor was recorded both in and out.",
    "page_size": 100,
    "credentials_sent": false,
    "third_party_servers_contacted": 0,
    "termination": "cursor_exhausted",
    "pages_logged": 480,
    "transient_failures": 2,
    "transient_failure_rule": "A non-2xx page or a transport timeout is NOT an end-of-cursor. Each failure was retried AT THE SAME cursor and recorded with its status, its file and its elapsed time; the chain contains no gap where a failure sits."
  },
  "counts": {
    "pages": 480,
    "rows_served": 47944,
    "unique_name_at_version": 47944,
    "duplicate_name_at_version": 0,
    "rows_with_at_least_one_remote": 31334,
    "rows_with_at_least_one_package": 18372,
    "rows_with_neither": 512,
    "remote_entries": 32105,
    "remote_types": {
      "streamable-http": 30999,
      "sse": 1106
    },
    "distinct_remote_hosts": 22950,
    "arithmetic_check": "479 full pages x 100 + last page 44 = 47944"
  },
  "concentration": {
    "note": "Remote rows are heavily concentrated in a few hosts, so a count of remote rows is NOT a count of independent operators. This is printed so the bigger number is not read as a bigger market.",
    "top_hosts_by_remote_entries": [
      {
        "host": "agent-observatory-sensor.nolimit-observatory.workers.dev",
        "remote_rows": 2365,
        "share_of_remote_rows": 0.0755
      },
      {
        "host": "gateway.pipeworx.io",
        "remote_rows": 1878,
        "share_of_remote_rows": 0.0599
      },
      {
        "host": "api.mcp.ai",
        "remote_rows": 1116,
        "share_of_remote_rows": 0.0356
      },
      {
        "host": "mcp.apify.com",
        "remote_rows": 434,
        "share_of_remote_rows": 0.0139
      },
      {
        "host": "api.m2mcent.com",
        "remote_rows": 276,
        "share_of_remote_rows": 0.0088
      },
      {
        "host": "server.smithery.ai",
        "remote_rows": 217,
        "share_of_remote_rows": 0.0069
      },
      {
        "host": "a2awire.com",
        "remote_rows": 120,
        "share_of_remote_rows": 0.0038
      },
      {
        "host": "modevc.com",
        "remote_rows": 88,
        "share_of_remote_rows": 0.0028
      },
      {
        "host": "mcp.zovo.one",
        "remote_rows": 79,
        "share_of_remote_rows": 0.0025
      },
      {
        "host": "tooloracle.io",
        "remote_rows": 57,
        "share_of_remote_rows": 0.0018
      }
    ],
    "top3_share_of_remote_entries": 0.171
  },
  "transient_failures": [
    {
      "page": 145,
      "kind": "failure",
      "status": 500,
      "error": "HTTPError",
      "body_prefix": "{\"title\":\"Internal Server Error\",\"status\":500,\"detail\":\"Failed to get registry list\"}\n",
      "cursor_in": "guru.goanywhere/storefront:0.1.0",
      "elapsed_seconds": 11.64,
      "at": "2026-10-21T23:44:24Z",
      "recovery": "retried at this same cursor; the page that follows in chain[] carries cursor_in equal to this row's cursor_in and is the recovered read"
    },
    {
      "page": 479,
      "kind": "failure",
      "status": null,
      "error": "URLError: <urlopen error timed out>",
      "body_prefix": null,
      "cursor_in": "xyz.apexfaucet/apex-evm:1.0.6",
      "elapsed_seconds": 59.907,
      "at": "2026-10-21T23:51:53Z",
      "recovery": "retried at this same cursor; the page that follows in chain[] carries cursor_in equal to this row's cursor_in and is the recovered read"
    }
  ],
  "receipt": {
    "chain_root_sha256": "cb40a9184e4d21a1d180673a30f9e4b6649664c1c259e5ca0efa3d6153606911",
    "names_root_sha256": "6105080a239fdb451274dc5bc5fbd94e28c6e91c060d3b2914cead4adf4669b9",
    "what_the_roots_cover": "chain_root is sha256 over the 480 page_file_sha256 values joined with \\n in page order; names_root is the same over the 480 names_sha256 values. Two roots, because one of them (names) survives a different JSON serializer.",
    "cursor_check": "each chain entry's page_file name is '<zero-padded page>-<cursor_in_sha12>.json', so chain continuity is checkable from the recorded file names alone."
  },
  "how_to_re_derive": [
    "1. GET the same list endpoint with page size 100 and no credentials: the response object carries `servers` and `metadata.nextCursor`.",
    "2. For each page: body_bytes = the raw response body; page_file_sha256 = sha256 of the body re-serialized exactly as json.dump(json.loads(body)) in Python (default separators ', ' and ': ', ensure_ascii=True). names_sha256 = sha256 of the sorted `name@version` strings for that page joined with '\\n' (no trailing newline) - this one is whitespace- and serializer-independent.",
    "3. Order pages by the cursor chain: page 1 has no incoming cursor; every later page's incoming cursor must equal the previous page's outgoing cursor; the walk ends when a page carries no nextCursor.",
    "4. Compare your chain_root_sha256 / names_root_sha256 with the values here. If they match, you have reproduced this same population at your moment; if they differ, compare page by page to find where the registry changed, and publish that page rather than a new total.",
    "5. If a page answers non-2xx or times out, retry at the SAME cursor; never treat an error body as an end-of-cursor. That single mistake is how a first-page slice gets quoted as a population."
  ],
  "chain": {
    "pages": 480,
    "full_pages": 479,
    "last_page_records": 44,
    "first_page": 1,
    "last_page": 480,
    "whole_chain": "/v1/registry-walk?pages=1",
    "one_page": "/v1/registry-walk?page=N for N in 1..480; anything outside that range is refused with 404 and a named reason, never an empty 200",
    "arithmetic": "479 full pages x 100 + last page 44 = 47944"
  },
  "what_this_does_not_say": [
    "It is a count of ROWS at one moment, not of active servers, live endpoints or operators, and not a market size.",
    "It says nothing about whether any listed server works: this walk read metadata only, contacted no MCP server and sent no credential.",
    "The raw bodies are not published - only two hashes per page - so recomputing the totals means re-running the walk, not re-deriving these hashes from published bytes.",
    "Another operator measured 38,977 rows / 24,321 with a remote on 2026-10-04. That is a different window from a method we have not inspected; it is not contradicted here and this block does not correct it.",
    "Our own conformance census covers a 109-row slice of this population and carries that slice's selection bias; /v1/census serves it and says so there too."
  ],
  "contribute": "If your walk disagrees, POST /v1/contribute with kind=discrepancy, the page number, and your page hash - a published number is never silently replaced, and the disagreement is stored with its evidence.",
  "analytics": {
    "served_as": "https://penny-surface-contract.penny-mcamp.workers.dev/v1/stats/summary",
    "what": "the compact scoreboard for this origin: per-day requests, the self (x-penny-self: 1) vs non-self split per route family, and per published ?ref= label, with an absent day or label reported as null rather than 0",
    "full_readout": "https://penny-surface-contract.penny-mcamp.workers.dev/v1/stats",
    "how_to_discount_us": "only the nonSelf figures describe anyone other than us, and the marker is a declaration, not a verified identity"
  },
  "free": "Free and unauthenticated. No signup, no key, no payment, no tracking parameter required."
}