For full documentation, see the Documentation Index.
Time: ~5 minutes. Prereqs: Docker. Outcome: authserver running locally, an admin user, a registered resource server, a working discovery endpoint.
Get a working MCP authorization server in under 10 minutes.
- Docker (any recent version)
docker pull authplane/authserver:latestThat's the published, signed release image — the path 99% of users want. Use it everywhere the steps below reference authserver:latest.
Alternative: build from a local checkout
Only needed if you're contributing to authserver itself or testing an unreleased change:
git clone https://github.com/authplane/authserver.git
cd authserver
docker build -t authserver:local -f build/Dockerfile .Then substitute authserver:local for authplane/authserver:latest in the docker run command below.
Generate an admin API key first — you'll use it to call the admin API and log in to the Admin UI:
export AUTHPLANE_ADMIN_API_KEY="$(openssl rand -hex 32)"cat > config.yaml << EOF
server:
issuer: "http://localhost:9000"
address: ":9000"
storage:
driver: sqlite
sqlite:
path: "/data/authserver.db"
wal: true
signing:
algorithm: ES256
key_path: "/data/keys"
dcr:
mode: open
admin:
enabled: true
address: ":9001"
api_key: "${AUTHPLANE_ADMIN_API_KEY}"
# Grants are off by default. Enable the ones your clients will use.
# (See docs/concepts/resources-and-scopes.md for which grant fits which use case.)
client_credentials:
enabled: true # machine-to-machine — the simplest MCP-server path
token_exchange:
enabled: true # RFC 8693 — needed for agent-to-agent delegation and Broker upstreams
max_chain_depth: 5
token_expiry: 1h
EOFThe config file deliberately does NOT preseed any resources. Step 5 below
registers your first resource via the admin CLI / API — that's the path
you'll use day-to-day. If you want to commit a resource to config (for
reproducible boots) the syntax is documented in
docs/reference/configuration.md, but
the admin path is what the docs walk through.
docker run -d --name authserver \
-v $(pwd)/config.yaml:/config.yaml:ro \
-v authserver-data:/data \
-p 9000:9000 \
-p 9001:9001 \
authplane/authserver:latest serve --config /config.yamldocker exec authserver /authserver --config /config.yaml admin user create \
--email admin@example.com \
--password changeme \
--role adminScopes are declared on the resource server they belong to. Register one via the CLI:
docker exec authserver /authserver --config /config.yaml admin resource create \
--slug my-mcp-server \
--backend-kind mint \
--uri "http://localhost:8080/mcp" \
--scopes 'mcp:echo||Echo a message' \
--scopes 'mcp:add||Add two numbers'The --scopes flag is repeatable. Each value is a name|upstream|description
tuple — for Mint resources the middle (upstream) part is empty, so the form
is name||description (double pipe).
The Resource
uribecomes the JWTaudclaim byte-for-byte. It must match the URL your MCP server actually serves (path included). Mismatches cause an opaqueinvalid_tokenfrom the resource server. See Connect an MCP Server § What can go wrong for the canonical-form rule.
Or via the admin API:
curl -X POST http://localhost:9001/admin/resources \
-H "Authorization: Bearer $AUTHPLANE_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"slug": "my-mcp-server",
"display_name": "My MCP server",
"uri": "http://localhost:8080/mcp",
"backend_kind": "mint",
"scopes": [
{"name": "mcp:echo", "description": "Echo a message"},
{"name": "mcp:add", "description": "Add two numbers"}
]
}'Open http://localhost:9001/admin/ui/ in your browser. Paste the value of $AUTHPLANE_ADMIN_API_KEY (from step 2) to log in. The dashboard shows server stats, registered clients, users, and scopes. You can manage all administrative operations from the web UI instead of using curl commands.
curl -s http://localhost:9000/.well-known/oauth-authorization-server | jq .The response is the OAuth 2.0 Authorization Server Metadata (RFC 8414). Spot-check three things:
issuermatcheshttp://localhost:9000.grant_types_supportedcontainsclient_credentialsandurn:ietf:params:oauth:grant-type:token-exchange— proof that the grant enables inconfig.yamltook effect. If you only see["authorization_code", "refresh_token"], the flags didn't apply: re-checkclient_credentials.enabledandtoken_exchange.enabledand restart the container.scopes_supportedcontains the scopes you declared on the resource (mcp:echo,mcp:add).
curl -s http://localhost:9000/.well-known/oauth-authorization-server \
| jq '{issuer, grant_types_supported, scopes_supported}'See the examples/ directory for complete working examples:
- Go MCP server — Minimal Go server using the Resource Server SDK
- Go agent — Client credentials, DPoP, introspection using the Auth SDK
- TypeScript MCP server — Express server using the TypeScript Resource Server SDK
- Python MCP server — FastMCP server using
authplane-fastmcp(Python 3.12+) - Tier-03 Go DPoP + scopes — Full-featured DPoP-bound, scope-enforcing example
Each example includes setup instructions. The Docker Compose examples start authserver alongside the MCP server.
The default config is for local development. Before deploying to production:
- Set the issuer URL to your public HTTPS URL (
server.issuer: https://auth.example.com) - Set a session secret (
session.secretorAUTHPLANE_SESSION_SECRETenv var) - Set an admin API key (
admin.api_keyorAUTHPLANE_ADMIN_API_KEYenv var) - Enable secure cookies (
session.secure: true— enforced automatically for non-localhost issuers) - Consider PostgreSQL for multi-instance deployments (
storage.driver: postgres)
See ../guides/deploy/configuration.md for operator-facing configuration prose, and ../reference/configuration.md for the field-by-field schema.
- 03 — Your first MCP server — drop in the Resource Server SDK
- 04 — Calling another resource from your MCP server — your MCP server acting as a client of a second resource
- Admin UI tour — manage clients, users, scopes from the web dashboard
- Admin CLI & API — scripted management for CI/CD and automation
- Connect an MCP server — point your MCP server at authserver
- Configuration reference — all config options with defaults
- OIDC federation — connect to Okta, Entra ID, or Google Workspace
- Deployment guide — production Docker Compose setup
- Architecture overview — how authserver works internally
- Upstream Connections guide — connect upstream OAuth providers (GitHub, Slack, etc.) and vend tokens via RFC 8693 token exchange
- SDKs — Go (go-sdk), TypeScript (ts-sdk), Python (python-sdk)