Skip to main content

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:

  1. A middleware that intercepts incoming requests, decrypts a JWE Bearer token, and makes the authenticated user's claims available via get_user().
  2. 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
  1. The token producer fetches the server's public key from /.well-known/jwks.json.
  2. It encrypts a JSON payload (user claims) into a JWE token using that public key.
  3. The token is sent as a Bearer token in the Authorization header.
  4. 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

mcp_auth_middleware-0.1.1.tar.gz (8.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_auth_middleware-0.1.1-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

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

Hashes for mcp_auth_middleware-0.1.1.tar.gz
Algorithm Hash digest
SHA256 68a57b16d79a1ff3b176ceb0c57229d4e94913c9c23044616ac885561adda24e
MD5 98c83692478673f2aa7cd3f86428a251
BLAKE2b-256 68fc69f385ca06706f34f706a75489d2bed0443d4afac437d143b1dae917c200

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_auth_middleware-0.1.1.tar.gz:

Publisher: publish.yml on fhswf/mcp-auth-middleware

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_auth_middleware-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for mcp_auth_middleware-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bace64181daea1d32a00eea2fd307be405eee3605091d7f5955c8b133f5e38df
MD5 c45309f03679c75bd28b2e3756c65058
BLAKE2b-256 081da651dfc2ab1a007376af5448ae25c527a32f3f1a4fdd276095990f8ca69a

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page