Adding mcp oauth
35 skills that teach AI coding agents to integrate Scalekit auth — agent auth, full-stack login, MCP OAuth 2.1, enterprise SSO, and SCIM. Works with Claude Code, Cursor, Windsurf, and 35+ other agents.
npx -y skills add scalekit-inc/skills --skill adding-mcp-oauthAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Guides users through adding OAuth 2.1 authorization to Model Context Protocol (MCP) servers using Scalekit. Use when setting up MCP servers, implementing authentication for AI hosts like Claude Desktop, Cursor, or VS Code, or when users mention MCP security, OAuth, or Scalekit integration.
SKILL.md
15.0 KB, as published. Nobody here has run it
Adding OAuth 2.1 Authorization to MCP Servers
Secure your MCP server with production-ready OAuth 2.1 authorization using Scalekit. This enables authenticated access through AI hosts like Claude Desktop, Cursor, and VS Code.
Critical Prerequisites
⚠️ MCP OAuth requires HTTP-based transport (Streamable HTTP): OAuth 2.1 authentication only works when your MCP server is exposed over HTTP using the Streamable HTTP transport. The standard StdioServerTransport (stdin/stdout) does not support OAuth flows.
Node.js requirement:
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
Python requirement (Streamable HTTP via ASGI app):
In Python, the practical equivalent of Node’s StreamableHTTPServerTransport is to create a Streamable HTTP ASGI app and run it behind an ASGI server (Uvicorn/Hypercorn). The official Python SDK exposes this as streamable_http_app() (convenience) or create_streamable_http_app(...) (lower-level).
Accurate Python snippet (FastMCP + Streamable HTTP):
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My MCP Server")
@mcp.tool
def ping() -> str:
return "pong"
# HTTP-based transport required for OAuth-capable deployments
app = mcp.streamable_http_app(path="/mcp")
Lower-level equivalent (explicit constructor):
from mcp.server.fastmcp import FastMCP
from fastmcp.server.http import create_streamable_http_app
mcp = FastMCP("My MCP Server")
app = create_streamable_http_app(server=mcp, streamable_http_path="/mcp")
Notes:
- The imports above match the official Python MCP SDK on PyPI (
mcp). See:https://pypi.org/project/mcp/1.9.1/ - The result is an ASGI
appyou run with an ASGI server (e.g.uvicorn module:app)—this is Streamable HTTP, not stdio. - SSE-only transports are not the same as Streamable HTTP; for OAuth with MCP hosts (Claude Desktop/Cursor/VS Code), use Streamable HTTP. See:
https://gofastmcp.com/python-sdk/fastmcp-server-http- Example run:
uvicorn your_module:app --host 0.0.0.0 --port 8000
- Example run:
If your MCP server currently uses stdio transport, you must migrate to HTTP-based transport before implementing OAuth. See MCP Transport Documentation for migration guidance.
Setup workflow
Copy this checklist and track progress:
MCP OAuth Setup:
- [ ] Step 1: Install Scalekit SDK
- [ ] Step 2: Register MCP server in Scalekit dashboard
- [ ] Step 3: Implement discovery endpoint
- [ ] Step 4: Add token validation middleware
- [ ] Step 5: (Optional) Add scope-based authorization
- [ ] Step 6: Test with AI hosts
Step 1: Install Scalekit SDK
Node.js:
npm install @scalekit-sdk/node
Python:
pip install scalekit-sdk-python
Get credentials from Scalekit dashboard after creating an account.
Step 2: Register MCP server
In Scalekit dashboard:
- Go to MCP servers → Add MCP server
- Provide a descriptive name (appears on consent page)
- Enable dynamic client registration (allows automatic MCP host registration)
- Enable Client ID Metadata Document (CIMD) (fetches client metadata automatically)
- Click Save
Advanced settings (optional):
- Server URL: Your MCP server identifier (e.g.,
https://mcp.yourapp.com) - Access token lifetime: 300-3600 seconds recommended
- Scopes: Define permissions like
todo:read,todo:write
Important: Restart your MCP server after toggling DCR or CIMD settings.
Step 3: Implement discovery endpoint
Create /.well-known/oauth-protected-resource endpoint. Copy metadata JSON from Dashboard > MCP Servers > Your server > Metadata JSON.
Node.js (Express):
app.get('/.well-known/oauth-protected-resource', (req, res) => {
res.json({
"authorization_servers": [
"https://<SCALEKIT_ENVIRONMENT_URL>/resources/<YOUR_RESOURCE_ID>"
],
"bearer_methods_supported": ["header"],
"resource": "https://mcp.yourapp.com",
"resource_documentation": "https://mcp.yourapp.com/docs",
"scopes_supported": ["todo:read", "todo:write"]
});
});
Python (FastAPI):
@app.get("/.well-known/oauth-protected-resource")
async def get_oauth_protected_resource():
return {
"authorization_servers": [
"https://<SCALEKIT_ENVIRONMENT_URL>/resources/<YOUR_RESOURCE_ID>"
],
"bearer_methods_supported": ["header"],
"resource": "https://mcp.yourapp.com",
"resource_documentation": "https://mcp.yourapp.com/docs",
"scopes_supported": ["todo:read", "todo:write"]
}
Replace placeholders with actual values from Scalekit dashboard.
Step 4: Add token validation middleware
Initialize Scalekit client
Node.js:
import { Scalekit } from '@scalekit-sdk/node';
const scalekit = new Scalekit(
process.env.SCALEKIT_ENVIRONMENT_URL,
process.env.SCALEKIT_CLIENT_ID,
process.env.SCALEKIT_CLIENT_SECRET
);
const RESOURCE_ID = 'https://your-mcp-server.com'; // Or autogenerated ID from dashboard
const METADATA_ENDPOINT = 'https://your-mcp-server.com/.well-known/oauth-protected-resource';
export const WWWHeader = {
HeaderKey: 'WWW-Authenticate',
HeaderValue: `Bearer realm="OAuth", resource_metadata="${METADATA_ENDPOINT}"`
};
Python:
from scalekit import ScalekitClient
import os
scalekit_client = ScalekitClient(
env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET")
)
RESOURCE_ID = "https://your-mcp-server.com"
METADATA_ENDPOINT = "https://your-mcp-server.com/.well-known/oauth-protected-resource"
WWW_HEADER = {
"WWW-Authenticate": f'Bearer realm="OAuth", resource_metadata="{METADATA_ENDPOINT}"'
}
Implement authentication middleware
Node.js:
export async function authMiddleware(req, res, next) {
try {
// Allow public access to well-known endpoints
if (req.path.includes('.well-known')) {
return next();
}
// Extract Bearer token
const authHeader = req.headers['authorization'];
const token = authHeader?.startsWith('Bearer ')
? authHeader.split('Bearer ')[1]?.trim()
: null;
if (!token) {
throw new Error('Missing or invalid Bearer token');
}
// Validate token against resource audience
await scalekit.validateToken(token, {
audience: [RESOURCE_ID]
});
next();
} catch (err) {
return res
.status(401)
.set(WWWHeader.HeaderKey, WWWHeader.HeaderValue)
.end();
}
}
// Apply to all MCP endpoints
app.use('/', authMiddleware);
Python:
from scalekit.common.scalekit import TokenValidationOptions
from fastapi import Request, HTTPException, status
async def auth_middleware(request: Request, call_next):
# Allow public access to well-known endpoints
if request.url.path.startswith("/.well-known"):
return await call_next(request)
# Extract Bearer token
auth_header = request.headers.get("Authorization", "")
token = None
if auth_header.startswith("Bearer "):
token = auth_header.split("Bearer ")[1].strip()
if not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
headers=WWW_HEADER
)
# Validate token
try:
options = TokenValidationOptions(
issuer=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
audience=[RESOURCE_ID]
)
scalekit_client.validate_token(token, options=options)
except Exception:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
headers=WWW_HEADER
)
return await call_next(request)
# Apply to all MCP endpoints
app.middleware("http")(auth_middleware)
Step 5: Scope-based tool authorization (Optional)
Add fine-grained access control at the tool execution level:
Node.js:
try {
await scalekit.validateToken(token, {
audience: [RESOURCE_ID],
requiredScopes: [scope] // e.g., 'todo:write'
});
} catch(error) {
return res.status(403).json({
error: 'insufficient_scope',
error_description: `Required scope: ${scope}`,
scope: scope
});
}
Python:
try:
scalekit_client.validate_access_token(
token,
options=TokenValidationOptions(
audience=[RESOURCE_ID],
required_scopes=[scope]
)
)
except Exception:
return {
"error": "insufficient_scope",
"error_description": f"Required scope: {scope}",
"scope": scope
}
Step 6: Verify and deploy
Verify your integration
Before testing with AI hosts, Claude Code will scan your project to determine the right URL to verify against. It will look for:
RESOURCE_IDorresourcevalues in your code or.env- The host/domain used in
/.well-known/oauth-protected-resource - Any deployed base URL in environment config (
SERVER_URL,PUBLIC_URL, etc.)
If no URL is found, you'll be asked:
"What is your MCP server base URL? (e.g.,
https://mcp.yourapp.comorhttps://mcp.yourapp.com/mcp)"
Once the URL is known, run these three checks:
Check 1 – Confirm 401 without token:
curl -i <your-mcp-url>
Expected: HTTP/1.1 401 Unauthorized
Check 2 – Confirm WWW-Authenticate header: The response must include:
WWW-Authenticate: Bearer realm="OAuth", resource_metadata="https://<your-domain>/.well-known/oauth-protected-resource"
This is what triggers the MCP client's OAuth flow. A plain 401 without this header will cause AI hosts (Claude Desktop, Cursor, VS Code) to fail silently.
Check 3 – Confirm metadata endpoint is reachable:
curl https://<your-domain>/.well-known/oauth-protected-resource
Expected: JSON with resource, authorization_servers, and scopes_supported.
Testing checklist (after verification passes)
- Test with Claude Desktop
- Test with Cursor
- Test with VS Code
- Verify token validation rejects invalid tokens
- Verify scope-based authorization (if implemented)
Production deployment checklist
- Configure CORS policies for endpoints
- Set up monitoring and logging for auth events
- Use HTTPS for all communications
- Store credentials in environment variables or secret management
- Configure appropriate token lifetimes
- Document authentication flow for users
Additional authentication methods
Beyond OAuth 2.1, enable these methods through Scalekit (no code changes needed):
Enterprise SSO: Organizations authenticate through Okta, Azure AD, Google Workspace
- Requires organization admins to register domains with Scalekit
- Centralized access control through enterprise identity systems
Social logins: Users authenticate via Google, GitHub, Microsoft
- Quick onboarding for individual users
- Reduced friction for personal and small team use
Custom auth: Use your own authentication system
- Integrate existing user management
- Maintain full control over authentication flow
See Scalekit documentation for configuration details.
Framework-specific guides
For detailed implementation guides with specific frameworks:
- FastMCP: 5-line integration with Scalekit provider (simplest approach)
- Express.js: Full OAuth implementation with manual middleware (most control)
- FastAPI + FastMCP: Python-based implementation with custom middleware
See Complete Working Examples below for production-ready code.
Complete Working Examples
Production-ready examples demonstrating different implementation approaches:
FastMCP (5-Line OAuth Integration)
Skill: add-auth-fastmcp
- Simplest approach with built-in OAuth provider
- Automatic token validation and scope enforcement
- Complete todo server with CRUD operations
- GitHub: todo-fastmcp
Express.js (Full Manual OAuth)
Skill: express-mcp-server
- Complete control over authentication middleware
- Modular architecture with transport, tools, auth layers
- Production-ready with CORS, logging, error handling
- GitHub: greeting-mcp-node
FastAPI + FastMCP (Custom Middleware)
Skill: fastapi-fastmcp
- Python-based with custom authentication middleware
- Combines FastAPI's HTTP control with FastMCP's tooling
- Ideal for existing FastAPI applications
- GitHub: greeting-mcp-python
Scalekit MCP Server (Production Reference)
Reference: scalekit-mcp-server.md
- Official Scalekit production implementation
- Comprehensive tooling for identity management
- Advanced patterns: scope-based auth, pagination, multi-step operations
- Demonstrates best practices for complex MCP servers
- GitHub: scalekit-inc/scalekit-mcp-server
Choosing an Example
| Example | Complexity | Best For | Control Level |
|---|---|---|---|
| FastMCP | Simplest | Quick prototypes, minimal code | Low (automatic) |
| Express.js | Medium | Production Node.js, custom logic | High (manual) |
| FastAPI + FastMCP | Medium | Production Python, existing apps | High (manual) |
| Scalekit Server | Advanced | Complex apps, reference patterns | Very High |
All examples include:
- Complete source code
- Setup instructions
- Environment configuration
- Testing guidance
Common issues
Token validation fails:
- Verify RESOURCE_ID matches Server URL in dashboard
- Check environment variables are set correctly
- Ensure token hasn't expired
Discovery endpoint not found:
- Verify endpoint path is exactly
/.well-known/oauth-protected-resource - Check endpoint is publicly accessible (not protected by auth middleware)
Scope validation errors:
- Verify scopes in dashboard match those in code
- Check token includes required scopes
- Ensure scope strings match exactly (case-sensitive)
Architecture summary
Scalekit OAuth server:
- Authenticates users and agents
- Issues access tokens with scopes
- Manages OAuth 2.1 flows
- Supports dynamic client registration
Your MCP server:
- Validates incoming tokens
- Enforces permissions from token scopes
- Executes tool calls for authorized requests
This separation ensures clean boundaries: Scalekit handles identity and token issuance, your server focuses on business logic.