Public, unauthenticated HTTP API for submitting and retrieving compliance reports keyed by URL hash. Used by the mcp-compliance badge <url> flow but also callable directly for custom integrations.
- URL hash —
sha256(url).slice(0, 24)(96 bits). The canonical key for any externally-tested MCP server. Compute it withurlHash()from@yawlabs/mcp-compliance, or with any SHA-256 implementation. The API also accepts legacy 12-char hashes from v0.9.0 publishes. - Delete token — returned once at submit time; required to remove a previously-submitted report. Stored client-side at
~/.mcp-compliance/tokens.json(mode 0600) by the CLI. Never logged or rendered server-side. - Retention — 90 days from last submission. Resubmitting the same URL resets the clock.
https://mcp.hosting
Submit a compliance report.
Request:
POST /api/compliance/ext HTTP/1.1
Content-Type: application/json
Content-Length: <body length, max 256 KB>
{
"schemaVersion": "1",
"url": "https://my-server.example/mcp",
"specVersion": "2025-11-25",
"toolVersion": "0.10.1",
"timestamp": "2026-04-13T01:23:45.000Z",
"grade": "A",
"score": 95,
"overall": "pass",
"tests": [ /* array of TestResult */ ],
"summary": { "total": 85, "passed": 80, "failed": 5, "required": 12, "requiredPassed": 12 },
"categories": { /* per-category counts */ },
"warnings": [],
"serverInfo": { "name": "...", "version": "...", "protocolVersion": "2025-11-25", "capabilities": {} },
"toolCount": 0, "toolNames": [], "resourceCount": 0, "resourceNames": [], "promptCount": 0, "promptNames": []
}The full report shape is defined by schemas/report.v1.json shipped with the npm package.
Validation:
urlrequired, must be HTTP/HTTPSgrade∈["A", "B", "C", "D", "F"]score0–100 integertestsmust be an array- Body ≤ 256 KB serialized
- URL must NOT resolve to private IP (
127.0.0.1, RFC1918, link-local, IPv6 ULA, AWS metadata) — 403 witherror: 'Backend resolves to a private IP address' - Auth headers (
headers,auth) are stripped server-side regardless of what the client sends; do not include them in your submission
Rate limit: 10 submissions / IP / hour. Returns 429 with Retry-After header on exhaustion.
Response 200:
{
"hash": "a1b2c3d4e5f6a1b2c3d4e5f6",
"reportUrl": "https://mcp.hosting/compliance/ext/a1b2c3d4e5f6a1b2c3d4e5f6",
"badgeUrl": "https://mcp.hosting/api/compliance/ext/a1b2c3d4e5f6a1b2c3d4e5f6/badge",
"deleteToken": "<48-char base64url>"
}Errors:
| Code | Reason |
|---|---|
400 |
Malformed body, missing required fields, invalid grade/score |
403 |
URL resolves to private IP (SSRF protection) |
413 |
Body exceeds 256 KB |
429 |
Rate-limited (Retry-After header set) |
Retrieve a previously-submitted report as JSON.
:hash accepts 12 or 24 lowercase hex chars.
Response 200: the original report body (same shape as the POST input). 404: no report found for that hash.
Retrieve the SVG badge for a hash.
Content-Type: image/svg+xmlCache-Control: public, max-age=300- Always returns a valid SVG. Unknown hash → renders an "untested" badge in gray.
Remove a previously-submitted report.
Request:
DELETE /api/compliance/ext/<hash> HTTP/1.1
Authorization: Bearer <delete-token>Rate limit: 5 / IP / hour (defends against token-enumeration).
Responses:
204 No Contenton success401ifAuthorizationheader missing403if token doesn't match (crypto.timingSafeEqualcomparison)404if hash doesn't exist429if rate-limited
- Hashing: the server computes its own hash from the submitted URL. Client-supplied hashes are ignored. You cannot squat someone else's URL by sending a colliding hash; collisions in the 96-bit space are vanishingly rare anyway.
- Trust: reports are self-submitted and stored as-is. The badge reflects what the submitter claimed, not what an independent party verified. Display accordingly if you build a public dashboard on top of this API.
- Idempotency: resubmitting the same URL overwrites the previous report and issues a new
deleteToken. The old token is invalidated. Save the latest one. - Metrics: Prometheus counters at
/metricstrack submit volume by grade, SSRF rejects, rate-limit hits, and delete outcomes. Seemcp-hosting/src/api/metrics.ts.
# 1. Build a report (or use mcp-compliance test --format json)
report=$(mcp-compliance test https://my-server.example/mcp --format json)
# 2. POST it
response=$(curl -sS -XPOST https://mcp.hosting/api/compliance/ext \
-H 'Content-Type: application/json' \
--data "$report")
# 3. Save the delete token
echo "$response" | jq -r '.deleteToken' > .mcp-delete-token
# 4. Embed the badge in your README
echo "$response" | jq -r '.badgeUrl'hash=$(echo -n "https://my-server.example/mcp" | sha256sum | cut -c1-24)
curl -XDELETE "https://mcp.hosting/api/compliance/ext/$hash" \
-H "Authorization: Bearer $(cat .mcp-delete-token)"(Or just use mcp-compliance unpublish https://my-server.example/mcp which does both for you.)