What goes into a trust score.
Every scan runs every rule that applies to what we could observe. Perimeter rules run on every scan; post-handshake rules require a successful MCP initialize / tools/list; auth-discovery rules apply only when the server requires authentication. Read the methodology for how scores are aggregated and verdicts decided.
Transport security
How the server is reached on the wire. Covers TLS and protocol-level confidentiality of probe traffic.
Server uses HTTPS
The MCP endpoint is reached over HTTPS. Plain HTTP allows trivial credential and prompt leakage on any shared network path.
transport_uses_https·highweight 10PerimeterTLS handshake completes with a trusted certificate
The MCP endpoint completes a TLS handshake whose certificate validates against Node's CA store and matches the requested hostname. Untrusted, expired, or hostname-mismatched certificates are observable here even when the JSON-RPC handshake fails.
tls_handshake_succeeded·highweight 8PerimeterTLS 1.2 or newer is negotiated
The negotiated TLS version is 1.2 or 1.3. TLS 1.0 and 1.1 are deprecated by RFC 8996 and have known cryptographic weaknesses; servers offering them broaden the attack surface for downgrade and padding-oracle attacks.
tls_uses_modern_version·mediumweight 5PerimeterTLS certificate has at least 14 days until expiry
The peer certificate's `notAfter` is at least 14 days in the future. Certificates that expire imminently cause sudden client-side failures and indicate an unmanaged renewal process.
tls_certificate_not_expiring_soon·lowweight 2PerimeterServer advertises HTTP Strict-Transport-Security
The HTTPS endpoint returns a `Strict-Transport-Security` header with a non-trivial `max-age`. HSTS prevents trivial downgrade attacks by instructing user agents to refuse plain HTTP for the host going forward.
transport_advertises_hsts·lowweight 3PerimeterProbe response size is within the safe bound
The probe response stayed under the 1 MB cap enforced by `safeFetch`. Servers that return multi-megabyte JSON-RPC responses can exhaust client memory and indicate something is wrong with their tools/list output.
transport_response_within_size_bound·infoweight 1PerimeterProbe redirects are within the safe bound
The probe completed within the 3-redirect cap enforced by `safeFetch`. Long redirect chains complicate SSRF defense and indicate misconfigured deployments.
transport_redirects_within_bound·infoweight 1PerimeterProbe response content-type was acceptable
The probe transport layer accepted the response's content-type. MCP servers MUST return JSON-RPC over HTTP or SSE; any other content-type makes the response unusable.
transport_content_type_acceptable·infoweight 1PerimeterServer validates the Origin header
The MCP server returns HTTP 403 to a POST whose `Origin` header points at a host other than its own. This is the documented DNS-rebinding mitigation in the MCP 2025-11-25 security best practices: a server that processes cross-origin requests can be coerced via DNS rebinding into accepting tool calls from any page in the user's browser. Real CVEs in this class include CVE-2025-10625 (Neo4j Cypher MCP) and CVE-2026-23744 (MCPJam Inspector).
transport_validates_origin·highweight 8PerimeterProbe completed within a reasonable wall-clock budget
The full `initialize` + `tools/list` + (best-effort) `resources/list` / `prompts/list` walk completed in under 15 seconds. The 10-second per-call timeout still applies; this rule surfaces the aggregate budget across all calls. A probe that consistently takes longer than 15 seconds is either tarpitting (slow-roll responses to drive up scanner cost) or running on a misconfigured deployment.
probe_completed_within_time_bound·infoweight 1Post-handshake
Endpoint hygiene
Properties of the URL itself: whether the host is intended for public use, whether secrets appear in the URL, and other observable URL-level signals.
Endpoint hostname is reachable from the public internet
The MCP endpoint hostname is not `localhost`, a loopback address, a private/link-local IP range, or a *.local / *.internal / *.intranet / *.lan name. Internal-only hostnames in a published MCP URL usually indicate a misconfigured deployment.
endpoint_uses_public_host·mediumweight 4PerimeterNo secret material leaked in observed metadata
No high-precision secret pattern (Bearer header, JWT, OpenAI/Anthropic key, GitHub PAT, AWS key, Stripe live key, PEM private key, etc.) appears in `initialize.instructions`, `serverInfo`, tool descriptions, or input schemas after the standard `redact()` pass.
no_secret_leakage_in_observed_text·mediumweight 5Post-handshakeNo credentials in the endpoint URL
The endpoint URL has no userinfo segment and no token-like keys (`token`, `api_key`, `apikey`, `access_token`, `refresh_token`, `password`, `secret`, `client_secret`) in its query string. Credentials in URLs leak through logs, Referer headers, browser history, and intermediary caches.
endpoint_no_credentials_in_url·highweight 6Perimeter
Tool surface risk
What an agent could do if it trusted every advertised tool. Covers destructive actions, credential disclosure, code execution, filesystem mutation, PII handling, prompt-injection-shaped input fields, and injection-bearing tool descriptions — i.e. the agent-specific threat surface, not just generic verb risk.
Post-handshake. Requires a successful MCP `initialize` / `tools/list`. Skipped on perimeter-only scans where the server refused or failed the MCP handshake.
No description-borne injection vectors
No advertised tool's `description` contains a markdown link with a `javascript:` or `data:` target, an HTML tag that a renderer would execute or fetch (`<script>`, `<img>`, `<iframe>`, `<svg>`, `<object>`, `<embed>`), or an inline URL on a non-HTTPS scheme. Each pattern is a vector for the upstream server to smuggle content past the agent's normal trust boundary.
no_description_injection·mediumweight 5Post-handshakeNo unconstrained prompt-injection vectors
No advertised tool's `inputSchema` exposes a free-form string property named `prompt`, `query`, `command`, `sql`, `instruction`, or `message` without an `enum`, `pattern`, or `maxLength` ≤ 200. These are the field shapes that let an upstream MCP server smuggle agent-instructable text — model prompts, SQL fragments, shell commands — into the call site.
prompt_injection_surface·highweight 8Post-handshakeNo destructive tools in the public surface
No advertised tool matches destructive data verbs (delete, drop, purge, wipe, destroy, truncate). Destructive actions are typically irreversible and should be exposed under explicit, scoped authorization rather than a default `tools/list`.
no_public_destructive_tools·highweight 8Post-handshakeNo credential-access tools in the public surface
No advertised tool references credentials, secrets, API keys, tokens, private keys, or vault material. A public tool that returns or mutates authentication material lets any caller pivot to other systems. The check is a keyword scan over each tool's name, description, and input schema; a benignly-named utility like `validate_api_key_format` will trip it. Hard-fail forces `block` precisely so the operator must consciously override the heuristic — either rename the tool, move it behind authentication, or treat the report as advisory and document the exception.
no_public_credential_access_tools·criticalweight 12Post-handshakehard-failNo code-execution tools in the public surface
No advertised tool offers shell access, arbitrary code evaluation, subprocess spawning, or interpreter execution. Public code execution turns the MCP server into a remote shell for any agent that connects to it. The check is a keyword scan over name, description, and schema; a sandboxed-by-design `eval_expression` math tool will trip it. Hard-fail forces `block` so the operator must explicitly authorise the surface — typically by moving it behind authentication, scoping it to a sandboxed runtime, or renaming if the keyword match is incidental.
no_public_code_execution_tools·criticalweight 14Post-handshakehard-failNo filesystem-write tools in the public surface
No advertised tool mutates the host filesystem (write_file, delete_file, chmod, mkdir, …). Public filesystem mutation can plant payloads, overwrite configuration, or destroy host state. The check is a keyword scan over name, description, and schema; a content-store tool exposing `upload_file` against an isolated bucket will trip it. Hard-fail forces `block` so the operator either fences the tool behind authentication, scopes it to a content store that does not expose raw paths, or documents the exception as an explicit override.
no_public_filesystem_write_tools·criticalweight 10Post-handshakehard-failNo admin-control tools in the public surface
No advertised tool grants administrative control over the system (shutdown, terminate, revoke, suspend, grant_role, sudo, …). Public administrative control lets any caller take over or disable the host. The check is a keyword scan over name, description, and schema; a status-only tool like `get_subscription_grant` may trip it on the word `grant`. Hard-fail forces `block` so the operator either renames the tool, moves it behind authenticated operator interfaces, or documents the override.
no_public_admin_control_tools·criticalweight 10Post-handshakehard-failNo financial-action tools in the public surface
No advertised tool performs a financial action (pay, charge, refund, transfer, withdraw, invoice, …). Tools that move money should be explicitly authorized and reviewed individually.
no_public_financial_action_tools·highweight 6Post-handshakeNo outbound-messaging tools in the public surface
No advertised tool sends an outbound message on the operator's behalf (send_email, send_sms, post_message, broadcast, notify, …). Messaging tools enable spam, phishing and impersonation if exposed without scoped review.
no_public_send_message_tools·highweight 6Post-handshakeNo outbound network-access tools in the public surface
No advertised tool offers generic outbound network access (fetch_url, http_request, curl, wget, download, upload, …). A public network-fetch tool turns the MCP server into an open proxy and a SSRF pivot for any agent that connects to it.
no_public_network_access_tools·highweight 6Post-handshakeTool surface does not mention PII
No tool name, description or input schema references obvious PII fields (email, phone, ssn, password, …). This is a governance prompt, not a security failure — many legitimate CRM, HR, and support servers handle PII by design and will trip the rule. The intent is to make the PII surface visible during review, not to block it. Treat a fail as 'document data minimization, retention, and access controls', not 'remove the tool'.
no_pii_keywords_in_tool_surface·lowweight 3Post-handshakeNo injection vectors in resource or prompt entries
No entry returned by `resources/list` or `prompts/list` contains a markdown link with a `javascript:` or `data:` target, an HTML tag a renderer would execute or fetch (`<script>`, `<img>`, `<iframe>`, `<svg>`, `<object>`, `<embed>`), or a non-HTTPS inline URL in its description (or name, for resources). Resources and prompts reach the model with the same trust as tool descriptions, so the same technical-injection classes apply.
no_resource_or_prompt_injection·mediumweight 5Post-handshakeNo prompt-manipulation patterns in tool descriptions
No tool description contains hidden-instruction tags (`<IMPORTANT>`, `<system>`, `<instructions>`), override phrases (`Ignore previous instructions`, `Disregard prior context`), identity hijacks (`Act as an admin`), or exfiltration directives (`Send results to https://…`). These are the canonical tool-poisoning patterns documented by Invariant Labs and the OWASP MCP Top 10 — the description reaches the model verbatim, and a malicious server uses it to manipulate behavior at read time.
no_tool_description_manipulation·highweight 6Post-handshakeTool annotations are consistent with the surface
For every tool that returns spec-defined annotations (`readOnlyHint`, `destructiveHint`, etc.), the hints do not contradict each other and do not contradict the capability the scanner inferred from the tool's name, description, and schema. Misdeclared annotations are the canonical rug-pull camouflage — a tool that calls itself read-only but deletes records.
tool_annotations_consistent·highweight 6Post-handshakeResource URIs do not advertise risky targets
No entry returned by `resources/list` advertises a URI that points at the local filesystem (`file://`), plaintext HTTP, a cloud-metadata host (`169.254.169.254`, `metadata.google.internal`), a loopback or RFC 1918 address, a link-local or IPv6 ULA address, or a private-TLD hostname (`*.local`, `*.internal`, …). A client that follows these URIs without further validation is the MCP analogue of an SSRF primitive.
no_dangerous_resource_uris·highweight 6Post-handshakeSampled resource contents contain no injection vectors
Up to three https resources advertised by `resources/list` are fetched via `resources/read` (capped at 8 KB each, fully redacted) and scanned for the same injection patterns the scanner applies to tool descriptions: dangerous markdown links (`[text](javascript:…)` / `data:`), executable HTML tags (`<script>`, `<iframe>`, `<svg>`, `<object>`, `<embed>`), and inline non-https URLs. Until tier-3-final the probe scanned only the `resources/list` metadata; this rule closes the gap that a server can publish clean metadata while the resource body itself carries an injection payload.
resource_contents_no_injection·mediumweight 5Post-handshakeServer identity does not match a known security advisory
The `serverInfo.name` and `serverInfo.version` advertised on `initialize` do not match any entry in the AgentReserve in-tree advisory catalog. The catalog (`src/lib/scoring/signals/known-advisories.ts`) is a small, curated, public list of MCP server implementations with documented security incidents — entries cite a public reference (CVE / GHSA / vendor advisory / post-mortem). A match is a hard-fail block: the operator should rotate to a fixed version (or off the implementation) before any further trust is extended.
serverInfo_not_in_known_advisory_list·criticalweight 12Post-handshakehard-failActive probe: read-only tools honor their declared schema
When the operator opts into a deep scan (`deepScan: true` request flag, plus the server-side `AGENTRESERVE_ENABLE_DEEP_SCAN=true` env gate), the scanner attempts up to 3 `tools/call` invocations against tools the classifier marks as READ. Each call uses strictly-typed arguments synthesized from the tool's own `inputSchema` (only enum / const / pattern-constrained string / numeric / boolean primitives — no free-form sensitive fields). The rule passes when every attempted call either succeeded or returned a transport-level error (network, 404, etc.); it fails when at least one tool rejected its own schema-compliant input with a JSON-RPC protocol error. The default scan stays passive — this rule is non-applicable when active probing did not run.
active_probe_read_tools_honor_their_schema·mediumweight 4Post-handshakeDestructive tools positively declare destructiveHint
Every tool the capability classifier flags as destructive (delete/write/financial verb in name, description, or schema) sets `annotations.destructiveHint: true`. Companion rule to `tool_annotations_consistent` — that rule fires only on contradictions (`destructiveHint: false` on a destructive tool). This rule fires on silence: a destructive-classified tool whose annotations omit `destructiveHint` entirely. Silence is exactly the camouflage pattern an operator needs flagged.
destructive_tools_declare_destructive_hint·mediumweight 4Post-handshakeNo injection vectors in initialize.instructions
The `initialize.instructions` string returned by the server contains no markdown link with a `javascript:` or `data:` target, no HTML tag a renderer would execute or fetch (`<script>`, `<img>`, `<iframe>`, `<svg>`, `<object>`, `<embed>`), and no inline URL on a non-HTTPS scheme. The instructions field reaches the model verbatim — same trust boundary as a tool description, same vector classes.
initialize_instructions_no_injection·mediumweight 5Post-handshake
Schema quality
Whether the tool surface is reviewable without invoking it. Tools without input schemas force agents to guess argument shapes; tool names that aren't plain ASCII identifiers confuse logging and allow-listing.
Post-handshake. Requires a successful MCP `initialize` / `tools/list`. Skipped on perimeter-only scans where the server refused or failed the MCP handshake.
All tools declare an input schema
Every tool returned by `tools/list` includes a non-empty JSON Schema for its arguments. Schema-less tools force agents to guess argument shapes and make pre-call validation impossible.
all_tools_have_input_schema·mediumweight 6Post-handshakeTool input schemas have bounded shape
No advertised tool's `inputSchema` carries broad-shape smells: `additionalProperties: true`, untyped `object` properties, or sensitive free-form string fields (`query`, `command`, `path`, `url`, `sql`, `prompt`, `token`, `secret`) without an `enum`, `pattern`, or `format` constraint. Each one is a passive smell that lets a caller send input the schema does not document. Prompt-injection-shaped instructable-text fields specifically are scored under `prompt_injection_surface`; this rule retains the broader structural shape evidence at a lower weight.
no_broad_schema_risk·lowweight 2Post-handshakeTool names are ASCII identifier-shaped
Every advertised tool name matches `/^[a-zA-Z][a-zA-Z0-9_.-]{0,63}$/` — a length-bounded ASCII identifier. Names with shell metacharacters, whitespace, control characters, or unicode look-alikes can confuse logging, agent argument parsers, and reviewers.
tool_name_safety·lowweight 2Post-handshakeTool input schemas are structurally well-formed
Every tool's `inputSchema` parses as a finite, well-formed JSON Schema document — no oversize blobs (>64 KB serialized), no nesting deeper than 32 levels, no `$ref` whose target is unresolvable or non-local, no recursion cycle through `$ref`. The existing `analyzeSchemaRisk` walker tolerates malformed schemas silently (it uses a WeakSet to avoid infinite loops) — so a schema this rule fails would otherwise reach the agent unchecked, defeating pre-call validation and review.
tool_input_schemas_well_formed·mediumweight 4Post-handshakeTool descriptions are within the size bound
Every tool's `description` is at most 4096 UTF-8 bytes. The model reads each description on every call, so an oversized description is both a per-call token-cost amplifier and a context-window-eviction primitive: a hostile server pads ignorable filler into a description so prior conversation gets pushed out before the agent decides what to do. 4 KB covers any benign documentation purpose.
tool_descriptions_within_size_bound·lowweight 2Post-handshake
MCP discovery posture
Whether the server cooperates with the MCP handshake — protocol version negotiation, capability flags, and other discovery signals clients depend on.
Post-handshake. Requires a successful MCP `initialize` / `tools/list`. Skipped on perimeter-only scans where the server refused or failed the MCP handshake.
Server advertises an MCP protocol version
The MCP `initialize` response includes a `protocolVersion`. Servers that omit it make safe client fallback impossible.
initialize_advertises_protocol_version·lowweight 3Post-handshaketools/list returned a tool array
The MCP `tools/list` call returned an array (possibly empty) rather than failing. This is a precondition for the rest of the tool-surface evaluation and a baseline indicator that the server speaks MCP correctly.
mcp_tools_list_succeeded·infoweight 1Post-handshakeProbe walked the full tool surface (pagination not truncated)
The probe followed every `nextCursor` returned by `tools/list` until the server signalled the final page. The MCP spec lets servers paginate the tool list with an opaque `cursor`; an honest server returns the full surface across however many pages it needs. The probe enforces a 5-page / 500-item cap to bound the walk; hitting that cap is a tell that either the server is unusually large (operator should split it) or that a hostile server is hiding tools beyond the visible window.
probe_walked_full_tool_surface·mediumweight 4Post-handshake
Metadata transparency
Whether the server identifies itself and documents its tools — and whether the advertised identity matches the wire identity (cert CN/SAN, hostname). Operators need a stable name, a version, and an internally consistent identity claim to perform any kind of audit.
Post-handshake. Requires a successful MCP `initialize` / `tools/list`. Skipped on perimeter-only scans where the server refused or failed the MCP handshake.
Server identifies itself
The `initialize` response includes a `serverInfo.name` so operators can recognize the implementation and cross-reference it with upstream advisories.
initialize_advertises_server_info·infoweight 2Post-handshakeServer advertises a version string
The `initialize` response includes a `serverInfo.version` so operators can pin known-good builds and cross-reference upstream advisories.
initialize_advertises_server_version·infoweight 1Post-handshakeServer identity claim matches transport identity
`serverInfo.name` is lowercased and split on non-alphanumerics; tokens shorter than three characters and noise words (`mcp`, `server`, `service`, `api`, `the`, `official`) are dropped. The rule passes if any surviving token is a substring of the TLS certificate CN, any SAN, or the URL hostname (also lowercased). A name like "Slack MCP" served from `mcp.attacker.com` with no matching SAN is a passive identity-claim mismatch worth flagging. The check is deliberately generous — case-insensitive substring, any token — so single-word product names hosted on their own domain pass cleanly. False positives are possible on multi-tenant CDN hostnames (e.g. `*.cloudfront.net`) where the cert identity belongs to the platform, not the operator; treat a fail as 'investigate', not 'malicious'.
server_identity_consistency·mediumweight 4Post-handshakeAll tools have descriptions
Every tool includes a non-empty `description`. Missing descriptions make capability review impossible without invoking the tool, which the scanner refuses to do.
all_tools_have_descriptions·lowweight 3Post-handshakeTool surface unchanged since the previous scan
The advertised tool surface (each tool's `name`, `description`, `inputSchema`, and `annotations`) hashes to the same value as the most recent prior scan of this server. A drift in any of those fields is the rug-pull pattern documented by the `postmark-mcp` September 2025 incident: a server changes a tool's behavior or name post-install while the user's existing approval still grants the agent access. The rule is silent on first scans (no prior surface to compare against).
tool_surface_unchanged_since_last_scan·mediumweight 5Post-handshakeNo new tools added since the previous scan
The set of tool names advertised by `tools/list` is a subset of the set advertised by the most recent prior scan of this server. A newly-added tool name is capability creep — the existing operator approval was granted against a smaller surface. Silent on first scans (no prior surface to diff against). Complement to `tool_surface_unchanged_since_last_scan`, which fires once on any drift; this rule surfaces specifically the additive subset with per-tool evidence.
no_new_tools_since_last_scan·mediumweight 4Post-handshakeExisting tool descriptions are unchanged since the previous scan
For every tool whose name appears in both this scan and the most recent prior scan, the `description` field is byte-identical. A description rewrite with no name change is the canonical rug-pull camouflage: the existing approval still routes calls to the same tool name, but the documented behavior — and therefore the model's interpretation of the call — has shifted underneath. Silent on first scans.
tool_descriptions_unchanged_since_last_scan·mediumweight 4Post-handshakeTool descriptions do not collide with other registered servers
No tool description on this server appears on at least one OTHER distinct server in the AgentReserve fingerprint store. Cross-server collisions catch the camouflage pattern where a malicious server clones a benign server's tool description verbatim — and broader campaigns that ship multiple servers all advertising the same suspect description. Companion to `tool_surface_has_no_duplicate_descriptions`, which catches duplicates inside the *same* scan; this rule looks across history.
tool_descriptions_unique_across_servers·mediumweight 4Post-handshakeServer advertises the latest published version of its upstream package
When `serverInfo.name` matches a known-published MCP server in the in-tree allow-list (initially the official `@modelcontextprotocol/*` namespace), the advertised `serverInfo.version` matches the registry's `latest` dist-tag. The lookup is cached for one hour per package, never reaches a registry outside the allow-list, and returns `not-applicable` when the server isn't covered. Tier-final / T2.6 — minimum-viable manifest provenance.
serverInfo_advertises_current_published_version·mediumweight 4Post-handshakeNo two tools share an identical description
Within the advertised `tools/list`, no two distinct tools share a byte-identical (post-trim) non-empty description. Within-server duplicate descriptions catch two failure modes: lazy / autogenerated catalogs where every tool reads the same boilerplate, and the camouflage pattern where a malicious tool inherits a benign sibling's description verbatim so a side-by-side reviewer can't tell them apart.
tool_surface_has_no_duplicate_descriptions·lowweight 2Post-handshake
Exposure minimization
Whether the server keeps its surface small. Large, sprawling tool sets expand the agent's blast radius and are harder to review.
Post-handshake. Requires a successful MCP `initialize` / `tools/list`. Skipped on perimeter-only scans where the server refused or failed the MCP handshake.
Auth discovery posture
When authorization is required, whether the server cooperates with the standards-based discovery chain — RFC 9728 protected resource metadata, RFC 8414 authorization server metadata, validated issuers, and safe grant types.
Auth-required. Only applies when the server signals that authentication is required (HTTP 401/403). Excluded from the score on public servers.
Auth-required server advertises discovery metadata
When the server requires authentication (HTTP 401/403 to an unauthenticated probe), it also advertises either a `WWW-Authenticate` challenge or a discoverable `.well-known/oauth-protected-resource` document. This is what allows a client to start a standards-compliant authorization flow without out-of-band documentation.
auth_discovery_advertised_when_required·mediumweight 5Auth-requiredAuthorization server metadata is parseable
When the protected resource metadata advertises one or more authorization servers, at least one of those issuers publishes a parseable RFC 8414 / OpenID Connect Discovery metadata document. This is what tells a client where to send the user, where to fetch tokens, and where to verify signatures.
auth_authorization_server_metadata_present·lowweight 3Auth-requiredAuthorization server issuers are HTTPS
Every authorization server advertised by the protected resource metadata uses an HTTPS issuer URL with no userinfo, fragment, or query. RFC 8414 and OpenID Connect Discovery both require HTTPS issuers — anything else allows trivial token interception.
auth_issuer_uses_https·highweight 6Auth-requiredAuthorization server advertises a token endpoint
When authorization server metadata is discovered, it advertises a `token_endpoint`. Without one, clients cannot exchange authorization codes (or refresh tokens) for access tokens — the flow is inert.
auth_advertises_token_endpoint·lowweight 2Auth-requiredAuthorization server advertises a JWKS URI
When authorization server metadata is discovered, it advertises a `jwks_uri` so clients can verify token signatures. Required for any authorization server issuing signed tokens.
auth_advertises_jwks_uri·lowweight 2Auth-requiredNo insecure grant types advertised
The advertised authorization server metadata does not advertise the resource owner password credentials grant or the implicit grant. Both are flagged as insecure by RFC 9700 and by OAuth 2.1, and should not appear on a modern authorization server.
auth_no_insecure_grant_types·highweight 6Auth-requiredAuthorization server advertises PKCE with S256
Every parsed authorization server metadata document advertises `S256` in `code_challenge_methods_supported`. RFC 9700 (OAuth 2.0 Security BCP) and OAuth 2.1 require PKCE on every authorization-code exchange, with S256 as the only acceptable challenge method (`plain` is forbidden).
auth_supports_pkce_s256·highweight 6Auth-requiredAuth-discovery metadata URLs are HTTPS and publicly routable
Every URL advertised in protected resource metadata or authorization server metadata — `authorization_endpoint`, `token_endpoint`, `jwks_uri`, `registration_endpoint`, `revocation_endpoint`, `introspection_endpoint`, and the PRM `resource` — uses HTTPS and resolves to a publicly routable hostname (no loopback, RFC 1918, link-local, or private TLD). The discovery chain has no value if it points clients at the cloud metadata service or the loopback interface.
auth_metadata_urls_https_public·highweight 6Auth-requiredAuth-required server advertises RFC 9728 protected-resource metadata
When the server requires authentication (HTTP 401/403 to an unauthenticated probe), its `WWW-Authenticate` challenge carries a `resource_metadata=` parameter pointing at a fetchable URL, *or* a parseable `.well-known/oauth-protected-resource` document is discoverable at the host root or under the endpoint path. RFC 9728 / MCP Authorization §discovery require one or the other so a client can start the authorization flow without out-of-band documentation. Stricter than `auth_discovery_advertised_when_required`: that rule passes on any `WWW-Authenticate` header; this one requires the spec-correct PRM hint or document.
auth_advertises_protected_resource_metadata·mediumweight 4Auth-required