Skip to content

Quickstart — 5 Minutes to Working MCP Auth

  • Docker (for Authgent)
  • An MCP server (Python/FastMCP, TypeScript, or Go)
  • Optional: PostgreSQL (SQLite used by default for dev)
Terminal window
# Generate an EC P-256 key pair for JWT signing
openssl ecparam -genkey -name prime256v1 -noout -out ec-private.pem
openssl ec -in ec-private.pem -pubout -out ec-public.pem
# Store securely — this signs all your tokens
chmod 600 ec-private.pem
Terminal window
docker run -d \
--name authgent \
-p 8080:8080 \
-v $(pwd)/ec-private.pem:/keys/ec-private.pem:ro \
-e AUTHGENT_ISSUER=http://localhost:8080 \
-e AUTHGENT_SIGNING_KEY=/keys/ec-private.pem \
-e AUTHGENT_SIGNING_ALG=ES256 \
-e AUTHGENT_DB_DSN=sqlite:///data/authgent.db \
authgent/authgent:latest

Verify it’s running:

Terminal window
curl http://localhost:8080/.well-known/oauth-authorization-server | jq .

Expected output:

{
"issuer": "http://localhost:8080",
"authorization_endpoint": "http://localhost:8080/oauth/authorize",
"token_endpoint": "http://localhost:8080/oauth/token",
"registration_endpoint": "http://localhost:8080/oauth/register",
"jwks_uri": "http://localhost:8080/.well-known/jwks.json",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "client_credentials", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "private_key_jwt"]
}
# Install: pip install fastmcp
from fastmcp import FastMCP
from fastmcp.server.auth import RemoteAuthProvider
auth = RemoteAuthProvider(
authorization_server_url="http://localhost:8080",
audience="http://localhost:8000",
required_scopes=["mcp:read"],
)
mcp = FastMCP(name="My Server", auth=auth)
@mcp.tool()
async def hello(name: str) -> str:
"""Say hello. Requires mcp:read scope."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="http", host="0.0.0.0", port=8000, path="/mcp")
// Install: npm install @modelcontextprotocol/sdk jose express
import { createRemoteJWKSet, jwtVerify } from "jose";
import express from "express";
const JWKS = createRemoteJWKSet(
new URL("http://localhost:8080/.well-known/jwks.json")
);
const app = express();
// Protected Resource Metadata — MCP clients discover auth requirements here
app.get("/.well-known/oauth-protected-resource", (req, res) => {
res.json({
resource: "http://localhost:8000",
authorization_servers: ["http://localhost:8080"],
});
});
// Auth middleware
app.use("/mcp", async (req, res, next) => {
const token = req.headers.authorization?.slice(7);
if (!token) return res.status(401).json({ error: "missing_token" });
try {
const { payload } = await jwtVerify(token, JWKS, {
issuer: "http://localhost:8080",
audience: "http://localhost:8000",
});
(req as any).auth = payload;
next();
} catch {
res.status(401).json({ error: "invalid_token" });
}
});
// go get github.com/authgent/authgent-go
import (
"github.com/authgent/authgent-go/verifier"
"net/http"
)
v, _ := verifier.New(verifier.Config{
Issuer: "http://localhost:8080",
Audience: "http://localhost:8000",
JWKSURL: "http://localhost:8080/.well-known/jwks.json",
})
mux := http.NewServeMux()
mux.Handle("/mcp", v.RequireAuth()(yourMCPHandler))
http.ListenAndServe(":8000", mux)

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
"mcpServers": {
"my-server": {
"url": "http://localhost:8000/mcp",
"transport": "http"
}
}
}

Claude will detect the /.well-known/oauth-protected-resource response, redirect to Authgent’s authorization endpoint, and complete the OAuth flow automatically.

In Cursor settings → MCP → Add server:

  • URL: http://localhost:8000/mcp
  • Auth: OAuth (auto-detected)
Terminal window
npx @modelcontextprotocol/inspector http://localhost:8000/mcp

The inspector handles the OAuth flow automatically and shows all available tools.

After a successful auth flow, Authgent issues JWTs like this:

{
"iss": "http://localhost:8080",
"sub": "user_abc123",
"aud": "http://localhost:8000",
"exp": 1700000300,
"iat": 1700000000,
"jti": "tok_xyz",
"scope": "mcp:read",
"email": "user@yourcompany.com",
"name": "Alice"
}

Decode any token at jwt.io or:

Terminal window
echo "YOUR_TOKEN" | cut -d. -f2 | base64 -d | jq .