Skip to main content
A production MCP server answers 401 to anyone without a credential. Give mcpscore the same credential your MCP client uses and it audits everything behind the gate.
That is a full audit: every applicable rule could inspect the live session. Rules for a revision or a catalog the server does not have still skip. The token is sent as an Authorization header and is never logged or written to the report.

Step 1: See what a stranger sees

Start without a credential. mcpscore detects the 401 and scores the part of the server that an anonymous client can observe.
This is a partial audit. It covers TLS, the 401 challenge, the RFC 9728 protected-resource metadata, and the authorization server that metadata names. Those are real findings about your auth posture: a missing PKCE advertisement, or no way for a new client to register, shows up here without any credential. The score line says PARTIAL and from 12 of 115 checks on purpose. A bare 30/32 pasted into a chat reads like the score of the whole server. A partial score and a full score are not comparable. A partial audit cannot pass a threshold. Add --fail-under and it fails:

Step 2: Pass a token

Add --token. It is shorthand for --header 'Authorization: Bearer <token>'.
The PARTIAL marker is gone and the check count is back to the full rule set. In the JSON report, authenticated is true and partial is false.

Step 3: Keep the token out of your shell history

A value passed to --token lands in your shell history and in the process list. Set MCPSCORE_TOKEN instead. mcpscore reads it whenever --token is absent.
The output is identical to step 2. This is the form to use in CI, where the token comes from a secret.

Step 4: Send an API key or any other header

Servers that authenticate with an API key take --header, which works the same way as --token and repeats:
Only an Authorization header sets authenticated: true in the report. Other headers are sent on every request, but the report makes no claim that the audit ran behind the gate. An explicit Authorization header wins over --token and MCPSCORE_TOKEN.

Step 5: Sign in through the browser with OAuth

When you have no token to paste, --oauth gets one. It opens your browser on the server’s own sign-in page, runs the authorization code flow with PKCE, and holds the token in memory for this one audit.
--oauth registers mcpscore as a client on the fly (dynamic client registration, RFC 7591). Some authorization servers, GitHub’s among them, do not offer registration. For those, register an OAuth app once and pass its client ID:
The registered app must allow the loopback redirect http://127.0.0.1:<port>/callback. If the authorization server insists on the exact registered port, pin it with --callback-port.
  • The flow follows the MCP authorization spec: mcpscore reads the server’s protected-resource metadata, discovers the authorization server, and runs the authorization code grant with PKCE.
  • The token is never written to disk and never logged. It is placed in the Authorization header for the audit and discarded when the process exits.
  • --oauth refuses to run alongside --token, an Authorization --header, or MCPSCORE_TOKEN, so the server never receives two credentials.
  • RFC 8252 §7.3 requires authorization servers to accept any port on a loopback redirect. --callback-port exists for the servers that do not follow it.

Use it in CI

Store the token as a repository secret and expose it as MCPSCORE_TOKEN. Nothing else changes. With the GitHub Action:
If the secret is missing or expired, the run becomes a partial audit and min-score fails the job, as --fail-under does on the command line:

Options

When it fails

Partial audit (Server rejected the provided credentials (HTTP 401) ...)
  • Cause: the server refused the token you sent.
  • Fix: check the token is current and has access to this server. For GitHub, gh auth status shows the account and scopes.
Partial audit (... the session could not be established (HTTP 400), so the supplied credentials were never exercised ...)
  • Cause: the connection failed before the credential was used. The server answered the session request with an error that is not an auth refusal.
  • Fix: check the token’s format against what the server expects. An invalid GitHub token produces this exact message.
PARTIAL score although you passed --header
  • Cause: the header was not Authorization, so the server still answered 401 to mcpscore’s session.
  • Fix: send the credential in the header the server checks. If the server expects a bearer token, use --token.
Gate failed — --fail-under 80: this was a partial audit (exit 3)
  • Cause: no credential reached the server, so the score cannot demonstrate the threshold.
  • Fix: pass a credential, or drop --fail-under for that run.
OAuth: OAuth flow failed: ... try --client-id (exit 1)
  • Cause: the authorization server does not support dynamic client registration.
  • Fix: register an OAuth app yourself and pass --client-id.
The browser shows a redirect URI mismatch
  • Cause: the authorization server requires the exact registered redirect, port included.
  • Fix: pin the registered port with --callback-port.

Recap

  • Run without a credential to see the partial audit of your auth posture.
  • Pass --token to audit behind the gate.
  • Move the token to MCPSCORE_TOKEN to keep it out of shell history.
  • Send API keys with --header.
  • Use --oauth, with --client-id if needed, when you have no token.
  • In CI, set MCPSCORE_TOKEN from a secret and gate with --fail-under.

What’s next

GitHub Action

Gate pull requests on the score, gated servers included

Rules reference

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