.github/workflows/mcpscore.yml
rule_id, so a fix is one lookup in the
rules reference away.
Set it up step by step
The workflow above has three parts that matter. Each step below adds one.Step 1: Audit on every pull request
.github/workflows/mcpscore.yml
target is the server to audit: a URL, or a .py or .js file in the
repository. The action installs mcpscore, runs the audit, writes
mcpscore-report.json, and publishes the report to the job summary. With no
threshold the job passes whatever the score, which is a safe way to see your
number first.
Step 2: Give it permission to comment
.github/workflows/mcpscore.yml
Step 3: Fail below a threshold
.github/workflows/mcpscore.yml
min-score is a percentage from 0 to 100. A server at 84% passes a threshold
of 80. Set it to 90 and the same server fails the job:
Variations
Each variation below changes the Step 3 workflow in one place.A server that lives in the repo
Check out the code and pointtarget at the entry file. mcpscore starts it
with python or node from the runner, so that runtime and the server’s
dependencies must be installed first. This job runs the server code from the
pull request, so give it no write scope: checkout needs contents: read and
nothing else, and the report is in the job summary. Keep the commenting job,
if you want one, on a remote target that the pull request cannot change.
An auth-gated server
Store the credential as an Actions secret and export it asMCPSCORE_TOKEN.
The CLI sends it as a bearer token, and the value never reaches the log or
the report.
args. The action splits args on
whitespace, so write the header as one token with no space after the colon:
Without a credential an auth-gated server gets a partial audit, and
min-score is judged on that partial score, which covers only the auth, TLS,
and transport surface. Pass a credential before trusting the gate. See
authenticated servers.Turn rules off for this repository
Amcpscore.toml in the checkout is picked up the same way the CLI picks it
up, so the action needs an actions/checkout step before it. Rules you have
turned off or re-ranked shape the score the action gates on and comments
with. The badge never changes. See configure rules.
mcpscore.toml
[gate] failure fails the job with the rule named:
Also require readiness for MCP 2026-07-28
readiness.max_score. A legacy server still
has its gateway checks assessed, so it is gated on the few points those
carry, like the 13 above. Check the readiness line of a run before choosing
the threshold.
Send findings to code scanning
Setsarif-path and upload the file. Every failed rule becomes an alert in
the repository’s Security tab, next to the findings of your other scanners.
The upload needs security-events: write, and in a private repository
contents: read as well.
min-score is what fails the pull request.
upload-sarif allows one upload per job for a given tool and category. To
audit two servers in one job, give each upload step its own category:
input, or the second upload is rejected with “only one run of the
codeql/upload-sarif action is allowed per job per tool/category”. Severity
mapping and what the file shows of the target are on the
CLI reference.
Smoke-test the tools
For a server you operate, add--smoke through args. After the audit,
mcpscore calls the tools annotated readOnlyHint: true and checks their
answers. A failed check fails the job after the report and comment are
published:
Use the numbers in later steps
Give the step anid and read its outputs:
Pin everything
@v1 follows the latest v1 release, and version defaults to the latest
mcpscore on PyPI. For reproducible runs, pin the action to a commit and
version to an mcpscore release:
Inputs
Outputs
When it fails
::error::score 91/108 (84%) is below the required 90%
- Cause: the server scored under the threshold. This is the action doing its job.
- Fix: open the comment or job summary, take the failed
rule_ids to the rules reference or improve your score, and fix the server.
- Cause: the workflow token is read-only. GitHub does this on pull requests
from forks, and when the job lacks
permissions: pull-requests: write. - Fix: add the permission. For fork PRs, read the report in the job summary.
Do not switch the workflow to
pull_request_targetto get the comment. That event runs with a write token in the base repository’s context, and running the fork’s server there hands the token to untrusted code. The GitHub Security Lab write-up explains the trap.
- Cause: the server needs a credential and the workflow passed none, so
min-scorewas judged on the observable surface only. - Fix: export
MCPSCORE_TOKENfrom a secret, as above, so the gate judges the full audit.
::error::mcpscore could not audit ./server.py (exit 2)
- Cause: the server did not start or did not answer. The CLI’s own error is
printed with it.
Server script not found: ./server.pymeans noactions/checkoutstep, or a path relative to the wrong directory. - Fix: check out the repo first, use a path relative to its root, and install the server’s dependencies before the action runs. Troubleshooting covers the other connection failures.
::error::'sarif-path' and a `--sarif` in `args` name two files; keep one (prefer the input)
- Cause: the SARIF file was requested twice.
- Fix: remove
--sariffromargsand keepsarif-path.
Recap
- Add a workflow that runs
mcp-box/mcpscore-action@v1onpull_requestwith atarget. - Grant
pull-requests: writeso the report lands as a comment. - Set
min-scoreto fail pull requests that lower the score.
What’s next
Smoke mode
Call your server’s tools in CI, not only list them
CI beyond GitHub
The same gate in GitLab or any CI with exit codes
Score badge
A README badge that follows your server’s score
Action source
mcp-box/mcpscore-action on GitHub