MCPRift
Find the seam before someone crosses it.
MCPRift is an experimental, bounded authorization-contract runner for Model Context Protocol (MCP) servers. It checks whether a controlled server's authorization boundary behaves as expected when the client presents different identities or sends unusual protocol requests.
It is designed for authorized testing of local or otherwise controlled targets. It is not a network scanner, exploit framework, complete OAuth certification suite, or general-purpose MCP client.
Licensed under Apache-2.0.
What it does
MCPRift currently provides:
- Baseline connection using the official MCP Python SDK over Streamable HTTP.
- Capability inspection for tools, resources, resource templates, and prompts without invoking them.
- Identity comparison across anonymous, authenticated, invalid, and expired credential contexts for one explicitly acknowledged-safe action.
- Authorization checks for allowed and denied tool calls and per-user resource access.
- Session/state checks that change actors inside one reused SDK/HTTP session and verify that the first actor's credentials do not authorize the second.
- OAuth boundary checks for discovery metadata, exact 401/403 challenges, expiry, scopes, token audience, S256 PKCE, resource indicators, and token passthrough to a synthetic downstream API.
- Deterministic protocol mutations for malformed JSON-RPC and unknown or incomplete requests.
- Sanitized evidence written as private JSON records, with terminal, JSON, and SARIF reporting.
- Replay of a recorded built-in case against a controlled target.
- A disposable local lab with opt-in vulnerability toggles for reproducible tests.
The primary CI workflow is contract-driven:
mcprift init assessment.json --lab
mcprift validate assessment.json
mcprift run assessment.json --acknowledge-safe-actions
init writes a runnable, non-secret JSON contract. validate performs only
offline structural and safety checks and does not need credential variables.
run resolves credentials from the declared environment-variable names,
executes access, visibility, session, and protocol cases, and writes sanitized
terminal, JSON, or SARIF evidence. It returns 0 for passing verdicts, 1
for security-policy failures, and 2 for invalid configuration or execution
errors.
For access cases, denied is satisfied only by an explicit HTTP 401 or
403 response from the protected endpoint. Tool errors, MCP protocol errors,
transport failures, and an unavailable endpoint are execution errors; they do
not count as successful denials.
Safety boundary
Safety constraints are part of the implementation, not just usage advice:
- Targets must be absolute, credential-free
httporhttpsURLs on a loopback host such as127.0.0.1orlocalhost. - Redirects are disabled for SDK-managed requests and raw mutations.
- Tool calls require
known_safe: true, a non-empty safety justification, and the run-time--acknowledge-safe-actionsflag. The lab contract invokes only the side-effect-freesafe_echotool. - Bearer tokens are read from environment variables, never URL or CLI token arguments.
- Evidence stores a target fingerprint rather than the target URL.
- Evidence excludes token values, server response bodies, and action argument values. Raw mutation evidence retains only status, content type, response size, and a response hash.
- Response and evidence sizes are bounded.
Use MCPRift only against systems and data you are authorized to assess.
Requirements
- Python 3.12+
uv- An MCP server reachable through controlled Streamable HTTP
The project currently depends on mcp==2.0.0 and httpx2.
Installation
Install a published release as an isolated command-line tool:
uv tool install mcprift
mcprift version
mcprift demo
For source development, clone this repository and use the locked environment shown below.
Quick start: disposable lab and CI contract
The fastest way to try MCPRift is the one-command demo:
uv sync
uv run mcprift demo
It starts the disposable lab on a temporary loopback port, runs the 22-case
contract, and removes the lab and temporary contract afterward. To retain
sanitized evidence, add --evidence-dir mcprift-evidence.
Interactive terminal output uses green for successful checks and yellow for
guidance or attention-required results. Set NO_COLOR=1 to disable terminal
colors. JSON, SARIF, evidence files, and redirected output remain uncolored.
To see the individual steps, start the local lab yourself. The lab binds to
127.0.0.1:8080 by default and serves synthetic, side-effect-free data.
In terminal one:
uv sync
uv run python -m mcprift.lab
In terminal two:
export MCPRIFT_AUTH_TOKEN=mcprift-lab-alice
export MCPRIFT_BOB_TOKEN=mcprift-lab-bob
export MCPRIFT_INVALID_TOKEN=mcprift-lab-invalid
export MCPRIFT_EXPIRED_TOKEN=mcprift-lab-expired
uv run mcprift init assessment.json --lab
uv run mcprift validate assessment.json
uv run mcprift run assessment.json --acknowledge-safe-actions
The secure lab should produce 22 passing contract cases. The command writes a
private evidence file under mcprift-evidence/ and returns:
0when all selected checks pass;1when a security check fails;2when execution or configuration fails.
CI
The checked-in
authorization-contract workflow
is the reference CI integration. It runs the unit suite and Ruff, starts the
disposable lab, validates and runs the secure contract, uploads its private
evidence, and confirms that a seeded cross-tenant regression returns exit code
1. Its SARIF is retained as an artifact on every run and uploaded to GitHub
code scanning for same-repository pull requests, with a source location in the
checked-in contract. It is deliberately not uploaded on main, where the known
self-test failure would create a persistent false alert. GitHub shows an inline
pull-request annotation only when the referenced contract line is part of the
change.
Run the same checks locally:
uv sync
uv run python -m unittest discover -s tests -v
uv run ruff format --check .
uv run ruff check .
The workflow intentionally tests only the bundled loopback lab. A remote staging target and a real identity provider are later, explicitly controlled integration phases; a passing local workflow is not evidence about production.
For CI, keep credentials in the job's secret environment and make the evidence directory an explicit artifact path:
uv run mcprift validate assessment.json
uv run mcprift run assessment.json \
--acknowledge-safe-actions --format sarif --evidence-dir artifacts/mcprift
Contracts contain access, visibility, and protocol cases. Access actions
are tool-call, resource-read, or prompt-get; visibility cases assert
whether one tool, resource, resource template, or prompt is visible to one
actor; protocol cases select one deterministic mutation. Credentialed actors
contain only a token_env name, never a token value.
For a step-by-step guide to writing a contract for your own MCP server, see Writing an MCPRift authorization contract.
Public end-to-end example
The companion MCPRift pilot server
is a small independent Streamable HTTP server with two synthetic identities,
Alice and Bob. Its contract lives in
testdata/pilot-assessment.json and checks
that each identity may read only its own synthetic profile.
The pilot repository's GitHub Actions workflow starts the server and runs this contract from MCPRift on every push and pull request. It is a concrete example of the intended integration: keep the contract with the server's test setup so a cross-tenant regression blocks CI. Run the pilot in its deliberately vulnerable mode locally to see MCPRift report the expected failure.
OAuth and PKCE lab
The separate OAuth lab is a protected MCP resource and a minimal local authorization server. Start it in terminal one:
uv run python -m mcprift.oauth_lab
Run its twelve checks in terminal two:
uv run mcprift oauth-test http://127.0.0.1:8090/mcp
This verifies protected-resource and authorization-server discovery, Bearer challenges, expiry, insufficient scope, audience rejection, resource indicators, S256 PKCE failure and success, and use of a separate credential for the synthetic downstream API.
Reproduce controlled failures
The lab can intentionally enable one or more known vulnerabilities. This is useful for testing MCPRift's detection and reporting behavior:
uv run python -m mcprift.lab --vulnerable anonymous-tool
uv run python -m mcprift.lab --vulnerable expired-credential
uv run python -m mcprift.lab --vulnerable cross-user-resource
uv run python -m mcprift.lab --vulnerable session-identity-crossover
uv run python -m mcprift.lab --vulnerable prompt-access
uv run python -m mcprift.lab --vulnerable capability-visibility-leak
uv run python -m mcprift.oauth_lab --vulnerable wrong-audience
uv run python -m mcprift.oauth_lab --vulnerable token-passthrough
Available toggles:
| Toggle | Simulated failure |
|---|---|
anonymous-tool |
Anonymous callers can invoke safe_echo. |
expired-credential |
The expired lab credential is accepted. |
cross-user-resource |
A user can read another user's synthetic resource. |
session-identity-crossover |
A reused session keeps Alice's established identity after its request credentials change to Bob. |
prompt-access |
Anonymous callers can retrieve the protected review prompt. |
capability-visibility-leak |
Anonymous capability listings expose protected templates and prompts. |
OAuth-lab toggles:
| Toggle | Simulated failure |
|---|---|
wrong-audience |
The MCP resource accepts a token intended for another resource. |
token-passthrough |
The MCP access token is forwarded to and accepted by a downstream API. |
The toggles may be repeated to combine failures. They affect only the disposable local lab.
CLI
Show the available commands:
uv run mcprift help
uv run mcprift version
Connect and inspect a controlled server:
uv run mcprift connect http://127.0.0.1:8080/mcp
uv run mcprift inspect http://127.0.0.1:8080/mcp
uv run mcprift inspect http://127.0.0.1:8080/mcp --json
inspect lists capabilities but does not invoke tools, read resources, or run
prompts. To inspect what a credentialed test identity can see, provide a name
and token environment variable; the token itself is never accepted on the
command line or written to the output:
export TEST_ALICE_TOKEN='...'
uv run mcprift inspect http://127.0.0.1:8080/mcp \
--actor alice --token-env TEST_ALICE_TOKEN --json > alice-inventory.json
Compare one explicitly safe tool across identity contexts. The three token variables below are required; their values never appear in output or evidence:
uv run mcprift compare http://127.0.0.1:8080/mcp \
--safe-tool safe_echo \
--arguments '{"message":"probe"}'
Run the complete bounded authorization suite, or select individual cases:
uv run mcprift test http://127.0.0.1:8080/mcp
uv run mcprift test http://127.0.0.1:8080/mcp \
--case MCPRIFT-AUTH-001 \
--case MCPRIFT-BOUNDARY-002
Run only the bounded session/state invariant. MCPRift opens one controlled
session as Alice, reads Alice's synthetic resource, changes that same session's
request credentials to Bob, and verifies that Bob is denied. This command pins
the SDK to its handshake-era legacy mode so the server supplies an MCP
session ID:
uv run mcprift session-test http://127.0.0.1:8080/mcp
Run the bounded OAuth suite against the disposable OAuth lab:
uv run mcprift oauth-test http://127.0.0.1:8090/mcp
uv run mcprift oauth-test http://127.0.0.1:8090/mcp --format sarif
Send one deterministic raw JSON-RPC mutation. Valid kinds are
invalid-json, missing-jsonrpc, unknown-method, and empty-batch:
uv run mcprift mutate http://127.0.0.1:8080/mcp unknown-method
Render an existing evidence record in terminal, JSON, or SARIF format, or replay one canonical case:
uv run mcprift report mcprift-evidence/mcprift-<run-id>.json --format sarif
uv run mcprift replay http://127.0.0.1:8080/mcp \
mcprift-evidence/mcprift-<run-id>.json \
--case MCPRIFT-AUTH-001
Commands that execute the built-in suite, the session test, or replay a case
require all four lab credential variables: MCPRIFT_AUTH_TOKEN,
MCPRIFT_BOB_TOKEN, MCPRIFT_INVALID_TOKEN, and
MCPRIFT_EXPIRED_TOKEN.
Built-in cases
The default registry contains nine stable cases:
MCPRIFT-AUTH-001— anonymous tool access is denied.MCPRIFT-AUTH-002— an authenticated caller can use the safe tool.MCPRIFT-AUTH-003— invalid credentials are denied.MCPRIFT-AUTH-004— expired credentials are denied.MCPRIFT-BOUNDARY-001— Alice can read Alice's resource.MCPRIFT-BOUNDARY-002— Alice cannot read Bob's resource.MCPRIFT-BOUNDARY-003— Bob can read Bob's resource.MCPRIFT-BOUNDARY-004— Bob cannot read Alice's resource.MCPRIFT-SESSION-001— Alice's credentials cannot authorize Bob's request after the controlled SDK/HTTP session is reused and rebound to Bob.
These cases exercise observable authorization outcomes. They do not establish full OAuth behavior or prove that an arbitrary production deployment is secure.
Development
Install the locked development environment and run formatting, linting, and tests:
uv sync
uv run ruff format --check .
uv run ruff check .
uv run python -m unittest discover -s tests -v
The Python implementation lives in src/mcprift/. The previous Go
implementation is retained in legacy-go/ and is not part of the Python build
or test commands.
Current scope and limitations
MCPRift 0.5.0 does not claim complete OAuth conformance, stdio target support, broad network scanning, exploit automation, or arbitrary third-party plugins. The OAuth suite remains explicitly lab-only and uses a disposable HTTP-only local provider; production TLS, external identity providers, dynamic client registration, refresh-token rotation, revocation, and browser interaction are outside the tested boundary. Session testing remains limited to one deterministic actor change in one reused, handshake-era Streamable HTTP session. Modern sessionless endpoints, cookies, concurrent sessions, server restarts, and replayed protocol messages remain outside that invariant.
The project is experimental. Treat its results as bounded evidence for the tested target and configuration, not as a certification or a guarantee of security.
License
MCPRift is licensed under the Apache License 2.0. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcprift-0.5.0.tar.gz.
File metadata
- Download URL: mcprift-0.5.0.tar.gz
- Upload date:
- Size: 56.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c945634f1e674c96b87da27f1b6a84d91ead1da9f0245005d33bbee8b144dfbd
|
|
| MD5 |
c54c1b7004b7b37ed0f1ca1e9a85f012
|
|
| BLAKE2b-256 |
dbfcdf530ee6c32ac21a7ba00a7284353b51a9a4865099251278b0b8a8a89236
|
Provenance
The following attestation bundles were made for mcprift-0.5.0.tar.gz:
Publisher:
publish-to-pypi.yml on sanjayy0612/MCPRift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcprift-0.5.0.tar.gz -
Subject digest:
c945634f1e674c96b87da27f1b6a84d91ead1da9f0245005d33bbee8b144dfbd - Sigstore transparency entry: 2585976160
- Sigstore integration time:
-
Permalink:
sanjayy0612/MCPRift@abcb494395bdf5798e12c149fa0df1a6b9779705 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/sanjayy0612
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@abcb494395bdf5798e12c149fa0df1a6b9779705 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcprift-0.5.0-py3-none-any.whl.
File metadata
- Download URL: mcprift-0.5.0-py3-none-any.whl
- Upload date:
- Size: 49.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff8659f3c9d047ae1dc7e4afa43f1fb112f238dbc40d35641e74960a0e9f42ab
|
|
| MD5 |
3b5ef91c23a57ce4610bbb2f8e9dc366
|
|
| BLAKE2b-256 |
ac4d5eea753781218e5c9157e2d3498ef3c7c5bb47a9326dd0bc1bd0d2a495d3
|
Provenance
The following attestation bundles were made for mcprift-0.5.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on sanjayy0612/MCPRift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcprift-0.5.0-py3-none-any.whl -
Subject digest:
ff8659f3c9d047ae1dc7e4afa43f1fb112f238dbc40d35641e74960a0e9f42ab - Sigstore transparency entry: 2585976313
- Sigstore integration time:
-
Permalink:
sanjayy0612/MCPRift@abcb494395bdf5798e12c149fa0df1a6b9779705 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/sanjayy0612
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@abcb494395bdf5798e12c149fa0df1a6b9779705 -
Trigger Event:
push
-
Statement type: