MCP • OAuth • bearer • device • client credentials

MCP auth surfaces,

without guesswork

This project gives you two things: a FastAPI server with intentionally separate MCP auth surfaces, and a standalone mcp-auth CLI that can discover and exercise those surfaces end to end.

One OAuth resource

/mcp/oauth accepts OAuth-issued bearer tokens from auth-code, device, and client-credentials grants, while keeping resource, audience, issuer, and PKCE behavior visible.

One static bearer surface

/mcp/bearer-token is deliberately simpler and does not participate in discovery or token issuance, which makes bearer-only client behavior easy to isolate.

One generic client

The CLI is resource-centric, not server-specific. It discovers metadata, selects an auth mode, saves profiles, and keeps tokens valid across dynamic registration and seeded CIMD fixture clients.

The mounted app also exposes read-only /debug/* endpoints, a tool-policy model with scope declarations and argument validation, and seeded

dev-* fixture clients that let you exercise common flows without a registration step.

Architecture at a glance

flowchart TD
    classDef primary fill:#0f172a,stroke:#93c5fd,color:#e5e7eb,stroke-width:1.5px;
    classDef secondary fill:#111827,stroke:#7dd3fc,color:#e5e7eb,stroke-width:1.5px;
    classDef decision fill:#1e293b,stroke:#fcd34d,color:#e5e7eb,stroke-width:1.5px;
    classDef tertiary fill:#172554,stroke:#a5b4fc,color:#e5e7eb,stroke-width:1.5px;
    classDef success fill:#1f2937,stroke:#86efac,color:#e5e7eb,stroke-width:1.5px;
    classDef failure fill:#3f1d2e,stroke:#fda4af,color:#e5e7eb,stroke-width:1.5px;

    CLI["mcp-auth CLI"]:::secondary
    APP["app.py\nFastAPI entrypoint"]:::primary
    DOCS["/docs, /redoc,\n/docs/oauth-callback"]:::tertiary

    subgraph MR["Mounted routers"]
        direction TB
        BEARER["/mcp/bearer-token\n/test-auth/bearer-token/mint"]:::primary
        OAUTH["/mcp/oauth\n/oauth/*"]:::primary
        DCR["/oauth/register"]:::secondary
        DISC["/.well-known/*"]:::secondary
        DEBUG["/debug/*"]:::secondary
    end

    subgraph CORE["Auth and policy core"]
        direction TB
        AUTH["auth/*.py"]:::tertiary
        POLICY["mcp/policy.py\nmcp/tools.py"]:::tertiary
        STORE[("InMemoryTokenStore")]:::success
    end

    ALT["Source-defined alternate routers:\noauth_v2_2l.py, device_flow.py, oauth_v21.py"]:::decision

    CLI --> DISC
    CLI --> OAUTH
    APP --> BEARER
    APP --> OAUTH
    APP --> DCR
    APP --> DISC
    APP --> DEBUG
    APP --> DOCS
    BEARER --> AUTH
    OAUTH --> AUTH
    OAUTH --> POLICY
    DCR --> AUTH
    AUTH --> STORE
    POLICY --> STORE
    DEBUG --> STORE
    ALT --> AUTH

Quick start

uv sync --dev
uv run uvicorn mcp_auth_test_server.app:app --reload --port 8765

In a second terminal:

uv run mcp-auth discover http://127.0.0.1:8765/mcp/oauth
uv run mcp-auth login http://127.0.0.1:8765/mcp/oauth
uv run mcp-auth call http://127.0.0.1:8765/mcp/oauth initialize
Auth-code default behavior

When you run mcp-auth login --auth-mode auth-code, the CLI starts a localhost callback listener, prints the consent URL, and waits for the browser redirect in the background.

What the docs site covers

  • Flow walkthroughs with exact CLI commands for each auth mode
  • Reference for endpoints, discovery documents, policy behavior, CIMD fixture clients, and debug routes
  • Enough context to verify whether a client implementation is doing the right thing

If you need the endpoint inventory or want the policy/debug details in one place, start with the reference page.

Why this exists

Most auth test fixtures blur different schemes together. This one does the opposite: each auth surface stays explicit, and the shared OAuth resource keeps resource, issuer, PKCE, policy, and refresh behavior visible enough to test against.

Built with SvelteKit + mdsvex. Deployable as a static site on GitHub Pages.