oauth-mcp-server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@oauth-mcp-serverlist the available tools"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
oauth-mcp-server
A production-grade Model Context Protocol (MCP) resource server with OAuth 2.1 authorization. Written in TypeScript with the official @modelcontextprotocol/sdk.
It is provider-agnostic — swap between Auth0, Keycloak, Google, Entra ID, Okta, or any OAuth 2.1-compliant authorization server by changing one configuration file.
Compatible with both MCP specification versions:
2026-07-28 (primary: per-request
_meta,server/discover, stateless auth)2025-11-25 (backward compat:
initializehandshake)
Quick Start
Requires Node.js >= 18. All commands below are PowerShell (Windows). For macOS/Linux, replace
$env:VAR = "value"withVAR=value.
# 1. Install dependencies
npm install
# 2. Build the TypeScript
npm run build
# 3. Start the server on port 6000 (dev mode — no OAuth required)
$env:PORT = "6000"; $env:MCP_NO_AUTH = "true"; npm run start:httpVerify it's running:
# Health check
curl http://localhost:6000/health
# Protected Resource Metadata (RFC 9728)
curl http://localhost:6000/.well-known/oauth-protected-resourceExpected output:
{"status":"ok","server":"oauth-mcp-server v1.0.0","auth":"disabled (dev mode)","sessions":0}Running on a Custom Port
The server reads the PORT environment variable. The config file uses port 6000 by default. To use a different port, set both:
# Pick any free port
$env:PORT = "8080"; npm run start:httpIf you change the port, also update these fields in your config file to match (config/default.json):
{
"baseUrl": "http://localhost:8080",
"authorization": {
"validation": {
"jwks": {
"uri": "http://localhost:8080/.well-known/jwks.json",
"issuer": "http://localhost:8080"
}
},
"resourceMetadata": {
"authorizationServers": ["http://localhost:8080"],
"resource": "http://localhost:8080"
}
}
}The port appears in 5 places in the config — they must all match the port you actually run on.
Related MCP server: MCP REST API Server
Setup Guide: Local Development
For local development, start with OAuth disabled. This unlocks all capabilities without needing any tokens — perfect for testing the server itself.
# Build and start in dev mode
npm run build
$env:PORT = "6000"; $env:MCP_NO_AUTH = "true"; npm run start:httpVariable | What it does |
| Port to listen on (defaults to 6000) |
| Set to |
| Path to a config JSON file (defaults to |
| Bind address (defaults to |
Testing with curl (no auth)
# Discover server capabilities
$body = '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'
curl -X POST http://localhost:6000/mcp -H "Content-Type: application/json" -H "MCP-Protocol-Version: 2026-07-28" -d $body
# List tools (all 18 available since auth is off)
$body = '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
curl -X POST http://localhost:6000/mcp -H "Content-Type: application/json" -H "MCP-Protocol-Version: 2026-07-28" -d $body
# Call the echo tool
$body = '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"text":"hello world"}}}'
curl -X POST http://localhost:6000/mcp -H "Content-Type: application/json" -H "MCP-Protocol-Version: 2026-07-28" -d $bodyLocal dev with live reload
# stdio transport (no build step, auto-reloads on code changes)
npm run dev:stdio
# HTTP transport, no OAuth, auto-reloads
$env:MCP_NO_AUTH = "true"; npm run dev:httpSetup Guide: Enabling OAuth (Local Testing with a Real Provider)
When you're ready to test the full OAuth flow, point the server at a real authorization server. Here's the step-by-step for Auth0 (the same pattern works for Keycloak, Okta, etc.).
Step 1: Create an API in Auth0
Go to Auth0 Dashboard → Applications → APIs → Create API
Set Name:
MCP ServerSet Identifier:
http://localhost:6000(this becomes youraudience)Set Signing Algorithm:
RS256Click Create
Step 2: Add scopes to the API
In your Auth0 API settings, add these scopes:
mcp:read
mcp:write
mcp:adminStep 3: Create a Machine-to-Machine client
Go to Applications → Create Application
Name it
MCP ClientChoose Machine to Machine
Select the
MCP ServerAPIGrant it
mcp:readandmcp:writescopesNote the Client ID and Client Secret
Step 4: Update the server config
Copy the Auth0 example and fill in your details:
Copy-Item config\auth0.example.json config\production.jsonEdit config/production.json — the three fields you must change:
{
"baseUrl": "http://localhost:6000",
"authorization": {
"validation": {
"jwks": {
"uri": "https://YOUR_DOMAIN.us.auth0.com/.well-known/jwks.json",
"issuer": "https://YOUR_DOMAIN.us.auth0.com/",
"audience": "http://localhost:6000"
}
},
"resourceMetadata": {
"authorizationServers": ["https://YOUR_DOMAIN.us.auth0.com"],
"resource": "http://localhost:6000"
}
}
}Field | Where to find it |
| Auth0 Dashboard → your tenant domain (e.g. |
|
|
|
|
| The API Identifier you set in Step 1 |
Step 5: Start the server with OAuth enabled
# Omit MCP_NO_AUTH — OAuth is now active
$env:PORT = "6000"; $env:MCP_CONFIG_PATH = "config/production.json"; npm run start:httpStep 6: Get a token and test
# Get a token from Auth0 (Client Credentials flow)
$tokenResponse = curl -X POST https://YOUR_DOMAIN.us.auth0.com/oauth/token `
-H "Content-Type: application/json" `
-d '{"client_id":"YOUR_CLIENT_ID","client_secret":"YOUR_CLIENT_SECRET","audience":"http://localhost:6000","grant_type":"client_credentials","scope":"mcp:read"}'
$token = ($tokenResponse | ConvertFrom-Json).access_token
# Now call the server with the token
$body = '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
curl -X POST http://localhost:6000/mcp `
-H "Content-Type: application/json" `
-H "MCP-Protocol-Version: 2026-07-28" `
-H "Authorization: Bearer $token" `
-d $bodyWhat happens with no token (OAuth enabled)
# Without a token, the server returns 401
curl -X POST http://localhost:6000/mcp `
-H "Content-Type: application/json" `
-H "MCP-Protocol-Version: 2026-07-28" `
-d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{}}'
# Response:
# HTTP 401 Unauthorized
# WWW-Authenticate: Bearer resource_metadata="http://localhost:6000/.well-known/oauth-protected-resource", scope="mcp:read"Provider-specific notes
Provider | JWKS URI pattern | Issuer pattern | Notes |
Auth0 |
|
| Trailing slash on issuer |
Keycloak |
|
| No trailing slash |
Okta |
|
| Use the authorization server ID |
Entra ID |
|
| Use tenant ID, not domain |
Setup Guide: Production Deployment
Configuration changes for production
The only differences between local and production config are the URLs — everything else stays the same:
{
"baseUrl": "https://mcp.your-company.com",
"authorization": {
"validation": {
"jwks": {
"uri": "https://YOUR_DOMAIN.us.auth0.com/.well-known/jwks.json",
"issuer": "https://YOUR_DOMAIN.us.auth0.com/",
"audience": "https://mcp.your-company.com"
}
},
"resourceMetadata": {
"authorizationServers": ["https://YOUR_DOMAIN.us.auth0.com"],
"resource": "https://mcp.your-company.com"
}
}
}Config field | Local value | Production value |
|
|
|
| Your provider's JWKS URL | Same provider URL (unchanged) |
| Your provider's issuer | Same (unchanged) |
|
|
|
|
|
|
| Your provider | Same (unchanged) |
Key insight: The
authorizationServersand JWKS/issuer fields point to your OAuth provider (which doesn't change between local and prod). Only thebaseUrl,audience, andresourcefields reflect where this server is running.
Deploying to Google Cloud Run
npm run buildCloud Run sets PORT (usually 8080) and K_SERVICE automatically. The server detects K_SERVICE and binds to 0.0.0.0. Set your config path as an env var in Cloud Run:
MCP_CONFIG_PATH=config/production.jsonA Dockerfile is included for container-based deployments. After deployment:
MCP endpoint:
https://<your-cloud-run-url>/mcpHealth:
https://<your-cloud-run-url>/healthMetadata:
https://<your-cloud-run-url>/.well-known/oauth-protected-resource
Deploying to Vercel
api/
├── health.ts # Health check (no auth)
└── mcp.ts # Stateless Streamable HTTP handler with OAuthDeploy via Vercel Git integration — no build step or framework preset needed. Set these env vars in Vercel:
MCP_CONFIG_PATH=config/production.json
MCP_CORS_ORIGIN=https://your-client.example.comAfter deployment:
MCP endpoint:
https://<project>.vercel.app/api/mcpHealth:
https://<project>.vercel.app/api/healthMetadata:
https://<project>.vercel.app/.well-known/oauth-protected-resource
How OAuth Works on This Server
This server acts as an OAuth 2.1 Resource Server. It never issues tokens — it only validates tokens issued by an authorization server.
The Flow
MCP Client This Server Auth Server
│ │ │
│ POST /mcp (no token) │ │
│ ──────────────────────────────────►│ │
│ │ │
│ HTTP 401 │ │
│ WWW-Authenticate: Bearer │ │
│ resource_metadata="...", │ │
│ scope="mcp:read" │ │
│ ◄──────────────────────────────────│ │
│ │ │
│ [Client discovers auth server, │ │
│ gets a Bearer token via OAuth] │ │
│ ───────────────────────────────────────────────────────────────►│
│ ◄───────────────────────────────────────────────────────────────│
│ │ │
│ POST /mcp │ │
│ Authorization: Bearer <token> │ │
│ MCP-Protocol-Version: 2026-07-28 │ │
│ ──────────────────────────────────►│ │
│ │ Validate JWT signature │
│ │ Check exp, iss, aud │
│ │ Resolve scopes→permissions│
│ │ Filter capabilities │
│ HTTP 200 + MCP response │ │
│ ◄──────────────────────────────────│ │Token Validation Modes
Mode | How it works | When to use |
| Fetches JWKS from your provider, verifies signature locally. Fast, no network call per request after JWKS is cached. | Auth0, Okta, Keycloak — most providers |
| Sends the token to the provider's introspection endpoint on every request. Works with opaque tokens. | When tokens are not JWTs, or you need real-time revocation |
| Tries JWT validation first; falls back to introspection if JWT fails. | When your provider issues both JWT and opaque tokens |
Scope Model
Different OAuth scopes unlock different subsets of capabilities. The default config defines:
Scope | What it unlocks |
(no token) |
|
| Public tools + search documents, query analytics, list projects/files, read resources |
| Everything in read + create/update documents, create/update projects, upload files |
| Everything — including |
| Just |
| Just |
| File system tools |
| Project management tools |
Scopes are additive and resolved in the order listed — if a single token has both mcp:read and mcp:files:write, the user gets the union of both permission sets.
Configuration Reference
Environment Variables
Variable | Default | Description |
|
| HTTP port to listen on |
|
| Bind address ( |
|
| Path to the OAuth config JSON file |
| (not set) | Set to |
| (not set) | A static JWT to use for local testing (bypasses real OAuth flow) |
|
| Max characters logged per request/response body |
|
| CORS origin for Vercel/public deployments |
Config File Structure
The config file (config/default.json or your own) controls everything about the OAuth integration:
{
"name": "oauth-mcp-server",
"version": "1.0.0",
"baseUrl": "http://localhost:6000",
"authorization": {
"validation": {
"mode": "jwt",
"jwks": {
"uri": "https://YOUR_DOMAIN/.well-known/jwks.json",
"cacheTtlSeconds": 3600,
"allowedAlgorithms": ["RS256"],
"issuer": "https://YOUR_DOMAIN/",
"audience": "http://localhost:6000"
}
},
"resourceMetadata": {
"authorizationServers": ["https://YOUR_DOMAIN"],
"scopesSupported": ["mcp:read", "mcp:write", "mcp:admin"],
"bearerMethodsSupported": ["header"],
"resourceSupported": true,
"resource": "http://localhost:6000"
},
"scopeMapping": {
"scopes": {
"mcp:read": {
"tools": ["echo", "calculate", "current_time", "..."],
"resources": ["config://app", "docs://*", "..."],
"prompts": ["summarize", "code_review", "..."]
}
},
"defaultPermissions": {
"tools": ["echo", "current_time"],
"resources": ["config://app", "docs://about"],
"prompts": []
}
}
},
"capabilities": {
"tools": { "listChanged": true },
"resources": { "subscribe": true, "listChanged": true },
"prompts": { "listChanged": true }
},
"instructions": "OAuth-protected MCP resource server. ..."
}Tool/resource/prompt names in scopeMapping support glob patterns — "*" matches everything, "docs://*" matches all document resources.
Provider Presets
File | Provider | Validation |
| Local dev | JWKS (dev keys) |
| Auth0 | JWKS RS256 |
| Keycloak | JWKS RS256, ES256 |
HTTP API Reference
Method | Path | Auth Required | Purpose |
|
| Yes (Bearer token) | JSON-RPC requests |
|
| Session ID | Server-Sent Events stream |
|
| Session ID | Terminate a session |
|
| No | Health check |
|
| No | RFC 9728 metadata |
|
| No | Path-specific metadata |
Capabilities Inventory
Tools (18)
Category | Tool | Description | Scope |
Utility |
| Text echo — connectivity check | Public |
| add, subtract, multiply, divide, power, sqrt | Public | |
| Server time in any IANA timezone (structured output) | Public | |
| Random integer in [min, max] range | Public | |
| count, sum, min, max, mean, median, stddev | Public | |
Data Ops |
| Full-text search across 15+ articles |
|
| DAU, revenue, churn, regional data, feature usage |
| |
| Create a new KB article |
| |
| Update an existing KB article |
| |
| List/filter 12+ mock projects |
| |
| Create a new project |
| |
| Update project status/progress |
| |
File System |
| Browse mock directory tree |
|
| Read mock file contents |
| |
| Create/overwrite a mock file |
| |
Admin |
| Uptime, requests, memory, auth info |
|
| Hot-reload OAuth config from disk |
| |
| List all tools, resources, prompts |
|
Resources (10)
URI | Description | Scope |
| Server config and feature flags (JSON) | Public |
| About this server (Markdown) | Public |
| KB article by ID |
|
| 10 most recent articles |
|
| Current analytics snapshot |
|
| Regional user/revenue data |
|
| Project details by ID (listed) |
|
| Mock file content by path |
|
| Current user profile from token |
|
| Server health and auth info |
|
Prompts (5)
Prompt | Arguments | Description | Scope |
|
| Summarize text (bullet/paragraph/tweet/executive) |
|
|
| Structured code review with senior engineer persona |
|
|
| Guided brainstorming session |
|
|
| Data exploration with tool guidance |
|
|
| Structured refactoring with architect persona |
|
Using with Claude Desktop (stdio)
Add to claude_desktop_config.json:
{
"mcpServers": {
"oauth-mcp-server": {
"command": "node",
"args": ["C:\\Users\\Shylendra\\git\\oauth-mcp-server\\dist\\stdio.js"]
}
}
}The stdio transport does not use OAuth per the MCP spec — it runs in full-access mode. For OAuth, use the HTTP transport.
Testing
npm run test:smoke # stdio transport — validates all 18 tools, 10 resources, 5 prompts
npm run test:vercel # Vercel serverless handlerMCP Inspector
npm run inspect # stdio transportOr for HTTP: start the server, then open the Inspector and connect with Transport type: Streamable HTTP, URL: http://localhost:6000/mcp.
Project Layout
src/
├── server.ts # McpServer factory (auth-aware)
├── stdio.ts # stdio transport entry point
├── http.ts # Streamable HTTP entry point (OAuth)
├── banner.ts # Startup banner
├── logging.ts # Structured JSON logging
├── auth/
│ ├── index.ts # Public API surface
│ ├── types.ts # ValidatedToken, AuthContext, etc.
│ ├── token-validator.ts # JWT/JWKS + introspection
│ ├── scope-resolver.ts # OAuth scopes → MCP permissions
│ ├── discovery.ts # RFC 9728 metadata + WWW-Authenticate
│ └── errors.ts # 401/403/400 error responses
├── config/
│ ├── types.ts # Config type definitions
│ └── loader.ts # Config loading + Zod validation
├── capabilities/
│ ├── tools/ # 18 tools in 4 categories
│ ├── resources/index.ts # 10 resources
│ └── prompts/index.ts # 5 prompts
└── data/
├── users.ts # 8 mock users
├── documents.ts # 15+ knowledge base articles
├── projects.ts # 12+ mock projects
├── analytics.ts # 30-day metrics, regional data
└── files.ts # Mock directory tree
config/
├── default.json # Dev config (port 6000)
├── auth0.example.json # Auth0 preset
└── keycloak.example.json # Keycloak preset
api/
├── health.ts # Vercel health endpoint
└── mcp.ts # Vercel serverless OAuth handler
scripts/
├── smoke.mjs # stdio smoke test
├── smoke-http.mjs # HTTP smoke test
├── smoke-vercel.ts # Vercel smoke test
└── gen-jwks.mjs # Dev JWKS generatorStandards Compliance
MCP 2026-07-28 — Full spec:
server/discover, per-request_meta, stateless authMCP 2025-11-25 — Backward compatible
initializehandshakeOAuth 2.1 (draft-ietf-oauth-v2-1-13) — Resource server role
RFC 6750 — Bearer Token Usage
RFC 8414 — OAuth 2.0 Authorization Server Metadata
RFC 8707 — Resource Indicators for OAuth 2.0
RFC 9207 — Authorization Server Issuer Identification
RFC 9728 — OAuth 2.0 Protected Resource Metadata
License
MIT
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseDqualityDmaintenanceA ready-to-use starter implementation of the Model Context Protocol (MCP) server that enables applications to provide standardized context for LLMs with sample resources, tools, and prompts.21781
- Flicense-qualityDmaintenanceA server implementation of the Model Context Protocol (MCP) that provides REST API endpoints for managing and interacting with MCP resources.
- Alicense-qualityDmaintenanceA self-hostable OAuth 2.0 server designed for the Model-Context-Protocol (MCP) that enables you to secure your MCP applications with a robust implementation you control.2,868112ISC
- Alicense-qualityDmaintenanceA comprehensive reference implementation demonstrating all features of the Model Context Protocol (MCP) specification, serving as documentation, learning resource, and testing tool for MCP implementations.1MIT
Related MCP Connectors
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
MCP (Model Context Protocol) server for Appwrite
MCP server for verifying EUDI/Talao wallet data via OIDC4VP (pull) for AI agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Shylendra/oauth-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server