A Model Context Protocol (MCP) server that wraps the JSONPlaceholder users API, protected by Auth0 browser-based authentication.
| Tool | Description | Parameters |
|---|---|---|
list_users |
Get all users | — |
get_user |
Get a user by ID | id (number) |
search_users_by_username |
Find users by username | username (string) |
search_users_by_email |
Find users by email | email (string) |
The server uses Auth0 as the authorization server. When an MCP client (Claude Desktop, VS Code) first connects, it detects that the server requires authentication, opens the user's browser for an Auth0 login, and then uses the resulting token on every subsequent request.
Sign up at auth0.com if you do not already have an account. Note your tenant domain (shown in the top-left of the Auth0 dashboard), e.g. my-tenant.us.auth0.com.
- In the Auth0 dashboard go to Applications → APIs → Create API.
- Fill in:
- Name:
MCP Users Server(or any label you like) - Identifier (audience): a URI that uniquely identifies the API, e.g.
https://mcp-users-server/. This does not have to be a real URL. - Signing Algorithm:
RS256(the default — required for JWT verification via JWKS)
- Name:
- Click Create. Copy the Identifier — you will use it as
AUTH0_AUDIENCE.
MCP clients authenticate using the Authorization Code flow with PKCE (Proof Key for Code Exchange). This is Auth0's default for SPAs and native apps, so no extra configuration is required. The clients detect support by reading code_challenge_methods_supported from Auth0's discovery document.
Auth0 validates redirect_uri by exact match. For mcp-remote, the browser callback path is /oauth/callback, so you should pin Claude Desktop to a fixed localhost port and add that exact URL to your Auth0 application.
- In the Auth0 dashboard open Applications → Applications.
- Open the Auth0 application whose Client ID you use in Claude Desktop.
- Under Application URIs → Allowed Callback URLs add
http://localhost:3334/oauth/callback. - Save changes.
If you prefer a different port, use that same port both in Auth0 and in the mcp-remote arguments below.
Copy .env.example to .env and fill in your values:
cp .env.example .envAUTH0_DOMAIN=my-tenant.us.auth0.com # your tenant domain (no https://)
AUTH0_AUDIENCE=https://mcp-users-server/ # the API Identifier from Step 2
PORT=3000
SERVER_URL=http://localhost:3000Never commit
.envto source control. It is already listed in.gitignore.
The server loads .env automatically at startup. It checks these locations in order:
- The path in
MCP_ENV_FILE, if you set it. .envin the current working directory..envin the project root relative to the builtdistfiles.
This is important for Claude Desktop because it launches the server as a child process and may not provide the same shell environment you see in a terminal.
MCP Client This Server Auth0
(Claude Desktop / VS Code) (Express + MCP SDK) (Authorization Server)
│ │ │
│── POST /mcp ───────────────▶│ │
│ │ 401 Unauthorized │
│◀── WWW-Authenticate ────────│ (resource_metadata │
│ resource_metadata=URL │ points to this server)│
│ │ │
│── GET /.well-known/ ───────▶│ │
│ oauth-protected-resource │ 200 JSON │
│◀────────────────────────────│ (points to Auth0 │
│ │ as auth server) │
│ │ │
│── GET /.well-known/ ───────────────────────────────▶│
│ openid-configuration │ │ 200 JSON
│◀──────────────────────────────────────────────────── │
│ │ │
│ [browser opens] │ │
│── Authorization request ──────────────────────────▶│
│ + PKCE code_challenge │ │ Login page
│◀── redirect with code ──────────────────────────────│
│ │ │
│── POST /oauth/token ──────────────────────────────▶│
│ code + code_verifier │ │ JWT access token
│◀── access_token (JWT) ──────────────────────────────│
│ │ │
│── POST /mcp ───────────────▶│ │
│ Authorization: Bearer JWT │ verify JWT via JWKS │
│ │────────────────────────▶│
│ │◀─ public keys (cached) ─│
│ │ signature ✓ / exp ✓ │
│◀── MCP response ────────────│ │
fetchAuth0OAuthMetadata() — Called once at server startup. Fetches Auth0's OIDC discovery document (/.well-known/openid-configuration) and maps it to the OAuthMetadata shape the MCP SDK expects. This data is served by mcpAuthMetadataRouter so clients can discover Auth0 automatically.
createAuth0TokenVerifier() — Returns an OAuthTokenVerifier whose verifyAccessToken() method validates an Auth0 JWT using jose:
- Fetches (and caches) Auth0's public signing keys from
/.well-known/jwks.json. - Verifies the token signature,
iss(issuer),aud(audience), andexp(expiry). - Returns an
AuthInfoobject (clientId,scopes,expiresAt) that the MCP SDK attaches to the request.
mcpAuthMetadataRouter — Mounts GET /.well-known/oauth-protected-resource. This endpoint tells MCP clients that the server is OAuth-protected and points them to Auth0 as the authorization server. Without this, clients would not know how to authenticate.
requireBearerAuth — Express middleware placed in front of every /mcp route. It calls verifyAccessToken() on the Authorization: Bearer token, returns 401 if the token is missing or invalid, and attaches the decoded AuthInfo to req.auth if valid.
Session management — Each authenticated client gets a StreamableHTTPServerTransport + McpServer pair keyed by a UUID session ID. The session ID is returned in the Mcp-Session-Id response header on the first POST and must be echoed back by the client on all subsequent requests. Sessions are removed when the client sends DELETE /mcp or disconnects.
- Node.js 18+
- An Auth0 tenant configured as described above
npm install
npm run build
# Copy and fill in environment variables
cp .env.example .env
# edit .env with your AUTH0_DOMAIN and AUTH0_AUDIENCE
npm startThe server prints three URLs on startup:
MCP server listening on http://localhost:3000
MCP endpoint: http://localhost:3000/mcp
OAuth metadata: http://localhost:3000/.well-known/oauth-protected-resource
Claude Desktop expects command-style MCP entries. Use mcp-remote as a bridge to this HTTP MCP server.
- Keep this server running:
npm start- Edit
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"users": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://localhost:3000/mcp",
"3334",
"--host",
"localhost",
"--static-oauth-client-info",
"{\"client_id\":\"YOUR_AUTH0_APP_CLIENT_ID\"}",
"--debug"
]
}
}
}YOUR_AUTH0_APP_CLIENT_ID is the Auth0 Application Client ID for a pre-registered app (typically a Native application with PKCE).
With the config above, the exact redirect URI sent to Auth0 is http://localhost:3334/oauth/callback, which must appear in the Auth0 application's Allowed Callback URLs.
Why this is required: many Auth0 tenants disable Dynamic Client Registration (DCR). In that case mcp-remote fails during registerClient unless static client info is supplied.
- Restart Claude Desktop.
On the first use it will open your browser to Auth0 for login. After you authenticate, Claude Desktop stores the token and uses it automatically for all subsequent sessions.
Add the following to your VS Code settings.json (or workspace settings):
{
"mcp": {
"servers": {
"users": {
"type": "http",
"url": "http://localhost:3000/mcp"
}
}
}
}VS Code will similarly prompt for browser authentication on first use.
src/
auth.ts Auth0 JWT verification and OAuth metadata helpers
index.ts Express HTTP server, MCP routes, session management
.env.example Environment variable template
SPEC.md Feature specification
INSTALL.md Legacy stdio installation notes