MCP Verification Gate (MCP conduct register) API

Read-only, keyless REST API at https://gate.horizonshield.dev described by an OpenAPI 3.1.0 document (25 operations, version 0.4.7): the public register of MCP endpoints measured nightly against five stated conditions (speaks MCP, publishes an A2A agent card, declares who pays it, identical input gives identical output, the verdict is recomputable), per-endpoint history and change feeds (JSON and Atom), badges and embeds, the A2A Conduct Extension v1 specification, hash recomputation and NIP-01/BIP340 event verification, and the gate's own self-measurement. The same register is exposed over MCP (5 tools at /mcp) and A2A (/a2a), and consumed by the first-party npm client mcp-conduct and a GitHub Action.

Operations 25

GET /register Every row in the register
GET /verified.json Only the rows that passed every measured condition
GET /history Past measurements for one endpoint
GET /changes State changes only
GET /feed.xml The same changes as an Atom feed
GET /sitemap.xml One URL per measured endpoint
GET /e/{host}{path} The permanent page for one measured endpoint
GET /badge A badge drawn from the register at request time
GET /embed The verification envelope, ready to paste on your own site
GET /badge/seal A larger badge, sized for print and for other people's sites
GET /spec The five conditions, stated in full
GET /ext/conduct/v1 A2A Conduct Extension v1 (the URI is the identifier)
GET /self This gate measured against its own conditions
GET /health Liveness and the deployed commit
POST /recompute Work out which canonicalization reproduces a claimed hash
POST /verify-event Recompute a NIP-01 event id and verify its BIP340 signature
GET /mould Mould records. What class of assumption a fix came from, where the author searched for it, and what they found. A record with an empty search is published as such.
GET /sweep/last When the last scheduled re-measurement ran, under which coordinate rule, and why
GET /nenrin/window The instant coordinate's public window: salt commitment at creation, pinned rule, beacon, salt revealed after the window closes (0.3.5), and since 0.4.0 whether the commitment was
GET /register/lookup One read before connecting (conduct-v1.1 section 7): status verified / pending / declined / unknown, the last published ring's counts, where the record lives, and what the answer d
GET /watchlist Endpoints scheduled for re-measurement
GET /.well-known/agent-card.json A2A agent card for this gate
GET /.well-known/mcp-register.json Machine readable summary of the register
POST /check Measure one endpoint now
POST /mcp The same register over MCP

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/mcp-verification-gate-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

