Skip to main content
Your production server answers 401 to strangers, and so it should. mcpscore audits it the way any MCP client connects: present a credential and audit behind the gate, or present nothing and score what an anonymous client can see.
Credential values are never logged and never written to the report. The report records one boolean, authenticated, and nothing about the secret itself.

Bearer tokens

--token is shorthand for --header 'Authorization: Bearer <token>'. When the flag is absent, mcpscore reads MCPSCORE_TOKEN from the environment. That is the form for CI and for any shell where history is a concern:

API keys and other headers

--header sends any header and repeats:
An explicit Authorization header wins over --token and MCPSCORE_TOKEN. Only an Authorization credential sets authenticated: true in the report. Other headers are sent but make no claim that the audit ran behind the gate.

Browser OAuth

--oauth runs the authorization code flow with PKCE, as the MCP authorization spec describes, and holds the token in memory only. It needs the authorization server to support dynamic client registration (RFC 7591). When it doesn’t, register an app once and pass its ID:
The registered app must allow a loopback redirect, http://127.0.0.1:<port>/callback. RFC 8252 requires authorization servers to accept any port on loopback, but if yours demands the exact registered URI, pin the port you registered with --callback-port.

What a partial audit scores

Without a credential, an auth-gated server is still worth a look. The observable surface is real quality:
  • Auth posture: the 401 carries a proper WWW-Authenticate challenge, RFC 9728 protected-resource metadata is served, and the authorization servers it names are HTTPS-only with PKCE enforced.
  • TLS and transport behavior.
The report sets partial: true with a partial_reason, and the score line says PARTIAL with the check count on purpose. 24/27 on its own pastes into a chat as a near-perfect result. from 10 of 78 checks is what makes it honest.
A partial score covers fewer checks than a full audit, so the two are not comparable. --fail-under always fails on a partial audit for that reason. Pass a credential before gating CI on a threshold.

In CI

Store the token as a secret and export it as MCPSCORE_TOKEN. Nothing else changes. With the GitHub Action:

When it fails

PARTIAL score although you passed a header
  • Cause: the header was not Authorization, or the server rejected the credential and answered 401 anyway.
  • Fix: check the header name and that the token is current. Only an Authorization credential is treated as authenticating.
Gate failed — --fail-under 80: this was a partial audit (exit 3)
  • Cause: the audit ran without a credential, and a partial score cannot demonstrate a threshold.
  • Fix: pass a credential, or drop --fail-under for that run.
--oauth stops with a registration error (exit 1)
  • Cause: the authorization server does not offer dynamic client registration.
  • Fix: register an OAuth app yourself and pass --client-id.
The browser shows a redirect URI mismatch
  • Cause: the authorization server insists on the exact registered URI, including the port.
  • Fix: pin the port you registered with --callback-port.

What’s next

GitHub Action

Gate pull requests on quality, gated servers included

Rules reference

The auth-posture rules, with their spec and RFC citations