identity-aware-mcp-server
README.md
# Identity-Aware MCP Server — Companion Project
A production-ready reference implementation of an MCP server that requires
OAuth 2.1 Bearer tokens, validates them against a JWKS endpoint, enforces
tool-level scopes and roles, and logs every access decision with the full
delegation chain.
**Companion project for:** "Building an Identity-Aware MCP Server" (DZone, June 2026)
## What this server does
- **Authenticates every agent** — no anonymous or shared-key access
- **Validates JWTs** using stateless JWKS-based validation (no DB lookups)
- **Enforces tool-level authorization** — each tool declares required scopes and roles
- **Maintains a tool allow-list** — infrastructure-level enforcement (not prompt-level)
- **Logs the full delegation chain** — every action traces back to a human identity
- **Serves MCP discovery endpoints** — clients auto-discover auth configuration
## Architecture
```
Agent (Bearer Token)
│
▼
┌─────────────────────────────┐
│ 1. Token Validation (JWKS) │ ← Is this a valid token?
├─────────────────────────────┤
│ 2. Tool Allow-List Gate │ ← Is this tool allowed at all?
├─────────────────────────────┤
│ 3. Scope + Role Check │ ← Can THIS agent call THIS tool?
├─────────────────────────────┤
│ 4. Tool Handler │ ← Execute the actual operation
├─────────────────────────────┤
│ 5. Audit Log │ ← Who did what, on whose behalf?
└─────────────────────────────┘
```
## Requirements
- Python 3.12+
- An OIDC-compatible identity provider (Auth0 free tier works)
## Quick Start
```bash
# 1. Clone and set up
cd code
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 2. Configure (edit with your Auth0/Okta details)
cp .env.example .env
# 3. Run the server
python src/server.py
# 4. In another terminal — run the demo agent
python demo/agent.py --demo
```
## Demo Mode
The `--demo` flag generates self-signed tokens locally so you can test
the full flow without configuring an OAuth provider:
```
python demo/agent.py --demo
```
This runs three simulated agents:
- **Alice** (developer, `database.read + email.send` scopes) → can read, cannot write
- **Bob** (admin, full scopes) → can do everything
- **Carol** (contractor, zero scopes) → blocked from everything
## Testing
```bash
python -m pytest tests/ -v
```
Expected output:
```
tests/test_server.py::test_protected_resource_endpoint PASSED
tests/test_server.py::test_authorization_server_endpoint PASSED
tests/test_server.py::test_no_token_returns_401 PASSED
tests/test_server.py::test_invalid_token_returns_401 PASSED
tests/test_server.py::test_expired_token_returns_401 PASSED
tests/test_server.py::test_valid_token_read_customer_succeeds PASSED
tests/test_server.py::test_missing_scope_returns_403 PASSED
tests/test_server.py::test_write_scope_required_for_update PASSED
tests/test_server.py::test_missing_role_returns_403 PASSED
tests/test_server.py::test_admin_role_succeeds PASSED
tests/test_server.py::test_unknown_tool_returns_404 PASSED
tests/test_server.py::test_audit_log_entries PASSED
```
## Key Files
| File | Purpose |
|------|---------|
| `src/server.py` | Main MCP server — configuration, routing, auth enforcement |
| `src/auth/middleware.py` | JWKS cache, `TokenValidator`, `require_auth` decorator |
| `src/auth/discovery.py` | OAuth 2.1 discovery endpoints (RFC 8414 + RFC 9728) |
| `src/tools/database.py` | Example tools with scope and role declarations |
| `src/audit/logger.py` | JSON Lines audit log with delegation-chain tracing |
| `demo/agent.py` | Demo agent that connects with scoped tokens |
## Production Hardening
Before deploying:
- [ ] Use a real OAuth 2.1 provider (Auth0, Okta, Entra ID) — not demo keys
- [ ] Enforce HTTPS only — reject HTTP at the network level
- [ ] Set short token lifetimes (15-60 minutes) with refresh token rotation
- [ ] Sanitize Bearer tokens from request logs
- [ ] Run behind a reverse proxy with rate limiting
- [ ] Use PostgreSQL-backed token storage for multi-instance deployments
## Article Reference
- **Title:** Building an Identity-Aware MCP Server
- **Publication:** DZone
- **Date:** June 2026
- **Author:** Pravin Khandke
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues