One-line OAuth for self-hosted MCP servers. Fail-closed by default.
Project description
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.
Project details
Release history Release notifications | RSS feed
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 doorman_mcp-0.1.1.tar.gz.
File metadata
- Download URL: doorman_mcp-0.1.1.tar.gz
- Upload date:
- Size: 109.9 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
252b0c1c252fa60a6f5736147736fbcc83d421f85d2555e17a79bd39e129c03f
|
|
| MD5 |
192ca318f6b9c26a6b9d55cc513bd62e
|
|
| BLAKE2b-256 |
1807363b3ad4bba5c049e263d2ff1ec7cbfcf9a110558150490b538de426147e
|
File details
Details for the file doorman_mcp-0.1.1-py3-none-any.whl.
File metadata
- Download URL: doorman_mcp-0.1.1-py3-none-any.whl
- Upload date:
- Size: 5.3 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a51c634ad3b8b0988c9b0946f5c5439b55c39126ea0e211363343ea1c220e533
|
|
| MD5 |
dac1fcd0f8823f49d63a6b9c6346a1c4
|
|
| BLAKE2b-256 |
dbe2628bb2fab2672d8150034cabb633e5a780419cea24ee619f2a3215ffef08
|