Auth Step By Step
HTTP-SSEMCP server with HTTP transport and JWT authentication step-by-step tutorial
MCP server with HTTP transport and JWT authentication step-by-step tutorial
This repository demonstrates building an MCP (Model Context Protocol) server with HTTP transport and JWT authentication, progressing through iterative steps.
This repo is a companion to the in-depth, step-by-step blog posts on "MCP Authorization". See the following:
Part 4 (late addition to the series): MCP Authorization With Dynamic Client Registration
The table below shows support for OAuth RFCs required by the MCP authorization specification across major identity providers.
| Identity Provider | PKCE | RFC 8414 | RFC 7591 | RFC 8707 |
|---|---|---|---|---|
| Okta | Yes | Yes | Yes | N0 |
| Auth0 | Yes | Yes | Kinda | No |
| Keycloak | Yes | Yes | Yes | No |
| Ping Federate | Yes | Yes | Yes | Yes |
| ForgeRock | Yes | Yes | Yes | Kinda |
| Google OAuth | Yes | No | No | No |
| Microsoft Entra | Yes | Yes | No | No |
The project shows how to build a secure MCP server with:
http-transport-steps/src/mcp_http/step1.py/health)http-transport-steps/src/mcp_http/step2.py/mcp endpoint for MCP protocol communicationhttp-transport-steps/src/mcp_http/step3.pyecho, get_time)greeting, help)http-transport-steps/src/mcp_http/step4.pyhttp-transport-steps/src/mcp_http/step5.py/.well-known/jwks.json)generate_token.py)http-transport-steps/src/mcp_http/step6.py/mcp endpointhttp-transport-steps/src/mcp_http/step7.py/.well-known/oauth-protected-resource endpoint/.well-known/oauth-authorization-server endpointhttp-transport-steps/src/mcp_http/step8.pycheck_permission method for scope validationThe JWT tokens include:
mcp:read, mcp:tools, mcp:prompts)admin, user, guest)Each step includes a corresponding test script (test_stepX.sh) that validates:
uv: https://docs.astral.sh/uv/getting-started/installation/http-transport-steps directory# Run any step using uv run uv run step1 uv run step2 uv run step3 # ... etc
Step 10 supports environment-based configuration for Keycloak and MCP server URLs. You can specify an env file (not .env) using the --env flag, or let it default to keycloak_direct.env.
Two example env files are provided:
keycloak_direct.env (for direct Keycloak access at localhost:8080)keycloak_proxy.env (for proxy access at localhost:9090)Example usage:
# Run step 10 with a specific env file (e.g., proxy) uv run step10 --env keycloak_proxy.env
If the env file or environment variables are missing, the server will fall back to sensible defaults (localhost:8080, etc).
uv run step11
To run with mcp-inspector
mcp scopes issue: https://github.com/modelcontextprotocol/inspector/issues/587
For steps 5-8 that require JWT authentication, you can generate tokens using the generate_token.py script:
uv run python generate_token.py --username alice --scopes mcp:read,mcp:tools uv run python generate_token.py --username bob --scopes mcp:read,mcp:prompts uv run python generate_token.py --username admin --scopes mcp:read,mcp:tools,mcp:prompts uv run python generate_token.py --username guest --scopes ""
To quickly get a token for testing step9/keycloak:
curl -X POST "http://localhost:8080/realms/mcp-realm/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password" \ -d "client_id=mcp-test-client" \ -d "username=mcp-admin" \ -d "password=admin123" \ -d "scope=openid profile email mcp:read mcp:tools mcp:prompts" | jq -r '.access_token'
The script will output a JWT token that can be used in the Authorization: Bearer <token> header for authenticated requests.
The project uses uv for dependency management with pyproject.toml configuration.