doorman
One-line OAuth for self-hosted MCP servers.
53% of self-hosted API services ship with static API keys. Only 8.5% implement proper OAuth. For MCP servers exposed to AI agents and human clients alike, that gap is not a configuration choice: it is a vulnerability. Doorman closes it in one line.
Before / After
Before: 28 lines of boilerplate every time
import os
from fastmcp import FastMCP
from fastmcp.server.auth.providers.github import GitHubProvider
mcp = FastMCP("My Server")
# Manually validate every required credential
client_id = os.environ.get("GITHUB_CLIENT_ID")
if not client_id:
raise ValueError("GITHUB_CLIENT_ID is required")
client_secret = os.environ.get("GITHUB_CLIENT_SECRET")
if not client_secret:
raise ValueError("GITHUB_CLIENT_SECRET is required")
jwt_secret = os.environ.get("DOORMAN_JWT_SECRET")
if not jwt_secret:
raise ValueError("DOORMAN_JWT_SECRET is required")
mcp.auth = GitHubProvider(
client_id=client_id,
client_secret=client_secret,
base_url=os.environ.get("DOORMAN_BASE_URL", "http://127.0.0.1:8000"),
required_scopes=["read:user", "user:email"],
allowed_client_redirect_uris=["http://localhost:*", "http://127.0.0.1:*"],
jwt_signing_key=jwt_secret,
require_authorization_consent=False,
)
After: one line
import doorman
doorman.protect(mcp, github=True)
Quickstart
1. Install
pip install doorman-mcp
2. Set environment variables
Generate a strong JWT secret:
python -c "import secrets; print(secrets.token_urlsafe(48))"
Then export all required variables for your shell:
macOS / Linux (bash/zsh):
export GITHUB_CLIENT_ID=your_client_id
export GITHUB_CLIENT_SECRET=your_client_secret
export DOORMAN_JWT_SECRET=paste_the_generated_secret_here
export DOORMAN_BASE_URL=http://127.0.0.1:8000
Windows (PowerShell):
$env:GITHUB_CLIENT_ID = "your_client_id"
$env:GITHUB_CLIENT_SECRET = "your_client_secret"
$env:DOORMAN_JWT_SECRET = "paste_the_generated_secret_here"
$env:DOORMAN_BASE_URL = "http://127.0.0.1:8000"
Variables set this way last only for the current terminal session. For real deployments use a secrets manager or a gitignored .env file; never commit credentials.
3. Create a GitHub OAuth App
In GitHub → Settings → Developer settings → OAuth Apps → New OAuth App:
- Homepage URL:
http://127.0.0.1:8000 - Authorization callback URL:
http://127.0.0.1:8000/auth/callback
4. Wire doorman into your server
from fastmcp import FastMCP
import doorman
mcp = FastMCP("My Server")
doorman.protect(mcp, github=True)
@mcp.tool()
def hello() -> str:
return "authenticated!"
mcp.run()
5. Run and connect
python my_server.py
Any MCP client that supports OAuth 2.0 can now connect. The client is redirected to GitHub, authenticates, and receives a short-lived JWT: no static keys, no copy-paste credentials.
Fail-closed by design
Doorman's contract: if auth cannot be configured, the server refuses to start.
- Missing
GITHUB_CLIENT_ID?ValueError; the server does not start. - Missing
DOORMAN_JWT_SECRET?ValueError; the server does not start. - No provider specified at all?
ValueError; the server does not start.
There is exactly one escape hatch:
doorman.protect(mcp, allow_unauthenticated=True)
This emits a loud UserWarning and bypasses all auth. It is intended for local development only and must be opted into by name; you cannot accidentally end up in an unprotected state.
Built on FastMCP
Doorman is a thin configuration layer on top of FastMCP's first-class OAuth primitives. It does not replace or wrap FastMCP; it reads your environment, validates credentials, and calls GitHubProvider with safe defaults. All MCP protocol handling, session management, and transport concerns remain in FastMCP.
License
MIT: see LICENSE.
Metadata
Release files for doorman-mcp 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| doorman_mcp-0.1.1.tar.gz | 109.9 kB | Without provenance | Details for doorman_mcp-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| doorman_mcp-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for doorman_mcp-0.1.1-py3-none-any.whl |
Total release size: 115.2 kB
Release files / doorman_mcp-0.1.1.tar.gz
| Download URL | doorman_mcp-0.1.1.tar.gz |
|---|---|
| Size | 109.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
252b0c1c252fa60a6f5736147736fbcc83d421f85d2555e17a79bd39e129c03f
|
|
BLAKE2b-256 checksum How to use checksums |
1807363b3ad4bba5c049e263d2ff1ec7cbfcf9a110558150490b538de426147e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / doorman_mcp-0.1.1-py3-none-any.whl
| Download URL | doorman_mcp-0.1.1-py3-none-any.whl |
|---|---|
| Size | 5.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a51c634ad3b8b0988c9b0946f5c5439b55c39126ea0e211363343ea1c220e533
|
|
BLAKE2b-256 checksum How to use checksums |
dbe2628bb2fab2672d8150034cabb633e5a780419cea24ee619f2a3215ffef08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for doorman-mcp 0.1.1
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.