Menu

Methodology

The four assertion states

VERIFIED
We independently observed the check pass, on this run, against this exact endpoint.
FAILED
We independently observed the check fail. We show this plainly — a failed check is not hidden or reworded.
OBSERVED_RISK
We observed a condition worth flagging that isn't a pass/fail result by itself — for example a tool description containing content aimed at an AI reader rather than a human one. It is a signal, not a verdict.
UNVERIFIED
We could not reach a verdict for this check on this run, and we say why (see the reason attached to each UNVERIFIED assertion). UNVERIFIED is never treated as a pass.

Execution status (completed / errored / skipped / blocked) is tracked separately from the four states above and is never collapsed into a pass/fail count.

What two numbers mean

Reachable on observed days: the share of UTC days in the last 90 on which the endpoint answered, out of the days on which we know either way. A day counts as reachable when every reachability check that day got an answer from the endpoint, and as unanswered when any check that day got none — the host name did not resolve, the connection failed (counted since 2026-09-27), or no complete response arrived within the probe's time budget — or, since 2026-09-23, the handshake was answered with a 5xx. A day on which the only checks that could not conclude were our own doing — we were rate-limited, or our own policy declined to connect — is left out, counted neither way. The number of days counted is shown next to the percentage.

Protocol revision: the revision recorded from our handshake. Our probe first sends a revision 2026-07-28 server/discover request; if the server answers it with an HTTP 2xx and a discovery result, we record the first revision in the server's list of supported revisions. If the server answers with an HTTP 2xx or 4xx status other than 429, and with neither a discovery result nor a protocol error defined by the 2026-07-28 revision, the probe falls back to the legacy initialize handshake, offers revision 2025-11-25, the newest legacy revision it knows, and records the revision the server answered with. Either way, it is not a claim about the newest revision the server supports. Each run also records the revision our probe offered; a change in the recorded revision counts as a change on the server only between runs that offered the same revision, or when the server switched between the legacy initialize handshake and the newer server/discover handshake. Runs recorded before we stored the offered revision are treated as having offered 2025-06-18, unless the stored result shows our probe used the newer discovery handshake.

"Observed, unsigned"

Some target pages show raw observations without the four-state verdicts above and without a cryptographic signature. This happens when the run's attestation was disqualified for a stated reason (shown on the page), or when the run carries no attestation at all. It has nothing to do with whether the target is claimed: signing works the same whether or not anyone has claimed the target. These pages report what we saw, honestly — the checks are never shown with a pass/fail conclusion, a score, or a status badge, because no signature is attached to the observation.

"Auth-gated" signatures

When a server answers our credential-free request with a 401 and an authentication challenge, the run can still be signed, but the signature vouches only for what is observable outside that wall: reachability, redirect policy, transport and authentication metadata. The payload also records the challenge itself, reduced to names, at most one URL and at most one digest. Every other check in that signed payload is UNVERIFIED with its reason, and the report page shows those checks without a verdict. The probe never sends credentials. A 401 without a usable challenge, a failed connection or a 5xx does not produce this kind of signature.

A note on evidence format

Signed attestations issued before this page's schema note carry free-text reason and detail strings directly inside the signed payload. Attestations signed after it carry a reason key and structured parameters instead — this website renders that key into the reader's language at request time. Both forms are equally valid, verifiable evidence; only the encoding of explanatory text changed.

What identifies the code that ran

Every signed attestation names the check-suite code that produced it. From payload version 0.3 onward it carries both a suite_commit — a repository commit — and a suite_digest taken over the git tree listing of packages/checks at that commit. The exact recipe is deliberately not restated here: the obvious paraphrase of it names a different value than the real one, so the authoritative statement is the single command each evidence page prints. Attestations signed before 0.3 carry only a suite_digest, under an older definition that pointed at an npm package which was never published; those values are shown unchanged and cannot be recomputed. An attestation whose suite_commit is in github.com/mcpcheckup/checks can be recomputed by anyone with a clone of that repository; attestations that carry a suite_commit but were signed before the check suite went public name a commit in a repository that isn't public, so their digest can't be checked from outside the project. If an envelope declares a payload version this site has no schema for, the page says exactly that instead of guessing. None of this says anything about what the checks concluded — it only identifies which code reached those conclusions.

