JWE authentication middleware for MCP/Starlette applications
Project description
mcp-auth-middleware
JWE authentication middleware for HTTP MCP servers. Encrypt user data end-to-end so that only your MCP server can read it.
What it does
mcp-auth-middleware gives your MCP server two things:
- A middleware that intercepts incoming requests, decrypts a JWE Bearer token, and makes the authenticated user's claims available via
get_user(). - A CLI (
mcp-auth-middleware) that generates RSA key pairs in JWKS format, outputs Kubernetes Secret YAML, and securely deletes local keys when you're done.
Installation
pip install mcp-auth-middleware
This installs the library and the mcp-auth-middleware CLI.
Quick start (local development)
1. Generate keys
mcp-auth-middleware generate
Output:
Keys generated (JWKS format):
Private: .keys/mcp-private.json
Public: .keys/mcp-public.json
Add .keys/ to your .gitignore immediately.
2. Set the environment variable
Create a .env file (or export directly):
MCP_KEY_FILE_PATH=.keys/mcp-private.json
3. Add the middleware to your MCP server
Works with any Starlette-based MCP server:
from fastmcp import FastMCP
from mcp_auth_middleware import JWKSAuthMiddleware, get_user
import uvicorn
mcp = FastMCP("My Server")
# Register your tools on `mcp` as usual …
app = mcp.http_app()
app.add_middleware(JWKSAuthMiddleware)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Or with the official MCP Python SDK directly:
from mcp.server.fastmcp import FastMCP
from mcp_auth_middleware import JWKSAuthMiddleware, get_user
import uvicorn
mcp = FastMCP("My Server")
app = mcp.http_app()
app.add_middleware(JWKSAuthMiddleware)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
4. Access user claims inside any tool
@mcp.tool()
def whoami() -> str:
user = get_user()
return f"Hello, {user.name or 'anonymous'}!"
get_user() returns an AuthUser dict. Access claims as attributes — missing keys return None instead of raising.
Kubernetes deployment
1. Generate keys and pipe straight into kubectl
mcp-auth-middleware k8s | kubectl apply -f -
This creates a Kubernetes Secret named mcp-server-keys in the default namespace. Customise with flags:
mcp-auth-middleware k8s --namespace my-ns --secret-name my-mcp-keys | kubectl apply -f -
2. Clean up local key material
mcp-auth-middleware clean
Uses shred (Linux) for secure deletion when available; falls back to overwrite-then-delete.
3. Mount the secret in your deployment
Add the following snippets to your existing Deployment YAML.
Volume definition (under spec.template.spec.volumes):
- name: mcp-secret-volume
secret:
secretName: mcp-server-keys # must match --secret-name
defaultMode: 0400
items:
- key: mcp_jwks
path: key.json
Volume mount (under your container's volumeMounts):
- mountPath: /etc/mcp/secrets
name: mcp-secret-volume
readOnly: true
Environment variable (under env):
- name: MCP_KEY_FILE_PATH
value: "/etc/mcp/secrets/key.json"
A full example Deployment + Service is in examples/k8s-deployment.yaml.
CLI reference
All commands accept -o / --output <dir> to change the key directory (default: .keys).
| Command | Description |
|---|---|
mcp-auth-middleware generate |
Generate an RSA-4096 JWKS key pair |
mcp-auth-middleware k8s |
Generate keys and print a Kubernetes Secret YAML to stdout |
mcp-auth-middleware clean |
Securely delete keys from the output directory |
mcp-auth-middleware k8s flags
| Flag | Default | Description |
|---|---|---|
-n, --namespace |
default |
Kubernetes namespace |
-s, --secret-name |
mcp-server-keys |
Name of the K8s Secret |
API reference
JWKSAuthMiddleware
Starlette middleware. Attach it to any HTTP-based MCP server app:
app.add_middleware(
JWKSAuthMiddleware,
verifier=None, # optional: provide your own JWETokenVerifier
jwks_path="/.well-known/jwks.json", # public-key endpoint path
)
The middleware automatically serves your server's public JWKS at the configured path so that token producers can discover your encryption key.
get_user() -> AuthUser
Returns the authenticated user's claims for the current request. Safe to call from any async context (tools, routes, dependencies) during request handling.
user = get_user()
user.email # claim value or None
user["role"] # also works as a regular dict
JWETokenVerifier
Lower-level class if you need to verify tokens outside the middleware:
from mcp_auth_middleware import JWETokenVerifier
verifier = JWETokenVerifier() # reads MCP_KEY_FILE_PATH
claims = await verifier.verify_token(token_string)
public_jwks = verifier.get_jwks() # for publishing
Environment variables
| Variable | Required | Description |
|---|---|---|
MCP_KEY_FILE_PATH |
Yes | Path to the private JWKS JSON file |
How the token flow works
Producer (client/gateway) MCP Server
―――――――――――――――――――――――――― ――――――――――
GET /.well-known/jwks.json
◄──── { public RSA key }
Encrypt claims with public key
POST /mcp/tool ──────────────────────►
Authorization: Bearer <JWE>
Middleware decrypts JWE
with private key
─► get_user() returns claims
- The token producer fetches the server's public key from
/.well-known/jwks.json. - It encrypts a JSON payload (user claims) into a JWE token using that public key.
- The token is sent as a
Bearertoken in theAuthorizationheader. - The middleware decrypts it with the private key and exposes claims via
get_user().
Project structure
mcp-auth-middleware/
├── pyproject.toml
├── README.md
├── LICENSE
├── CHANGELOG.md
├── requirements.txt
├── requirements-dev.txt
├── MANIFEST.in
├── .gitignore
├── mcp_auth_middleware/
│ ├── __init__.py
│ ├── py.typed
│ ├── cli.py
│ ├── middleware.py
│ └── verifier.py
├── tests/
└── examples/
├── server.py
└── k8s-deployment.yaml
License
MIT
Project details
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 mcp_auth_middleware-0.1.1.tar.gz.
File metadata
- Download URL: mcp_auth_middleware-0.1.1.tar.gz
- Upload date:
- Size: 8.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68a57b16d79a1ff3b176ceb0c57229d4e94913c9c23044616ac885561adda24e
|
|
| MD5 |
98c83692478673f2aa7cd3f86428a251
|
|
| BLAKE2b-256 |
68fc69f385ca06706f34f706a75489d2bed0443d4afac437d143b1dae917c200
|
Provenance
The following attestation bundles were made for mcp_auth_middleware-0.1.1.tar.gz:
Publisher:
publish.yml on fhswf/mcp-auth-middleware
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_auth_middleware-0.1.1.tar.gz -
Subject digest:
68a57b16d79a1ff3b176ceb0c57229d4e94913c9c23044616ac885561adda24e - Sigstore transparency entry: 920703559
- Sigstore integration time:
-
Permalink:
fhswf/mcp-auth-middleware@86e96308d902bc58bef726c0b95fe57d173ded27 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/fhswf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@86e96308d902bc58bef726c0b95fe57d173ded27 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mcp_auth_middleware-0.1.1-py3-none-any.whl.
File metadata
- Download URL: mcp_auth_middleware-0.1.1-py3-none-any.whl
- Upload date:
- Size: 9.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bace64181daea1d32a00eea2fd307be405eee3605091d7f5955c8b133f5e38df
|
|
| MD5 |
c45309f03679c75bd28b2e3756c65058
|
|
| BLAKE2b-256 |
081da651dfc2ab1a007376af5448ae25c527a32f3f1a4fdd276095990f8ca69a
|
Provenance
The following attestation bundles were made for mcp_auth_middleware-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on fhswf/mcp-auth-middleware
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_auth_middleware-0.1.1-py3-none-any.whl -
Subject digest:
bace64181daea1d32a00eea2fd307be405eee3605091d7f5955c8b133f5e38df - Sigstore transparency entry: 920703622
- Sigstore integration time:
-
Permalink:
fhswf/mcp-auth-middleware@86e96308d902bc58bef726c0b95fe57d173ded27 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/fhswf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@86e96308d902bc58bef726c0b95fe57d173ded27 -
Trigger Event:
release
-
Statement type: