Skip to main content

mcpscore

CI Coverage PyPI Python License

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: 78/94
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 20 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. 78/94 means the server earned 78 of the 94 points its rules can award. Rules that cannot apply to this server are skipped and left out of the 94, 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: 131/155
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: 78/94
...
Gate failed — --fail-under 90: score 78/94 (83%) 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.

[![mcpscore score](https://mcpscore.dev/api/v1/servers/badge.svg?url=https%3A%2F%2Fyour-server.example%2Fmcp)](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

Exit code Meaning
0 The audit completed and every gate passed
1 The audit never ran: a usage error or a failed --oauth flow
2 mcpscore could not connect to the server
3 --fail-under, --fail-under-readiness, or [gate] was not met
4 A --smoke check failed (3 wins when both fail)

What the score measures

99 server rules in four categories:

  • Protocol: protocol version, server identity, capabilities, transport.
  • Primitives: tools, prompts, resources, and resource templates. Names, titles, descriptions, schemas, annotations, and pagination decide whether an agent picks the right tool and calls it correctly.
  • Security & Auth: TLS, Origin validation, 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: --stdio runs a program, and a bare script is looked up on PATH.
  • 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 --stdio names 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.

Metadata

Release files for mcpscore 1.21.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcpscore 1.21.2
File Size Uploaded
mcpscore-1.21.2.tar.gz 389.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcpscore 1.21.2
File Interpreter ABI Platform
mcpscore-1.21.2-py3-none-any.whl Python 3 none any Details

Total release size: 605.5 kB

Release files / mcpscore-1.21.2.tar.gz

Download URL mcpscore-1.21.2.tar.gz
Size 389.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c44f97ace274465c5b312c638070a41424598c19f1dbf6a4f6d2ce72c59a11b2
BLAKE2b-256 checksum
How to use checksums
b38c16ce3d414a11d36e258947ab8964c0a0f7b3eb7f4b3d180250546d6f2cac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / mcpscore-1.21.2-py3-none-any.whl

Download URL mcpscore-1.21.2-py3-none-any.whl
Size 216.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ac33266919a8f0e716ad8c99903bef57b34f241536e2a6add0aecb616686ddad
BLAKE2b-256 checksum
How to use checksums
1823caf07809a110ab837e9d0f6ce6720e6b568718803b4e266980fe1fa24c75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

1.22.2

2 release files

This release

1.21.2 This release

2 release files

1.21.1

2 release files

1.21.0

2 release files

1.20.0

2 release files

1.19.0

2 release files

1.18.0

2 release files

1.17.0

2 release files

1.16.0

2 release files

1.15.0

2 release files

1.14.2

2 release files

1.14.1

2 release files

1.14.0

2 release files

1.10.0

2 release files

1.9.0

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page