How the tool and schema fingerprints are computed

A report's toolset_fingerprint and schema_fingerprint are SHA-256 digests of a projection of the tool list the probe read from the server's tools/list answer, recorded under toolset_projection_version "v1". Each is written as sha256: followed by 64 lowercase hexadecimal characters. Only the fields named below are read from each tool; a new field next to them does not change either fingerprint.

toolset_fingerprint: take the name of every tool and nothing else. Sort the names as plain strings by UTF-16 code unit, before any normalization, and keep duplicates. Write the sorted array in the canonical form described below and take the SHA-256 of its UTF-8 bytes.

schema_fingerprint: take each tool's name and its inputSchema exactly as the server sent it, and nothing else. Sort these pairs by name in the same way; tools with the same name are ordered by the canonical text of their inputSchema, compared the same way. Write the sorted array of {"inputSchema": …, "name": …} objects in the canonical form and take the SHA-256 of its UTF-8 bytes. If any tool has no inputSchema, schema_fingerprint cannot be computed and is recorded as FAILED.

The canonical form, named nfc-jcs/v1, is RFC 8785 (JSON Canonicalization Scheme) applied after one extra step: every string and every object key is first converted to Unicode Normalization Form C, and an object in which two keys become identical that way, or a string with an unpaired surrogate, is refused. Object keys are then sorted by UTF-16 code unit at every level, array order is kept, no whitespace is written, strings use the shortest JSON escapes, and numbers are the IEEE 754 doubles that JSON.parse reads, written the way ECMAScript writes them. It is not called RFC 8785 because that standard forbids the normalization step.

Which tools are covered: since suite 0.15.0 the probe follows nextCursor and reads every page of tools/list, up to 10 pages and 5,000 tools, and both fingerprints are computed over all of them together; the order of pages and tools does not matter. If the list cannot be read to its end, because a limit is reached or a later page runs out of time, goes over the size limit or is not a readable tools/list answer, no fingerprint is computed for that run: both fingerprint checks are UNVERIFIED with the reason tools_list_incomplete, no tool list is stored for the run, and the run is not signed. If a later request fails in any other way, the run ends with that failure's own reason, or with the general reason for an aborted run, and the result is the same: no fingerprint, no stored tool list, no signature. In both cases the schema_fingerprint that get_mcp_report returns for that run is null, and its toolset_fingerprint is the latest stored tool list's, if there is one, with snapshot_run_id and snapshot_observed_at saying which run and when. Runs of suite 0.14.0 and earlier read only the first page, and a fingerprint they recorded covers that page alone.

To recompute a fingerprint yourself, fetch the server's tools/list answer (every page), then run recompute-fingerprints.mjs from packages/canonicalizer/scripts in github.com/mcpcheckup/checks on it. The script follows the steps above in plain Node with no dependencies and prints both fingerprints; PROJECTION-v1.md in the same package gives the exact rules and a curl command that fetches this site's own tool list. A fingerprint describes the tool list as it was when the run read it. It changes only when a field its projection covers changes: for toolset_fingerprint the tool names (a tool added, removed or renamed), for schema_fingerprint a tool name or an inputSchema; a changed description or any other field changes neither.

What this doesn't do

We check what we can independently observe from the outside, on a schedule, against a live endpoint. We do not audit source code, we do not guarantee an endpoint's behavior between our runs, and a clean report is not a guarantee — it's a record of what was checked and what was seen at the time it was checked. See each report page's own coverage summary for exactly what ran and what didn't, on that run.

The checks

Reachability & Performance

Is this endpoint alive right now, and how fast does it respond. **Observation only, no verdict** — we don't define how many milliseconds counts as slow; that depends on your users.

reachability

endpoint responds within budget

