Skip to content

kordian-smartbear/mcp_server_with_auth_example

Repository files navigation

MCP Weather Server with OAuth 2.1 Authorization

A reference implementation of a cloud-ready MCP server with full OAuth 2.1 authorization, using the Streamable HTTP transport. The server exposes simple weather tools behind authentication and works with multiple identity providers out of the box.

Features

  • MCP spec-compliant auth — serves Protected Resource Metadata (RFC 9728), returns proper WWW-Authenticate challenges, validates bearer tokens.
  • Multi-provider — preconfigured for Keycloak, Google Identity, and Atlassian (or bring any OIDC-compliant provider via generic mode).
  • Streamable HTTP transport — the recommended remote MCP transport with session management.
  • Cloud-ready — includes Dockerfile + docker-compose for one-command local testing.
  • Simple demo toolsget_weather, get_forecast, get_alerts (swap with real APIs).

Architecture

┌─────────────┐     OAuth 2.1      ┌───────────────────┐
│  MCP Client │ ◄──── flow ────►   │  Identity Provider│
│  (VS Code,  │                    │  (Keycloak/Google/ │
│   Claude,   │                    │   Atlassian/etc.)  │
│   custom)   │                    └───────────────────┘
└──────┬──────┘                              │
       │ Bearer token                        │ Token
       ▼                                     │ introspection
┌──────────────────────────────────────────────────────┐
│                 MCP Weather Server                    │
│                                                      │
│  /.well-known/oauth-protected-resource   (public)    │
│  POST /    — Streamable HTTP MCP endpoint (auth)     │
│  GET  /    — SSE notifications            (auth)     │
│  DELETE /  — Session teardown             (auth)     │
│  GET /health                              (public)   │
└──────────────────────────────────────────────────────┘

Quick Start (Keycloak)

This is the fastest way to see everything working end-to-end.

1. Prerequisites

  • Node.js ≥ 20
  • Docker Desktop (for Keycloak)

2. Start Keycloak

docker run -p 127.0.0.1:8080:8080 \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
  -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak start-dev

Open http://localhost:8080 and log in with admin / admin.

3. Configure Keycloak

Create the mcp:tools scope:

  1. Go to Client scopesCreate client scope.
  2. Name: mcp:tools, Type: Default, toggle Include in token scope ON.
  3. Inside the scope, go to MappersConfigure a new mapperAudience.
  4. Name: audience-config, Included Custom Audience: http://localhost:3000.

Allow dynamic client registration:

  1. Go to ClientsClient registrationTrusted Hosts.
  2. Disable Client URIs Must Match, add your host IP (check Keycloak logs for the IP).

Create a server client (for token introspection):

  1. Go to ClientsCreate client.
  2. Client ID: mcp-server, click Next.
  3. Enable Client authentication, click NextSave.
  4. Go to Credentials tab, copy the Client Secret.

4. Run the MCP Server

# Clone / extract the project
cd mcp-weather-oauth

# Install dependencies
npm install

# Create .env from the Keycloak template
cp .env.keycloak .env
# Edit .env — paste your OAUTH_CLIENT_SECRET

# Build and start
npm run build
npm start

You should see:

┌──────────────────────────────────────────────────┐
│  MCP Weather Server with OAuth 2.1               │
├──────────────────────────────────────────────────┤
│  URL:       http://localhost:3000
│  Provider:  keycloak
│  Issuer:    http://localhost:8080/realms/master/
│  Metadata:  http://localhost:3000/.well-known/oauth-protected-resource
└──────────────────────────────────────────────────┘

5. Connect from VS Code

  1. Press Cmd+Shift+PMCP: Add server…HTTP.
  2. Enter http://localhost:3000.
  3. Give it a name (e.g. weather).
  4. VS Code will open Keycloak login in your browser — log in and grant access.
  5. You should see the weather tools listed in the MCP panel.

Using Google Identity

1. Create OAuth Credentials

  1. Go to Google Cloud Console → Credentials.
  2. Create OAuth Client ID → type Web application.
  3. Add http://localhost:3000 to Authorized redirect URIs.
  4. Copy the Client ID and Secret.

2. Configure

cp .env.google .env
# Edit .env with your Google Client ID and Secret

3. Run

npm run build && npm start

The server will advertise Google's authorization endpoints in its Protected Resource Metadata. When an MCP client connects, it will be redirected to Google's consent screen.


Using Atlassian Identity

1. Create an OAuth 2.0 (3LO) App

  1. Go to Atlassian Developer Console.
  2. Create a new app → OAuth 2.0 (3LO).
  3. Set callback URL: http://localhost:3000/callback.
  4. Copy Client ID and Secret.

