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.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>'.
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.
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:
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:
http://127.0.0.1:<port>/callback. If the authorization server insists on
the exact registered port, pin it with --callback-port.
Technical details
Technical details
- 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
Authorizationheader for the audit and discarded when the process exits. --oauthrefuses to run alongside--token, anAuthorization--header, orMCPSCORE_TOKEN, so the server never receives two credentials.- RFC 8252 §7.3
requires authorization servers to accept any port on a loopback
redirect.
--callback-portexists for the servers that do not follow it.
Use it in CI
Store the token as a repository secret and expose it asMCPSCORE_TOKEN.
Nothing else changes. With the GitHub Action:
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 statusshows 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-underfor 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.
- 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
--tokento audit behind the gate. - Move the token to
MCPSCORE_TOKENto keep it out of shell history. - Send API keys with
--header. - Use
--oauth, with--client-idif needed, when you have no token. - In CI, set
MCPSCORE_TOKENfrom 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