What this checks:
Whether the endpoint returns a response within the given budget, and the latency distribution.
How it's judged:
Recorded as VERIFIED when our handshake exchange finishes with an HTTP response from the endpoint, whatever its status. If the probe stops before the exchange finishes — for example because the endpoint asks us to back off (429, or 503 with Retry-After), a response is over the size limit, the connection fails, or one of the probe's budgets runs out — this check is UNVERIFIED, with that reason, even if the endpoint had already answered an earlier request. The whole probe, handshake included, runs under hard caps: 8 requests and 10s in total, and 2MB per response. Reading the further pages of a long tool list adds at most 9 requests to the 8; they share the same 10s and the 2MB limit per response.
What it can't tell you:
A timeout doesn't mean you're down — it could be our network. That's why exceeding budget is UNVERIFIED, not FAILED. The directory's online rate is stricter: a day on which any probe got no complete answer within its budget counts as unanswered.

latency_profile

p50 / p95 response time of our requests, measured from our probe node

What this checks:
Within one run: the median (p50) and 95th-percentile (p95) time from sending a request to this endpoint to receiving its response headers, and how many requests were timed (n).
How it's judged:
**No verdict is made.** VERIFIED means the time was measured, not that it was fast: we set no threshold and do not rank servers by speed. Every request to the endpoint in the run that got an HTTP response is timed, whatever its status; a request that got none is not. The timer runs from just before the request is sent until the response headers arrive, so our own DNS checks and the download of the body are not included. p50 and p95 are nearest-rank values over the n timed requests; with fewer than 20 requests, p95 is the slowest of them. A run that stops at an authentication challenge during the handshake records no time. With no timed request, this check is UNVERIFIED.
What it can't tell you:
This is what our probe node observed, not what your users see. It includes the network between our node and your endpoint, setting up the connection, and any wait inside our own Worker.

Protocol Contract

Whether your server actually speaks the MCP protocol revision it claims to. This is the core of the product: most "the agent suddenly stopped working" failures live at this layer, not the security layer.

transport_type

the endpoint answered our handshake request over the transport used

What this checks:
The transport this probe used to reach the server, and whether the endpoint answered over it.
How it's judged:
Recorded as VERIFIED when our handshake exchange over that transport finishes with an HTTP response from the endpoint, whether or not the handshake itself succeeds — that is discovery_handshake's verdict. If the probe stops before the exchange finishes — for example because the endpoint asks us to back off, a response is over the probe's size limit, or one of the probe's budgets runs out — this check is not recorded as VERIFIED, and the reason is recorded with it. It is not compared against any transport the server declares.
What it can't tell you:
This cannot tell you whether the transport implementation has bugs.

protocol_revision

protocol revision is identifiable

What this checks:
The MCP protocol revision the server actually follows.
How it's judged:
Passes if any version within the recognized matrix is identified.
What it can't tell you:
This cannot tell you whether that version is fully implemented. When the handshake is credential-gated (see discovery_handshake), this check cascades to UNVERIFIED with reason credential_required: the handshake never completed, so no declared version is compared against the support matrix.

discovery_handshake

modern server/discover or legacy initialize behavior matches the revision's spec

What this checks:
Whether the modern server/discover or legacy initialize handshake behaves according to the identified revision.
How it's judged:
Compared against the matching spec for whichever revision was identified.
What it can't tell you:
This cannot tell you whether the content returned by discovery is accurate. If the response that settles the handshake is a 401 carrying a structurally valid WWW-Authenticate challenge, this check records UNVERIFIED with reason credential_required rather than FAILED — we never send credentials, so we cannot see past a deliberate access-control gate. A 401 without a valid challenge, a 403, or a recognized MCP protocol error code on the first request is not treated as a credential gate.

tools_list

tools/list is available and returns a legal structure

