Metadata-Version: 2.4
Name: reizan-mcpcheck
Version: 0.1.0
Summary: Deterministic MCP authorization fail-closed conformance checker.
Author: Reizan contributors
License: MIT
Keywords: mcp,oauth,authorization,conformance,security
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="assets/banner.png" alt="reizan-mcpcheck" width="100%">
</p>

# reizan-mcpcheck

`reizan-mcpcheck` is the fail-closed authorization conformance checker the
official MCP conformance suite does not cover. It probes MCP HTTP
authorization implementations for the MCP-2026 OAuth hardening requirements
with deterministic PASS/FAIL/INCONCLUSIVE verdicts, exact HTTP evidence, and
SHA-256-attested transcripts. No LLM is used in the verdict path.

This v0 tracks the draft MCP authorization specification as of 2026-06-26,
targeting the MCP-2026 final release expected on 2026-07-28.

## Scope and authorization

Use this only against MCP servers, authorization servers, and test fixtures
that you own or are explicitly authorized to assess. Reports contain exact
request and response evidence, including bearer tokens from the run.

`reizan-mcpcheck` is not a vulnerability scanner for third-party systems. The
target config requires `"owner_authorized": true` so CI jobs make that boundary
explicit.

## Probes

- `resource_param_mandatory`: verifies that `resource` is sent in both the
  authorization request and token request, then checks that omitting it is
  rejected fail-closed.
- `rfc9207_iss_validation`: validates the authorization response `iss` against
  the configured issuer using exact string comparison, with no URI
  normalization.
- `token_not_replayable_across_services`: mints a token for resource A and
  verifies that resource B rejects it.
- `fail_closed_missing_invalid_resource_or_audience`: verifies rejection for
  invalid resource indicators, missing Authorization, invalid bearer tokens,
  and wrong-audience tokens.

Each probe returns a deterministic verdict and a `transcript_sha256` over the
canonical JSON representation of the exact HTTP request/response transcript.
The top-level report also includes a `report_sha256`.

## Install

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

## CLI

```bash
reizan-mcpcheck examples/compliant.json --json
reizan-mcpcheck examples/compliant.json --out reports/mcpcheck.json
```

Exit codes:

- `0`: no probe returned FAIL.
- `1`: one or more probes returned FAIL.
- `2`: config or invocation error.

Use `--fail-on-inconclusive` if CI should also reject INCONCLUSIVE probes.

## Target config

```json
{
  "name": "my-owned-mcp-lab",
  "owner_authorized": true,
  "allow_insecure_http": false,
  "authorization_server": {
    "issuer": "https://auth.example.test",
    "authorization_endpoint": "https://auth.example.test/oauth/authorize",
    "token_endpoint": "https://auth.example.test/oauth/token",
    "authorization_response_iss_parameter_supported": true
  },
  "client": {
    "client_id": "mcpcheck",
    "redirect_uri": "http://127.0.0.1:65535/callback",
    "scope": "mcp:tools"
  },
  "resources": [
    {
      "name": "service-a",
      "resource": "https://mcp-a.example.test/mcp",
      "endpoint": "https://mcp-a.example.test/mcp"
    },
    {
      "name": "service-b",
      "resource": "https://mcp-b.example.test/mcp",
      "endpoint": "https://mcp-b.example.test/mcp"
    }
  ],
  "invalid_resource": "https://mcp-a.example.test/not-a-valid-resource"
}
```

The v0 runner assumes a noninteractive authorization endpoint that redirects
with an authorization code. Browser/login-driven flows may return
INCONCLUSIVE unless wrapped by a self-run lab harness.

## Deterministic demo

```bash
make demo
```

The demo starts two local, key-free fixtures:

- `http://127.0.0.1:8765`: compliant fixture, expected PASS.
- `http://127.0.0.1:8766`: deliberately misconfigured fixture, expected FAIL.

It writes:

- `examples/compliant.json`
- `examples/misconfigured.json`
- `reports/demo-compliant.json`
- `reports/demo-misconfigured.json`

## Tests

```bash
make test
```

## References

- MCP draft authorization spec:
  <https://modelcontextprotocol.io/specification/draft/basic/authorization>
- RFC 8707, Resource Indicators for OAuth 2.0:
  <https://www.rfc-editor.org/rfc/rfc8707>
- RFC 9207, OAuth 2.0 Authorization Server Issuer Identification:
  <https://www.rfc-editor.org/rfc/rfc9207>
- Official MCP conformance package:
  <https://github.com/modelcontextprotocol/conformance>
