MCP Registry Validator — Check Endpoints Actually Resolve avatar

MCP Registry Validator — Check Endpoints Actually Resolve

Pricing

Pay per usage

Go to Apify Store
MCP Registry Validator — Check Endpoints Actually Resolve

MCP Registry Validator — Check Endpoints Actually Resolve

Validate that MCP endpoints actually answer before you depend on them. Checks liveness, lists the tools a server really exposes right now, and detects schema drift since the last run. Free, no auth.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Parker Levin

Parker Levin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 hours ago

Last modified

Share

mcp-uptime

Is that MCP endpoint alive, what does it actually expose right now, and has its tool schema changed since you last looked?

52% of remote MCP endpoints are dead. Schema drift is worse than downtime, because downtime is loud and a quietly-changed inputSchema fails mid-agent-run and looks like a model error. This service answers four questions about any public MCP endpoint: liveness, tool inventory, schema drift, and 30-day uptime.

It ships twice from one codebase:

SurfaceWhat it isWhy
Cloudflare WorkerAn MCP server (/mcp) plus a plain HTTP API (/v1/*)Reach — agents discover it through the MCP registry
Apify ActorSame check logic, pay-per-eventMetering + payout — Apify counts runs and users natively

Free tier: 20 checks/day, on-demand. Paid tier ($9/mo, not wired up yet): scheduled monitoring, drift history, webhook alerts.


MCP tools

ToolProbes?Returns
check_mcp_endpointyesstatus, transport, latency, tool count (or the full list), schema hash, drift
check_schema_driftyestools added / removed / modified since the last snapshot
get_uptimenouptime % and incident windows over the last N days (max 30)
get_known_toolsnolast observed tool list + when the schema last changed

Point any MCP client at POST https://<your-worker>.workers.dev/mcp. Streamable HTTP; no auth needed for the free tier.

// tools/call → check_mcp_endpoint
{
"endpoint": "https://mcp.example.com/mcp",
"status": "up", // up | degraded | down
"transport": "streamable-http",
"latencyMs": 214,
"protocolVersion": "2025-06-18",
"server": { "name": "example-mcp", "version": "1.4.0" },
"toolCount": 12,
"toolsHash": "9f2c…",
"drift": { "changed": true, "added": [], "removed": ["search_v1"],
"modified": [{ "name": "search", "fields": ["inputSchema"] }] },
"firstSeen": false,
"error": null
}

degraded is its own status on purpose: the server answered initialize but its tools/list failed. Collapsing that into down would hide exactly the failure this product exists to surface.

HTTP API

MethodPathAuthNotes
POST/v1/checknone{ "url": "...", "include_tools": false }
GET/v1/status?url=…&days=30nonestored history, no probe
GET/POST/v1/monitorskeylist / create (scheduled checks + webhook)
GET/DELETE/v1/monitors/:idkeyown monitors only
POST/mcpnoneMCP Streamable HTTP
GET/healthnoneliveness
GET/admin/statsoperator secretprobe call counts; 404s without the secret

Security model

The whole job of this service is to fetch a URL a stranger hands it, so the guard rails are the product, not decoration. Implemented against the access-control matrix in ../PROBE-SPEC.md §SECURITY:

  • SSRF guard (src/core/ssrf.ts) — http/https only; loopback, RFC1918, CGNAT, link-local (incl. 169.254.169.254), NAT64, multicast, reserved, and IPv4-mapped IPv6 all rejected; internal TLDs and bare hostnames rejected; embedded credentials rejected; internal service ports blocked. Re-applied to every redirect hop — redirects are followed manually because a public host is allowed to bounce you at metadata.
  • Rate limit before auth — a coarse per-IP limiter runs before any key lookup, so an unauthenticated flood never reaches the tenants table. Plan quotas apply afterwards, on the resolved principal.
  • Fail closed — if the counter store errors, requests are denied (503), not admitted.
  • No cross-tenant reads — monitor queries are tenant-scoped in SQL. Another tenant's monitor returns 404, not 403, so ids are not an existence oracle.
  • No secrets or caller data in logs — callers are stored as SHA-256 hashes, never raw IPs; API keys are stored only as hashes; error bodies are fixed codes, never remote content or internals; URL query strings (which routinely carry tokens) are dropped before an endpoint is persisted.
  • Untrusted remote content — tool names and descriptions come from the endpoint being probed and are handed to agents, so they are count-capped (200) and length-capped before they reach storage.
  • Dependencies — exact-pinned, with a lockfile. The Worker has zero runtime dependencies: the MCP server is hand-rolled JSON-RPC.

Verified by tests/ssrf.test.ts, tests/ratelimit.test.ts, and tests/tenant.test.ts — the three tests the spec names.

Layout

src/core/ runtime-agnostic: SSRF guard, prober, drift, uptime, quotas, auth
src/worker/ Cloudflare Worker — HTTP router, MCP server, D1 adapter
src/actor/ Apify Actor entry point
tests/ node:test, no test framework to install
.actor/ actor.json, input schema, Dockerfile, PPE event reference
schema.sql D1 schema

src/core imports nothing but the Web platform (fetch, URL, crypto.subtle), which is why both wrappers run identical logic and the tests exercise the real thing rather than a stand-in.

Development

npm install
npm test # 37 tests, no network, no framework
npm run typecheck
npm run dev # wrangler dev on http://localhost:8787

Tests run the TypeScript sources directly under Node's strip-only mode (Node 22+), so there is no build step and no test runner to install. That constrains the source: no enums, no namespaces, no constructor parameter properties.

Deployment: see DEPLOY.md. Accounts and go-live: see LAUNCH-README.md.