What this checks:
Whether tools/list is available and returns a structurally valid tool list.
How it's judged:
An invalid structure is FAILED, since a downstream agent would fail to parse it directly. When the server splits its list into pages, we follow nextCursor and read every page, up to 10 pages or 5,000 tools in one run. Pages after the first do not count toward the 8-request limit; the 10s time limit and the 2MB limit per response still apply. If the list is longer than that, or a later page runs out of the time limit, goes over the 2MB limit (in its request or its response) or is not a readable tool list, we stop reading pages there: this check is still judged on the first page, and nothing that depends on the whole list is recorded as VERIFIED (see each check). Any other failure on a later page (for example the server asking us to back off, or a failed connection) ends the run, as it would on any other request.
What it can't tell you:
This cannot tell you the tools actually work — we never call business tools. If the handshake completed but tools/list answers 401 with a structurally valid WWW-Authenticate challenge, this check records UNVERIFIED with reason credential_required rather than FAILED; if the handshake itself was credential-gated, this check cascades to the same result.

error_taxonomy

error responses match the protocol's defined shape

What this checks:
Triggers a safe error scenario and checks whether the error response's shape matches the protocol.
How it's judged:
A deviation is recorded as OBSERVED_RISK — a non-conformant error shape prevents an agent from retrying correctly. If we could not read the whole tool list (see tools_list), we do not send this call, since the name we would call could belong to a tool we did not see; this check is then UNVERIFIED with reason probe_tool_name_unverifiable, or probe_tool_name_collision when a page we did read lists that name.
What it can't tell you:
This cannot tell you every error path is conformant — we only probe the handful of non-destructive ones.

Security Hygiene

Publicly observable baseline security properties. **This is not a security scan** — we don't test for prompt injection, data exfiltration, or privilege abuse, because those require calling business tools, which we refuse to do by default.

tls_certificate

certificate is valid, unexpired, SAN matches, chain is complete

What this checks:
Certificate validity period, whether the SAN matches, and whether the certificate chain is complete.
How it's judged:
Expired / mismatched / broken chain are all FAILED. A deterministic judgment.
What it can't tell you:
This is the most basic transport-layer hygiene — **it is not the same thing as "this server is secure."**

redirect_policy

redirects do not cross to an unexpected host

What this checks:
Whether a redirect crosses to an unexpected host.
How it's judged:
A cross-host redirect is recorded as OBSERVED_RISK. Only the GET for authorization metadata follows redirects, for at most 3 hops; a protocol POST that gets a redirect is recorded, not followed. A cross-host redirect is recorded even when the run stops afterwards, for example at the hop limit, at another host we do not connect to, or because the server asked us to back off.
What it can't tell you:
This cannot tell you the destination host is trustworthy — only whether a redirect happened.

auth_metadata

publicly observable auth metadata / challenge structure is legal

What this checks:
Whether the publicly observable authentication metadata / challenge structure, seen without credentials, is structurally valid.
How it's judged:
An invalid structure is OBSERVED_RISK; when it can't be observed at all, it's UNVERIFIED.
What it can't tell you:
**This cannot tell you the strength of authentication.** We have no credentials, so we never see the real authentication path execute.

tool_description_hygiene

tool name / description contains no agent-targeted injection patterns or hidden characters

