How to test your MCP server before you ship it
Before you tell anyone to add your MCP server, you want to know what an agent sees the first time it connects. Not what your unit tests say about your handlers, but what comes back over the wire: does the endpoint answer, does the handshake match the revision it claims, is the tool list something a client can parse, what does a refusal look like.
The box on our homepage answers exactly that, for the endpoint you paste, without an account. This post walks through what it runs, what each check can conclude, and the three things the result will not do.
Three things to know first
The result is not signed. A homepage check is one on-demand observation. It is not a run in any directory page’s history, and we do not attach our signature to it. The result page says so, in those words. What the check keeps, and what it never creates, is in its own post.
The result page expires after 24 hours. The homepage says it next to the button, and the result page prints the exact time it stays available until. After that the link answers like any page that does not exist.
Checks that need a baseline show UNVERIFIED. Two checks compare your tool list with a baseline someone has approved. A one-off homepage check has no baseline, so both come back as UNVERIFIED with the reason printed on the row: no approved baseline exists for this target. That is the check telling you it could not compare, not a verdict on your server.
What happens when you press the button
You paste an https:// address and the probe connects the way a client would: without credentials, and only to the host you named. A homepage check sends at most 8 requests (at most 17 if the server returns its tool list in pages), stops after 10 seconds, and reads at most 2 MB of any one response. Redirects are followed only on the one GET a check sends, the fetch of your authorization metadata, and for at most 3 hops. The protocol requests are POSTs, and a POST is never sent again to a redirect target. A homepage check also never contacts a host other than the one you asked about. If a redirect or your authorization metadata points to another host, the check does not go there: a redirected request is recorded with the answer it got, and a check that needed the other host comes back not checked with a reason.
Every check in our registry appears as a row on the result page, 15 of them, whether or not it could run. A check that did not run is listed as such and never dropped. Each row ends in one of four states, spelled on the page as Verified, Failed, Observed risk and Not checked. Not checked is what the rest of our site calls UNVERIFIED, and it always carries a reason.
One practical limit: we send at most one on-demand check to a host every 10 minutes. If you fix something and press the button again straight away, you get the stored observation from the earlier check, labelled as such, not a new one. Make the change, wait, then check again.
Reach and handshake
reachability is Verified when our handshake exchange with your endpoint finishes with an HTTP response, whatever its status. A 401 counts: something answered. If the probe stops before that exchange finishes, because your endpoint asks us to back off (a 429, or a 503 with Retry-After), a response is over the size limit, the connection fails or a budget runs out, the row is Not checked with that reason, even if your endpoint had already answered an earlier request. A timeout is Not checked rather than Failed, because it could be our network and not your server.
discovery_handshake checks that the handshake behaves the way the identified protocol revision says it should: the modern server/discover or the legacy initialize. protocol_revision checks that the revision you speak is one we recognize. If either fails, the row gives the reason, for example that no revision was declared or that we do not recognize it. Neither can tell you the revision is fully implemented, only that the handshake you advertise is the handshake that answered.
If your server deliberately puts the handshake behind a key, these come back Not checked with the reason credential_required. We never send credentials, so we cannot see past your gate, and that is not the same as the gate being broken. How we separate a refusal from a failure has the details.
The tool list
tools_list asks for your tools and checks the structure of what comes back. An invalid structure is Failed, since a client would fail to parse it the same way. It does not tell you the tools work: we never call them.
error_taxonomy does call one thing, and it is a tool that does not exist. The probe asks for a tool called __mcpcheckup_probe_nonexistent_tool__ and looks at the shape of your refusal. A well-formed JSON-RPC error is Verified. An HTML error page, an empty body, or a 200 carrying a bare {"error": "nope"} is Observed risk, because a client has no reliable way to read it. A plain 401 or 403 is not counted against you. If your tool list is paged, or already contains a tool with that name, the call is withheld and the row is Not checked.
tool_description_hygiene reads your tool names and descriptions, the text an agent reads before it decides what to call, and looks for zero-width characters, Unicode tag characters, bidi overrides, private-use characters, unusually long text, phrasing that instructs the assistant instead of describing the tool, pseudo-system markers, embedded markup, non-ASCII tool names. A hit is Observed risk, never Failed. It reports a signal and cannot infer intent. A tool that really does handle Unicode may trip some of these for good reasons.
toolset_fingerprint and schema_fingerprint compute a fingerprint of your tool list and of each input schema. Verified here only means the fingerprint could be computed. They say nothing about whether anything changed.
At the edge of the connection
redirect_policy records a cross-host redirect as Observed risk, since an agent following it ends up somewhere you may not have meant. It cannot tell you the destination is a good one. As above, the homepage check does not follow it.
auth_metadata looks at the authentication challenge and metadata your endpoint shows an anonymous caller and asks whether the structure is valid. An invalid structure is Observed risk. It cannot say anything about the strength of your authentication, because we have no credentials and never see the real path run.
What the homepage check does not look at
tls_certificate is on the list, and today it is Not checked on every run we make, homepage checks and scheduled runs alike. The row says why: validating the certificate chain needs a different kind of executor than our fetch-based prober. If you want your certificate checked, check it yourself before you ship.
toolset_unchanged_vs_approved and schema_unchanged_vs_approved are the two baseline checks from above. They only run when an approved baseline exists, and a homepage check never has one.
The page does not combine these rows into one verdict. It counts how many checks were observed and how many were not, and each row keeps its own state and reason.
When one check is not enough
A homepage check shows you one moment. A server changes: a new tool, a reworded description, a refusal that stopped matching its shape. If you want the same checks to run on a schedule, with a report page that keeps the history, the next step is claiming your server. You prove you control the address with a DNS record or a file we ask you to serve, and we never ask for credentials. What claiming changes is laid out on one page.