The registry API.
BASE URL https://api.policylayer.com
- Free, no key required
- Implements the official MCP Registry API
- Risk data under _meta in every entry
- Works with any spec-compatible client
- API key on every request
- Full records: identity, posture, grade, tools, policy
- Ordered change feed and signed webhooks
- Nightly snapshot, signed download
Integrate in four steps.
Get your key
Buy a licence ($199/month) and
the key lands in your inbox, prefixed plr_. Send it on every /v1
request; either header works.
curl https://api.policylayer.com/v1/registry/servers/stripe \ -H "Authorization: Bearer plr_your_key" # or curl https://api.policylayer.com/v1/registry/servers/stripe \ -H "X-API-Key: plr_your_key"
Fetch your first record
One call returns everything we know about a server: verified identity, probed auth posture, the A-to-F grade, every tool with capability flags and a recommended policy.
{
"slug": "stripe",
"version": 14,
"updatedAt": "2026-07-07T02:14:09.812Z",
"record": {
"slug": "stripe",
"name": "@stripe/mcp",
"identityConfidence": "verified",
"identityEvidence": { … },
"posture": { "auth": "gated", "cors": null, "rateLimited": false, … },
"grade": "F",
"toolCount": 21,
"tools": [
{
"name": "create_refund",
"category": "Financial",
"severity": "Critical",
"riskWeight": 0.85,
"capabilities": { "movesMoney": true, … },
"owaspClasses": [ … ],
"recommendedPolicy": { "action": "approve", … }
},
…
],
"recentEvents": [ … ]
}
} Stay current
Poll the change feed with a cursor, or register a webhook and we push. Either way you hear about new tools, grade moves and posture flips within the hour.
# poll GET /v1/registry/events?after_id=48112&severity=warning # or push POST /v1/registry/webhooks
Take the whole catalogue
The snapshot endpoint redirects to a signed download of the nightly export: gzipped NDJSON, one record per line. Build your own index from it.
GET /v1/registry/snapshot # 302, one-hour signed URL
/v0/servers Registry-spec entries, cursor-paginated FREE GET /v0/servers/{name} One entry, by spec name or slug FREE GET /v1/registry/servers/{slug} Full record for one server LICENCE GET /v1/registry/servers All records, keyset-paginated LICENCE GET /v1/registry/events Ordered change feed, severity filter LICENCE GET /v1/registry/snapshot Signed link to the nightly bulk export LICENCE POST /v1/registry/webhooks Register a webhook, HMAC secret shown once LICENCE GET /v1/registry/webhooks List your registered webhooks LICENCE DELETE /v1/registry/webhooks/{id} Remove a webhook LICENCE Keys and errors.
Keys authenticate every /v1 request (step 1 above shows both headers). A key is shown once; we store only a hash. A lost or leaked key is rotated, not resent: the old key stops working the moment the new one is issued.
Retry-After header says when to come back./v0/servers?limit=50&cursor=…List servers
Cursor-paginated list of every published server. Entries carry server
(name, packages, remotes, repository) as the MCP Registry spec defines;
PolicyLayer's data sits under _meta["com.policylayer/registry"].
metadata.nextCursor is present until the final page.
{
"servers": [{
"server": {
"name": "com.policylayer/github",
"title": "GitHub",
"packages": [{ "registryType": "npm",
"identifier": "@modelcontextprotocol/server-github" }]
},
"_meta": {
"com.policylayer/registry": {
"grade": "D",
"identityConfidence": "verified",
"toolCount": 86,
"record": "https://api.policylayer.com/v1/registry/servers/github"
}
}
}],
"metadata": { "nextCursor": "…", "count": 1 }
} /v0/servers/{name}Get a server
A single entry, by spec name or bare slug.
curl "https://api.policylayer.com/v0/servers/com.policylayer/stripe" curl "https://api.policylayer.com/v0/servers/stripe"
/v1/registry/servers/{slug}Get a record
The full record for one server (step 2 above shows the shape): identity with confidence and evidence, probed posture, grade, every tool with parameters, capability flags, risk classification and a recommended policy, plus the ten most recent change events.
stripe.The response carries a version field that increments only when record
content changes. The same version means an identical record.
curl https://api.policylayer.com/v1/registry/servers/stripe \ -H "X-API-Key: plr_your_key"
/v1/registry/servers?after={slug}&limit=100List records
Full records, keyset-paginated in slug order. A page shorter than limit
is the final one.
nextAfter value from the previous page.GET /v1/registry/servers?limit=100 GET /v1/registry/servers?after=figma&limit=100
/v1/registry/events?after_id=0&limit=200Change events
The ordered change feed. Ids are strictly increasing: persist the last id you processed and resume from it.
info, notice, warning, critical.Types include tool_added, tool_removed,
tool_reclassified, grade_changed,
auth_downgraded, impostor_flagged.
{
"events": [
{
"id": 48112,
"server": "figma",
"type": "grade_changed",
"payload": { "from": "C", "to": "D" },
"fromVersion": null,
"toVersion": "1.4.2",
"severity": "warning",
"occurredAt": "2026-07-06T23:41:02.114Z"
}
],
"nextAfterId": 48112
} /v1/registry/snapshotSnapshot
Returns a 302 redirect to a one-hour signed download of the nightly catalogue:
gzipped NDJSON, one record per line. Add ?format=json to get the URL,
size and generation time as JSON instead.
curl -L https://api.policylayer.com/v1/registry/snapshot \ -H "X-API-Key: plr_your_key" \ -o registry.ndjson.gz
/v1/registry/webhooksWebhooks
Push instead of poll. The response includes the HMAC secret, shown once. Deliveries start from the moment of registration; for earlier events, use the events endpoint.
Delivery is at-least-once and in order per webhook. Respond with any 2xx to advance the cursor. Failed deliveries are retried on the next cycle; a webhook that fails twenty times in a row is disabled until re-registered.
GET lists your webhooks; DELETE /v1/registry/webhooks/{id}
removes one.
{ "url": "https://you.example/hook",
"minSeverity": "warning" } VERIFY EVERY DELIVERY X-PolicyLayer-Signature: sha256=<hex hmac of the raw body>
How the API behaves.
{ servers, nextAfter }, { events, nextAfterId }. Single records come as { slug, version, updatedAt, record } — the record itself is inside record.after/cursor token from the previous page, never offsets. Every retry is idempotent.Retry-After.