What this checks:
Whether tool name / description contains hidden characters or agent-targeted injection patterns.
How it's judged:
Zero-width characters, Unicode Tag block, bidi override characters, imperative phrasing directed at an assistant, spending or sending identity data without per-action confirmation, continuing without another user prompt — any hit is recorded as OBSERVED_RISK. A description over 2,048 characters is still listed as anomalous length, but length alone no longer counts as a risk. A ZWNJ or ZWJ between two characters of one script that uses it in ordinary spelling (Arabic, Devanagari and the other scripts we list), combining marks skipped, is not a hit; nor is a ZWJ directly between two pictographic (emoji) characters, though a ZWJ after a skin-tone modifier still is. "Do not tell the user" (or inform, mention, reveal, show; "the" optional, "human" for "user") counts when followed by "about", "that you", "what you", "how you", "anything", "any of", one of . , ; : ! ) or the end of the text; it also counts when followed by 's or ’s, a hyphen with a space or tab before it, an en or em dash, a line break, or one of that, what, why, where, how, this or any, unless the text after it (after a line break, the next line) has until, unless, yet, before, after, otherwise, once, while, if or when before the next . , ; ! ? or line break or ,。!?;. "System prompt" or "developer prompt" counts after "your" or "your new"; after one of reveal, print, output, repeat, disclose, leak, ignore, override, disregard, forget, show, return, display, replace, dump, echo or act as, optionally followed in turn by out, by the, any, all or your, by previous, prior, above, earlier, original or contents of, and by the or your; or at the start of a line (after only spaces, tabs, #, *, >, [ or -) when followed, after optional whitespace, by a colon or by "override". "Never", "do not" or "don't" (either apostrophe) followed by say, mention, tell, disclose, reveal or state also counts when missing, not verified, unverified, failed or absent comes after it in the same clause, unless because, when, if, unless, since, as or so comes between them. Spending or sending identity data without per-action confirmation is one of these phrases: "pay" followed by "by default" in the same sentence (ended by . ! ? a line break or 。!?) within 40 characters; "no approval" or "no confirmation" followed by needed or required (optionally after "is"); "need not ask"; "zero questions"; "without asking", unless never, not or a word ending in n't (ASCII apostrophe) comes within 30 characters before it in the same sentence (ended by . ! ? a line break or 。!?), or for or us follows it; "does not" or "doesn't" "require" per-change, per-action, per-call, per-purchase or per-payment (or "per" and a space) "approval"; "the" grant, allowance or delegation "is" the or your permission, approval or consent; accept or approve, then all, every or any, then up to two words, then prompt, confirmation, dialog or request (or their plurals); continues, repeats or recurs "automatically"; the, your or this "agent loop"; "git config", then user.email (options such as --get may come between); "never ask" (optionally followed by "the user") "before" charging, paying, buying, purchasing, spending, billing, renewing or subscribing; 无需确认 or 无需另行确认; 视为同意 or 视为用户同意; sell, sells, selling, sellable, resell, resells, reselling or resellable, then "entrusted" or "lent", optionally "to", and "you", in the same sentence within 80 characters; raise, increase, disable or "turn off", optionally the or your, then spendControls (optionally followed by a dot and a name), maxAmountPerPayment, "spend" or "spending" and limit, limits, cap or caps, or "payment" and cap or caps; "set", optionally the or your, then one of those, optionally ":", "=" or "to", then false or 0 (not followed by a dot, letter, digit or underscore); pays, charges or spends, then within 40 characters in the same sentence "from" your owner's, the user's, your human's or the human's (either apostrophe), then credit, credits, balance, wallet or funds, unless that sentence or the next has confirm, confirms, confirmed, confirming, confirmation, confirmations, approve, approves, approved, approving, approval or approvals, or ask, asks, asked or asking followed within 40 characters in the same sentence by "permission" (for this form a sentence ends at . ! or ? followed by whitespace or the end of the text, at a line break, or at 。!?, and the two sentences reach at most 400 characters before the start of the phrase and 800 after it). It counts only when a money or identity word is within 200 characters of it (pay, pays, paid, paying, payer, payment, payments, charge, charges, charged, charging, credit, credits, wallet, wallets, USDC, invoice, invoices, purchase, purchases, purchased, purchasing, buy, buys, buying, subscribe, subscribes, subscribed, subscribing, subscription, subscriptions, renew, renews, renewed, renewal, renewals, bill, bills, billed, billing, cost, costs, fee, fees, refund, refunds, refunded, refundable, spend, spends, spending, money, identity, email, emails, e-mail, e-mails, a $ amount, or 扣费, 续费, 费用, 支付, 付款, 购买, 订阅, 钱包, 邮箱, 身份), and no cap, caps, capped, limit, limits, ceiling, ceilings, max, maximum, policy, policies, mandate, mandates, allowlist, allowlisted, whitelist, whitelisted (each also with a hyphen: allow-list, white-list), threshold, thresholds, quota or quotas (or 上限, 额度, 限额, 策略, 白名单) is in the 120 characters before it (this does not apply to raising or switching off a limit). A money or identity word does not count when its own clause says it costs nothing or is not sent: "cost" or "costs" followed by "nothing" (up to two words between), "free", "no charge", "no cost" or "no fee" (or their plurals), does not, doesn't, do not, don't, never, will not or won't (ASCII apostrophe) followed by send, collect, store or share (or sends, collects, stores, shares), or 免费 or 不收费; "free" right after "feel" does not count. A clause ends at . ! or ? followed by whitespace or the end of the text, at , ; or a line break, or at ,。!?;, and reaches at most 200 characters either side of the first character of the word; the same words in another clause change nothing. A phrase does not count when its own clause has "no purchase" (or "no purchases"), "read-only" (or "read only"), "does not" or "doesn't" followed by buy, purchase, register or charge, "what it costs" or "how much"; the same words in a money or identity word's own clause stop that word from counting, and in any other clause they change nothing. Continuing without another user prompt is "without", optionally "waiting for", then another, a further, any further, further, a new, a second, an additional or additional, then user or human, then prompt, message, request, turn or reply (or their plurals). We read every page of the tool list. If we could not read the whole list, a hit in the tools we did read is still OBSERVED_RISK, but no hit is UNVERIFIED rather than VERIFIED: the tools we did not see were not checked.
What it can't tell you:
**This cannot infer intent.** These patterns can also occur in legitimate tools (e.g. a tool that specifically handles Unicode). We only report the signal.

