mcpscore
Lighthouse for MCP. Point mcpscore at an MCP server and get a score plus a list of what to fix, each finding cited to its source: the MCP spec, an RFC, the MCP Registry schema, or a stated best practice.
A missing tool title, a stale protocol version, or an endpoint that accepts
any Origin never crashes your server. It makes agents pick the wrong tool,
or lets a malicious web page talk to your server. mcpscore finds those before
your users do. It is deterministic and needs no API key and no sign-up.
Documentation: docs.mcpscore.dev
Install
pip install mcpscore
With uv, uvx mcpscore runs it without installing. Node users can run
npx @mcp-box/mcpscore with uv or pipx on PATH; the npm package launches
this Python engine (npm setup).
Audit your first server
mcpscore https://mcp.deepwiki.com/mcp
Welcome to mcpscore!
Successfully connected to MCP server via Streamable HTTP: https://mcp.deepwiki.com/mcp
Transport: streamable-http
Starting the audit...
✅ Protocol version "2025-11-25" is one of the allowed versions.
❌ Server title is not present in server info. This is an optional quality recommendation. First affected field: server "/serverInfo/title".
Fix: Add a human-readable serverInfo.title so clients can display a recognizable server name.
✅ Server uses HTTPS with valid TLS (TLSv1.3).
❌ Streamable HTTP does not reject an invalid foreign Origin with HTTP 403, risking DNS rebinding. Observed HTTP status: 200.
Fix: Validate supplied Origin headers against the origins allowed for this endpoint. Return HTTP 403 for invalid origins; do not allow every origin to satisfy browser requests.
...
Audit finished. Final score: 96/114
Spec: 2025-11-25 negotiated (latest: 2026-07-28) · era: legacy
Readiness for MCP 2026-07-28: 3/13 (informative — not part of the main score; 4 of 21 checks assessed)
That's it. The server has a score.
Each ✅ or ❌ is one rule: one check, like security_origin_validation, that
passed or failed. Every ❌ carries a Fix: line. 96/114 means the server
earned 96 of the 114 points its rules can award. Rules that cannot apply to
this server are skipped and left out of the 114, so a server is never
penalized for a feature it does not have.
Audit a server on your machine
A local server is audited the same way as a URL: put the script where the URL was.
mcpscore path/to/server.py
Transport: stdio
...
Audit finished. Final score: 140/164
Spec: 2025-11-25 negotiated (latest: 2026-07-28) · era: dual-era
Python and Node scripts are detected by extension. For any other language,
or a Python server in its own environment, name the command after --stdio:
mcpscore --stdio ./my-go-server
mcpscore --stdio uv run server.py
--stdio consumes the rest of the line, so put every other option before it.
More: audit a local server.
Audit a server behind auth
mcpscore https://your-server.example/mcp --token "$TOKEN"
Without a credential, an auth-gated server still gets a partial score for its
auth, TLS, and transport surface. --oauth opens the browser instead of
taking a token, and --header sends an API-key header.
More: authenticated servers.
Fail the build when the score drops
mcpscore https://your-server.example/mcp --fail-under 90
When the score is below the threshold, the last line says so and the exit
code is 3:
Audit finished. Final score: 96/114
...
Gate failed — --fail-under 90: score 96/114 (84%) is below the required 90%
A refactor that drops a tool description now fails the pull request instead of reaching someone's agent. On GitHub, the Action runs the same gate and posts the report as a pull request comment:
- uses: mcp-box/mcpscore-action@v1
with:
target: https://your-server.example/mcp
min-score: 80
--json writes the full report to stdout and --sarif FILE writes failed
rules for GitHub code scanning.
More: GitHub Action,
other CI systems.
Check that your tools actually work
--smoke runs the audit, then calls your server's tools and checks how they
answer:
mcpscore --smoke path/to/server.py
Smoke checks (tools/call): 0 passed, 1 failed, 2 skipped — only readOnlyHint: true tools called (see --call-all). Never counted in the score.
skip smoke_structured_content [add]: not called under the safety default (readOnlyHint is not true); pass --call-all to include it
...
FAIL smoke_unknown_tool: reported a tool execution error (isError) for a nonexistent tool — the spec's unknown-tool example is a protocol error, and an isError result claims the tool exists and ran
Gate failed — --smoke: 1 smoke check(s) failed
A failed smoke check exits 4 and never changes the score. By default only
tools annotated readOnlyHint: true are called; --call-all includes the
rest, so use it only on a server you own.
More: smoke mode.
Turn off or re-rank rules
Put an mcpscore.toml next to your code:
[rules]
server_websiteurl_present = "off" # does not run
tools_title_present_in_all = "critical" # counts as CRITICAL
[gate]
fail_on = "high" # exit 3 on any failed rule at HIGH or above
The CLI finds it by walking up from the current directory, and also reads
[tool.mcpscore] in pyproject.toml. It changes the score in your
runs only; the website and the badge always use the defaults.
More: configure rules.
Show the score in your README
Audit the server once on mcpscore.dev and embed its badge. It always shows the latest score.
[](https://mcpscore.dev/s?url=https%3A%2F%2Fyour-server.example%2Fmcp)
More: score badge.
Score a published package
mcpscore --package pypi:mcpscore
📦 Auditing package pypi:mcpscore
Resolved version: 1.20.0
✅ Package "mcpscore" is published on pypi.
✅ Source repository declared: "https://github.com/mcp-box/mcpscore".
...
Audit finished. Final score: 16/16
Packaging audit of pypi:mcpscore — metadata only, the package was not downloaded or run.
This scores the npm or PyPI listing from registry metadata, on its own scale. More: package audits.
Exit codes
What the score measures
144 server rules in four categories:
- Protocol: protocol version, server identity, capabilities, transport.
- Primitives: tools, prompts, resources, resource templates, and skills served over the Skills extension. Names, titles, descriptions, schemas, annotations, and pagination decide whether an agent picks the right tool and calls it correctly.
- Security & Auth: TLS,
Originvalidation, error responses that leak data, and for auth-gated servers the OAuth challenge and metadata chain. - Readiness: how ready the server is for the 2026-07-28 spec revision. It counts in the main score for servers already on the new stateless lifecycle and is guidance for the rest.
Each rule has a severity: CRITICAL is worth 5 points, HIGH 3, MEDIUM 2,
LOW 1. Six more rules score packages under --package.
More: scoring methodology,
every rule,
what is stable between releases.
When it fails
Error connecting to the MCP server: https://... (exit 2)
- Cause: the URL answered, but not as an MCP endpoint.
- Fix: point at the MCP endpoint itself, usually ending in
/mcp.
'server.py' is a script, not an executable command (exit 2)
- Cause:
--stdioruns a program, and a bare script is looked up onPATH. - Fix: name the interpreter:
--stdio uv run server.py.
Audit finished. PARTIAL score: ... — not comparable to a full audit.
- Cause: the server answered 401 and you passed no credential.
- Fix: pass
--token,--header, or--oauth.
More: troubleshooting.
Requirements
- Python 3.11 or newer.
- For local servers, the server's own runtime on
PATH: Python for.py, Node.js for.js, and whatever--stdionames for the rest.
Use as a library
The package is fully typed (py.typed). MCPClient connects and collects,
and MCPAuditor runs the rules and builds the same report the CLI prints.
The --json report shape is versioned by schema_version, and rule_id
values are stable across releases.
Contributing
CONTRIBUTING.md covers the development setup and how to add a rule. MISSION.md says why the project exists, and SECURITY.md is for security reports. Every release is in the CHANGELOG. Bugs and ideas go to GitHub issues.
License
MIT. See LICENSE.