horizonshield-dev-mcp-conduct-register-openapi.json Raw ↑
{
  "openapi": "3.1.0",
  "info": {
    "title": "MCP conduct register",
    "version": "0.4.7",
    "description": "A register of measured conduct for MCP endpoints. Five stated conditions are measured on a schedule. A condition that could not be measured is never counted as a pass, including for the operator of this gate. Read only. No account, no key, no fee. Every verdict carries a SHA-256 that a stranger can recompute.",
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/licenses/MIT"
    },
    "contact": {
      "url": "https://shield.the-horizons-innovation.com/verify-directory/"
    }
  },
  "servers": [
    {
      "url": "https://gate.horizonshield.dev"
    }
  ],
  "paths": {
    "/register": {
      "get": {
        "summary": "Every row in the register",
        "description": "Rows are scheduled measurements, not endorsements. An endpoint that is absent has simply never been measured.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/verified.json": {
      "get": {
        "summary": "Only the rows that passed every measured condition",
        "description": "A schema.org Dataset. Returns zero rows when zero rows pass. The bar is not lowered to avoid an empty list.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/history": {
      "get": {
        "summary": "Past measurements for one endpoint",
        "description": "Query with ?endpoint=. Records are appended, never edited. The gate keeps the most recent 400 per endpoint (30 until 2026-09-05); older records leave this response, so archive the export.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/changes": {
      "get": {
        "summary": "State changes only",
        "description": "A change means a condition flipped, not merely that a new verdict was issued.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/feed.xml": {
      "get": {
        "summary": "The same changes as an Atom feed",
        "description": "For subscribing rather than polling.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "summary": "One URL per measured endpoint",
        "description": "Only endpoints that have actually been measured appear. No page is minted for an endpoint nobody has measured.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/e/{host}{path}": {
      "get": {
        "summary": "The permanent page for one measured endpoint",
        "description": "Carries the verdict, the time it was taken, the SHA-256 of the record, and the command to recompute it. 404 when the endpoint has never been measured.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/badge": {
      "get": {
        "summary": "A badge drawn from the register at request time",
        "description": "Query with ?endpoint=. Short cache, so a green cannot be kept up after the row stops being green.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/embed": {
      "get": {
        "summary": "The verification envelope, ready to paste on your own site",
        "description": "Query with ?endpoint=. The text twin of the badge: JSON-LD for crawlers and the same statement as visible text for language models that fetch the page. Carries status, the record sha256, the recompute recipe and what it does not establish; never a score. Add format=json for the JSON-LD alone. A copy is a snapshot with its date inside; the /e/ page is the live statement.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/badge/seal": {
      "get": {
        "summary": "A larger badge, sized for print and for other people's sites",
        "description": "Query with ?endpoint=. Carries the operator label, the endpoint, the measurement date and the verify URL. Add download=1 to receive it as a file. A downloaded file is a snapshot: the date is drawn into the image for exactly that reason, and the live row remains the only current statement.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/spec": {
      "get": {
        "summary": "The five conditions, stated in full",
        "description": "Includes what a pass does not mean.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/ext/conduct/v1": {
      "get": {
        "summary": "A2A Conduct Extension v1 (the URI is the identifier)",
        "description": "JSON by default. Accept: text/markdown returns the specification text. Declared by agent cards under capabilities.extensions[].",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/self": {
      "get": {
        "summary": "This gate measured against its own conditions",
        "description": "It does not currently pass all of them, and the reason is published.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/health": {
      "get": {
        "summary": "Liveness and the deployed commit",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/recompute": {
      "post": {
        "summary": "Work out which canonicalization reproduces a claimed hash",
        "description": "Send a JSON object as it was published, and a SHA-256 somebody claims was taken over it. Returns every recipe that reproduces the value, whether that recipe is RFC 8785 JCS, how many combinations were tried, and the exact space they covered. A hash that could not be reproduced is reported as not reproduced with the number of combinations tried, and never as invalid. Omit the claimed hash to receive canonical forms and their hashes instead, so a reading can be held by someone who does not operate the source. Contacts nothing. Stores nothing.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "object"
                ],
                "properties": {
                  "object": {
                    "type": "object",
                    "description": "The JSON object as published."
                  },
                  "claimed": {
                    "type": "string",
                    "description": "Optional. 64 hex characters. Omit to receive canonical forms."
                  },
                  "max_candidates": {
                    "type": "integer",
                    "description": "Optional upper bound on combinations tried."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/verify-event": {
      "post": {
        "summary": "Recompute a NIP-01 event id and verify its BIP340 signature",
        "description": "Computed here from the curve parameters, with no library and no network call, so neither the issuer's own verification service nor a dependency has to be trusted. For each field name given, the answer states whether it sits inside the signed bytes or beside them. A valid signature shows that the holder of the key signed those bytes. It does not make the bytes true.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "event"
                ],
                "properties": {
                  "event": {
                    "type": "object",
                    "description": "A complete signed event with id, pubkey, created_at, kind, tags, content and sig."
                  },
                  "assert_inside": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Optional field names to locate inside the signed bytes. Both content and tags are searched, and the answer says which one carried the field."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/mould": {
      "get": {
        "summary": "Mould records. What class of assumption a fix came from, where the author searched for it, and what they found. A record with an empty search is published as such.",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/sweep/last": {
      "get": {
        "summary": "When the last scheduled re-measurement ran, under which coordinate rule, and why",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/nenrin/window": {
      "get": {
        "summary": "The instant coordinate's public window: salt commitment at creation, pinned rule, beacon, salt revealed after the window closes (0.3.5), and since 0.4.0 whether the commitment was filed to the ledger witness intake before the window opened",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/register/lookup": {
      "get": {
        "summary": "One read before connecting (conduct-v1.1 section 7): status verified / pending / declined / unknown, the last published ring's counts, where the record lives, and what the answer does not establish. Query endpoint=<https MCP endpoint>. Cached 24 hours. No score.",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/watchlist": {
      "get": {
        "summary": "Endpoints scheduled for re-measurement",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/.well-known/agent-card.json": {
      "get": {
        "summary": "A2A agent card for this gate",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/.well-known/mcp-register.json": {
      "get": {
        "summary": "Machine readable summary of the register",
        "description": "",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/check": {
      "post": {
        "summary": "Measure one endpoint now",
        "x-known-limitation": "Known limitation of this instrument, measured 2026-08-18 and unresolved: requests carrying the Python urllib user agent are refused with 403 by a Cloudflare managed rule in front of this Worker, so that one client is turned away before any code here runs. curl, python-requests, node-fetch, undici, axios, okhttp, Go, Java, Postman and an absent user agent were all measured at 200 on the same day. This is stated here rather than worked around silently.",
        "description": "Measures the stated conditions against the endpoint you name. Determinism stays unmeasured unless the owner has recorded consent, because measuring it requires calling a tool on someone else's server.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpoint"
                ],
                "properties": {
                  "endpoint": {
                    "type": "string",
                    "format": "uri",
                    "description": "The MCP endpoint to measure."
                  },
                  "allow_tool_call": {
                    "type": "boolean",
                    "description": "Only the owner of the endpoint may set this true."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "summary": "The same register over MCP",
        "description": "Streamable HTTP, JSON-RPC 2.0.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  }
}