2. Configure

cp .env.atlassian .env
# Edit .env with your Atlassian credentials

3. Run

npm run build && npm start

Atlassian doesn't provide a token introspection endpoint, so the server falls back to local JWT validation (decoding + expiry + audience check).


Using Any OIDC Provider (generic mode)

Set OAUTH_PROVIDER=generic and supply all endpoints explicitly:

OAUTH_PROVIDER=generic
OAUTH_ISSUER=https://your-idp.example.com
OAUTH_AUTHORIZATION_ENDPOINT=https://your-idp.example.com/authorize
OAUTH_TOKEN_ENDPOINT=https://your-idp.example.com/token
OAUTH_INTROSPECTION_ENDPOINT=https://your-idp.example.com/introspect
OAUTH_JWKS_URI=https://your-idp.example.com/.well-known/jwks.json
OAUTH_CLIENT_ID=your-client-id
OAUTH_CLIENT_SECRET=your-client-secret

Cloud Deployment

Docker

docker build -t mcp-weather-oauth .
docker run -p 3000:3000 --env-file .env mcp-weather-oauth

Docker Compose (with Keycloak)

# Make sure .env exists with your config
docker compose up

Deploy to a Cloud Platform

The Dockerfile is a standard multi-stage Node.js build. Deploy to any container service:

  • AWS ECS / Fargate — push the image to ECR, create a task definition.
  • Google Cloud Rungcloud run deploy --source .
  • Azure Container Appsaz containerapp up --source .
  • Fly.iofly launch

Remember to:

  1. Set SERVER_URL to your public URL (e.g. https://mcp-weather.fly.dev).
  2. Set HOST=0.0.0.0 so the server binds to all interfaces.
  3. Update the Keycloak audience / redirect URIs to match the public URL.
  4. Use HTTPS in production — never accept tokens over plain HTTP.

Project Structure

mcp-weather-oauth/
├── src/
│   ├── index.ts           # Express app, MCP server, route wiring
│   ├── config.ts          # Env-based config with provider presets
│   └── token-verifier.ts  # Token validation (introspection + JWT)
├── .env.example           # Generic env template
├── .env.keycloak          # Keycloak-specific template
├── .env.google            # Google Identity template
├── .env.atlassian         # Atlassian template
├── Dockerfile             # Multi-stage production build
├── docker-compose.yml     # Local dev: Keycloak + server
├── tsconfig.json
├── package.json
└── README.md

How the Auth Flow Works

The MCP authorization flow follows OAuth 2.1 (authorization code + PKCE):

  1. Client connects → Server returns 401 Unauthorized with a WWW-Authenticate header pointing to /.well-known/oauth-protected-resource.
  2. Client fetches PRM (Protected Resource Metadata) → learns which authorization server to use and what scopes are available.
  3. Client discovers auth server → fetches the OIDC/OAuth metadata from the authorization server.
  4. Client registers (if DCR is supported) or uses pre-registered credentials.
  5. User authenticates → browser-based login + consent at the identity provider.
  6. Client gets tokens → authorization code exchanged for access + refresh tokens.
  7. Client calls MCP → sends the access token in Authorization: Bearer <token>.
  8. Server validates → introspection or JWT verification, audience check.

MCP Tools

Tool Description
get_weather Current weather for a city (temp, condition, humidity, wind)
get_forecast 3-day forecast
get_alerts Active weather alerts

The weather data is simulated (deterministic based on city name). Replace simulateWeather() with calls to a real API like OpenWeatherMap for production use.

Environment Variables Reference

Variable Default Description
OAUTH_PROVIDER keycloak Provider preset: keycloak, google, atlassian, generic
HOST localhost Server bind address
PORT 3000 Server port
SERVER_URL http://{HOST}:{PORT} Public-facing URL (set for cloud deploys)
AUTH_HOST localhost Keycloak host
AUTH_PORT 8080 Keycloak port
AUTH_REALM master Keycloak realm
OAUTH_CLIENT_ID mcp-server OAuth client ID for introspection
OAUTH_CLIENT_SECRET OAuth client secret
OAUTH_SCOPES mcp:tools Comma-separated scopes
OAUTH_ISSUER (generic) Token issuer URL
OAUTH_AUTHORIZATION_ENDPOINT (generic) Authorization URL
OAUTH_TOKEN_ENDPOINT (generic) Token URL
OAUTH_INTROSPECTION_ENDPOINT (generic) Introspection URL
OAUTH_JWKS_URI (generic) JWKS endpoint

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors