plugins/mcp-auth/skills/mcp-auth/SKILL.md
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.
npx skillsauth add scalekit-inc/claude-code-authstack adding-mcp-oauthInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
⚠️ 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:
mcp). See: https://pypi.org/project/mcp/1.9.1/app you run with an ASGI server (e.g. uvicorn module:app)—this is Streamable HTTP, not stdio.https://gofastmcp.com/python-sdk/fastmcp-server-http
uvicorn your_module:app --host 0.0.0.0 --port 8000If your MCP server currently uses stdio transport, you must migrate to HTTP-based transport before implementing OAuth. See MCP Transport Documentation for migration guidance.
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
Node.js:
npm install @scalekit-sdk/node
Python:
pip install scalekit-sdk-python
Get credentials from Scalekit dashboard after creating an account.
In Scalekit dashboard:
Advanced settings (optional):
https://mcp.yourapp.com)todo:read, todo:writeImportant: Restart your MCP server after toggling DCR or CIMD settings.
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.
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}"'
}
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)
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
}
Before testing with AI hosts, Claude Code will scan your project to determine the right URL to verify against. It will look for:
RESOURCE_ID or resource values in your code or .env/.well-known/oauth-protected-resourceSERVER_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.
Beyond OAuth 2.1, enable these methods through Scalekit (no code changes needed):
Enterprise SSO: Organizations authenticate through Okta, Azure AD, Google Workspace
Social logins: Users authenticate via Google, GitHub, Microsoft
Custom auth: Use your own authentication system
See Scalekit documentation for configuration details.
For detailed implementation guides with specific frameworks:
See Complete Working Examples below for production-ready code.
Production-ready examples demonstrating different implementation approaches:
Skill: add-auth-fastmcp
Skill: express-mcp-server
Skill: fastapi-fastmcp
Reference: scalekit-mcp-server.md
| 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:
Token validation fails:
Discovery endpoint not found:
/.well-known/oauth-protected-resourceScope validation errors:
Scalekit OAuth server:
Your MCP server:
This separation ensures clean boundaries: Scalekit handles identity and token issuance, your server focuses on business logic.
development
Walks through a structured production readiness checklist for Scalekit SSO implementations. Use when the user says they are going live, launching to production, doing a pre-launch review, hardening their SSO setup, or wants to verify their Scalekit implementation is production-ready.
data-ai
Implements complete SSO and authentication flows using Scalekit. Handles modular SSO, IdP-initiated login, user session management, and enterprise customer onboarding. Use when adding authentication, SSO, SAML, OIDC, or user login to applications.
testing
Implements Scalekit's admin portal for customer self-serve SSO and SCIM configuration. Generates portal links server-side and embeds the portal as an iframe in the app's settings UI. Use when the user asks to add an admin portal, customer self-serve SSO setup, iframe embed for SSO config, shareable setup link, or let customers configure their own SSO or SCIM connection.
development
Walks through a structured production readiness checklist for Scalekit SCIM provisioning implementations. Use when the user says they are going live, launching to production, doing a pre-launch review, or wants to verify their SCIM directory sync implementation is production-ready.