Contract Drift & Supply Chain

Whether the toolset or schema changed without your knowledge. A change is only judged "failed" **after you've approved a baseline**; before that, it's just a recorded fact.

toolset_fingerprint

the toolset's canonical fingerprint is computable and recorded

What this checks:
Canonicalizes the toolset via JCS and computes a SHA-256 fingerprint, then archives it.
How it's judged:
**Computing successfully is VERIFIED** — this only judges "can it be computed," not "did it change." It is computed over every page of the tool list. If we could not read the whole list (see tools_list), no fingerprint is computed and this check is UNVERIFIED.
What it can't tell you:
The fingerprint by itself says nothing about good or bad — it's only a fact a third party can independently recompute.

schema_fingerprint

each tool's inputSchema canonical fingerprint is computable and recorded

What this checks:
Canonicalizes each tool's inputSchema and computes a fingerprint, then archives it.
How it's judged:
Same as above: only judges computability. It is computed over every page of the tool list, and is UNVERIFIED when we could not read the whole list.
What it can't tell you:
Same as above.

toolset_unchanged_vs_approved

toolset is unchanged relative to the approved release

What this checks:
Whether the current toolset matches the release you approved.
How it's judged:
Only runs when an approved baseline exists; adding or removing a tool is FAILED. If we could not read the whole tool list, there is nothing to compare and this check is UNVERIFIED.
What it can't tell you:
Without a baseline it is always UNVERIFIED — nobody has told us what the correct shape looks like.

schema_unchanged_vs_approved

canonical schema is unchanged relative to the approved release

What this checks:
Whether each tool's canonical schema matches the approved baseline.
How it's judged:
Only runs when a baseline exists. **This is the most direct check against a rug pull.** If we could not read the whole tool list, it is UNVERIFIED.
What it can't tell you:
Same as above: on an unclaimed server, this can only ever be UNVERIFIED.

Reading a report page's availability record

A day counts as Answered only when every check that day got an answer. If any check that day got no answer, or its handshake got a 5xx, the day counts as No answer. A day with a check the probe could not tell, and none unanswered, is not counted.

On a report page's availability record, a small dot marks a day before our first check of that server. Those days are not counted, and nothing is filled in or guessed for them.

Verifying a signed report page

On a report page with a full signature, every result is signed — that server's evidence page has the full signed payload. Independent verification uses our published public keys, listed at /keys.

How often we check

Unclaimed servers are probed every 12 hours. Once claimed, a server is probed every 8 hours on the Free plan and every 15 minutes on Indie. Each target's report page shows which cycle it is currently on.

"Observation not updated for X h"

A page (the report page, a /servers card, or the home page's latest list) is marked "Observation not updated for X h" once the observation it shows is older than that server's own check cycle (12 hours for servers not yet claimed by their owner, 8 hours on the Free plan, 15 minutes on Indie) plus a fixed margin of 4 h 40 min. The margin is sized for the worst-case wait in our probing queue at up to 6,250 servers, plus our own platform's per-run and in-flight limits. A badge shows the same fact as "observation X h old". An MCP tool response carries the same fact as machine-readable fields, defined in each tool's description — never this page's wording. The margin is the same for every plan; only the cycle differs, and paying only ever changes the cycle, never this rule. X is floored to whole hours; because of caching, a page can lag up to about 6 minutes behind the underlying data, and a badge cached by another site can lag longer. This describes only our own observation cadence — see /status for whether our prober itself is running (its signal changes after 20 minutes with no run at all, or once a probe job has stayed unfinished for 8 minutes). If every page shows "Observation not updated for X h", our observations have stopped; it does not mean the servers changed.

Unchanged days

On a report page, "Tool list last changed … (unchanged for at least N days)" gives the UTC date of the newest stored tool list that differs from the one before it. N is a lower bound: it counts the UTC calendar days after that date on which at least one of our runs listed the server's tools successfully. Days with no successful listing, and days for which we no longer hold per-run records, are not counted.

Which endpoint we probe

When we add a server from the Registry, we take a Streamable HTTP remote from its latest active version: the first listed URL in alphabetical order whose host no other server we track already uses. If that version lists no streamable-http remote, we do not add the server, because our probe speaks Streamable HTTP only. Every 6 hours we compare servers we added from the Registry with their latest active Registry version. While the URL we probe is still listed there, we keep it. If it is no longer listed, we may move the probe to the first listed Streamable HTTP URL in alphabetical order whose host no other server we track already uses, after one credential-free test round shows that it answers. A server its owner added through a claim is probed at the endpoint the owner gave. Report pages and MCP tool responses name the endpoint the shown run probed, when it is known, and say whether that URL comes from the signed payload or from our own unsigned record.

Search ordering

Search results are ordered by lexical text-match relevance — how well each word of your query matches a server's observed tool names and descriptions — with ties broken by most recent observation time. Paid tier never influences ordering. Each query word matches either its exact form or its English-stemmed form; a word prefixed with "-" excludes servers whose current toolset matches it in either form. Quoted-phrase adjacency and OR operators are not supported.

On /servers, "Relevance" first lists servers whose provider or name equals your query, then servers whose provider, name or address contains every word of it, then servers matched only by their tool names and descriptions as described above; ties are broken by most recent observation time. Matches by name or address also include servers that require credentials and servers whose latest check did not obtain a tool list. Paid tier never influences this order either.

On /servers, servers whose provider, name or address is only spelled similarly to your query are listed after all other results, whichever sort you choose, under their own heading, which is shown on every page where they appear.

Corrections

Before suite 0.12.0, three checks could be recorded as passed without observing what they name. Since our first recorded run (2026-08-20): latency_profile was recorded as passed on every run that got an answer to its handshake request, though no latency was measured; auth_metadata was recorded as passed whenever the answer to the probe's tool call was not a 401, so there was no authentication challenge to check; error_taxonomy was recorded as passed whenever that answer was a 401 or 403, so no protocol error had been returned to classify. Suite 0.12.0 (corrected 2026-10-17) records these cases as unverified, with a reason. Earlier signed results are unchanged and still verify as signed; on report pages, latency_profile from earlier suites is shown as not recorded. Since suite 0.16.0, latency_profile is measured: response time to the headers, from our probe node, with no verdict. Since suite 0.17.0, tool_description_hygiene no longer counts some zero-width joiners and non-joiners used in ordinary spelling or in emoji, and it counts "do not tell the user" and mentions of a system or developer prompt only in specific forms; the check's page lists the exact conditions. Earlier signed results are unchanged. Since suite 0.18.0, tool_description_hygiene also flags some phrasings of spending or sending identity data without per-action confirmation, and counts again some forms of "do not tell the user" and of system or developer prompt mentions that suite 0.17.0 had stopped counting. Since suite 0.19.0, tool_description_hygiene no longer counts length alone as a risk (a description over 2,048 characters is still listed), and it also flags some phrasings that tell the assistant to keep working without another user prompt, some phrasings that tell it not to say a check is missing or failed, and some further phrasings of spending.