OAuth 2.1 for Implementing MCP Server with ScaleKit: Step-by-step Coding Tutorial
In this tutorial, we will explore how to implement OAUTH 2.1 step by step for MCP servers. To keep things practical, we will build a simple financial sentiment analysis server and protect it with ScaleKit (ScaleKit), a tool that makes setting up OAuth faster and easier.
With ScaleKit, all we have to do is expose the metadata endpoint URL of the MCP client to discover the server and add authorization middleware for security token-based authentication. ScaleKit handles all complex OAuth 2.1 flows, so you don’t need to manually implement or manage token generation, refresh or validation. Once this setup is complete, your MCP server can seamlessly process verified requests. Check The complete code is here.
Alpha Vantage API
To get stock news sentiment, we will use the Alpha Vantage API. Get free API keys:
- Use this link to access the Alpha Vantage platform
- Enter your email and the required details.
- You will receive the API key – copy and store securely as you need it to verify the request.
Node JS
run MCP Inspector To test our application, we need to install node.js.
- Download the latest version of node.js from nodejs.org
- Run the installer.
- Keep the default settings and complete the installation.
Python dependencies
pip install fastapi fastmcp mcp scalekit-sdk-python
Scalekit
Get started Scalekitplease follow the following steps:
Create your ScaleKit account
- Visit Scalekit.com and register.
- ScaleKit offers free tiers so you don’t have to worry about billing.
- After logging in, click “Activate full stack auth. ”
Set permissions
- Open the Authorization panel.
- Under the Permissions section, click Add License.
- Use the following values:
License name: News: Read
describe: Get inventory sentiments using Alpha Vantage
ScaleKit’s permissions are used to define and manage scopes to control the features or resources that an application can access. For example, News: Read License allows your MCP server to access inventory emotional data from Alpha Vantage, while creating additional permissions to log in to other features or APIs in the application.
Add your MCP server
- Go to the MCP Server section and click “Add MCP Server”.
- Fill in the required fields:
Server name: Any name you like.
Resource Identifier: A unique identifier for the MCP server. This value is included in the AUD statement of the access token, helping the server to verify the request.
For local tests, set it to:
When using FASTMCP, the /MCP path is automatically added to the endpoint. Make sure to include the rear slashes in the end to avoid configuration issues. Check The complete code is here.
Set the scope to the permissions you just created: News: Read
After the server is created, ScaleKit will generate your resource metadata. Be sure to note the MCP server identifier (found next to the server name, for example, RES_88056357768398086) because you need it later.

Resource metadata example
Your metadata looks similar to this (but unique to your account):
Metadata endpoint URL:
/.well-known/oauth-protected-resource/mcp
Resource metadata JSON:
{
"authorization_servers": [
"
],
"bearer_methods_supported": ["header"],
"resource": "",
"resource_documentation": "docs",
"scopes_supported": ["news:read"]
}


Obtain API credentials
- Go to Settings → API Credentials.
- Copy your customer ID and environment URL.
- Click Generate New Secret to create your secret key.
Safely store these values - we need them to be configured later.


.env
Now we will create a .ENV file with the following variables
ALPHA_VANTAGE_API_KEY=
METADATA_JSON_RESPONSE=
SCALEKIT_ENVIRONMENT_URL=
SCALEKIT_CLIENT_ID=
SCALEKIT_CLIENT_SECRET=
SCALEKIT_RESOURCE_METADATA_URL=
SCALEKIT_AUTHORIZATION_SERVERS=
SCALEKIT_AUDIENCE_NAME=
SCALEKIT_RESOUCE_NAME=
SCALEKIT_RESOUCE_DOCS_URL=
alpha_vantage_api_key
Your personal API keys are from Alpha Vantage and are used to get inventory emotional data.
metadata_json_response
The JSON response generated by ScaleKit when configuring the MCP server.
It contains details such as authorization server, support scope and document URL.
scalekit_environment_url
The environment URL under the settings section.
scalekit_client_id
Set the customer ID mentioned in the Settings section.
scalekit_client_secret
The secret key you generated under Settings → API credentials.
scalekit_resource_metadata_url
Your MCP server is exposed to the URL of the metadata request.
example:
scalekit_authorization_servers
URL to the MCP server identifier issued by ScaleKit.
example:
https://.scalekit.dev/resources/res_***************
You can find the subdomain from resource metadata JSON


scalekit_audience_name
The (AUD) claim used in the access token to verify the request. Check The complete code is here.
scalekit_resouce_name
The resource name of the MCP server. In most cases, this is with scalekit_audience_name. Check The complete code is here.
scalekit_resouce_docs_url
Location where to host the MCP server documentation.
example:
docs
We will first create a configuration file to load all environment variables that will be used later. Check The complete code is here.
import os
from dotenv import load_dotenv
load_dotenv()
class Settings():
ALPHA_VANTAGE_API_KEY = os.environ.get('ALPHA_VANTAGE_API_KEY')
METADATA_JSON_RESPONSE = os.environ.get('METADATA_JSON_RESPONSE')
SCALEKIT_ENVIRONMENT_URL = os.environ.get('SCALEKIT_ENVIRONMENT_URL')
SCALEKIT_CLIENT_ID = os.environ.get('SCALEKIT_CLIENT_ID')
SCALEKIT_CLIENT_SECRET = os.environ.get('SCALEKIT_CLIENT_SECRET')
SCALEKIT_RESOURCE_METADATA_URL = os.environ.get('SCALEKIT_RESOURCE_METADATA_URL')
SCALEKIT_AUTHORIZATION_SERVERS = os.environ.get('SCALEKIT_AUTHORIZATION_SERVERS')
SCALEKIT_AUDIENCE_NAME = os.environ.get('SCALEKIT_AUDIENCE_NAME')
SCALEKIT_RESOUCE_NAME = os.environ.get('SCALEKIT_RESOUCE_NAME')
SCALEKIT_RESOUCE_DOCS_URL = os.environ.get('SCALEKIT_RESOUCE_DOCS_URL')
PORT = 10000
settings = Settings()
This code block uses the Alpha Vantage API to get real-time news sentiment data for a given stock. It searches three recent articles, summarizing their titles, abstracts, sources and publication time for quick insights. Check The complete code is here.
from mcp.server.fastmcp import FastMCP
from typing import Any
import os
import httpx
from typing import Dict, List
from config import settings
# Create an MCP server
mcp = FastMCP("finance-news")
BASE_URL = "
async def call_alpha_vantage(endpoint: str, params: dict[str, Any]) -> dict[str, Any] | None:
"""Generic async caller to Alpha Vantage."""
params["apikey"] = settings.ALPHA_VANTAGE_API_KEY
params["function"] = endpoint
async with httpx.AsyncClient() as client:
try:
response = await client.get(BASE_URL, params=params, timeout=30.0)
response.raise_for_status()
return response.json()
except Exception:
return None
@mcp.tool()
async def get_news_sentiment(ticker: str) -> str:
"""Get news sentiment data for a stock ticker.
Args:
ticker: Stock ticker symbol (e.g., MSFT, AAPL)
"""
data = await call_alpha_vantage("NEWS_SENTIMENT", {"tickers": ticker.upper()})
if not data or "feed" not in data:
return "Couldn't retrieve news sentiment."
articles = data["feed"][:3]
result = []
for item in articles:
result.append(f"""
📰 {item['title']}
Summary: {item['summary']}
Source: {item['source']} | Published: {item['time_published']}
""")
return "n---n".join(result)
This middleware acts as the authorization layer for your MCP server to ensure that only authenticated requests are processed. It uses ScaleKit Client Verify the access token in each incoming request. When a request comes in, the middleware first checks whether the path is public, such as a metadata endpoint /. famous/.
If the request is not a public path, it will look for an authorization title with a valid carrier token. Then use ScaleKit to verify the token. If the token is missing, invalid or expired, the middleware responds immediately 401 Unauthorized Error and structured error messages. Check The complete code is here.
If the token is valid, the request is passed to the next layer of the application. In addition, records are integrated throughout the process to capture critical events, making it easier to debug and audit authentication flows.
Finally, this middleware will be imported and added to the server file to protect all security endpoints. Check The complete code is here.
import json
import logging
from fastapi import HTTPException, Request
from fastapi.security import HTTPBearer
from fastapi.responses import JSONResponse
from scalekit import ScalekitClient
from starlette.middleware.base import BaseHTTPMiddleware
from config import settings
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# Security scheme for Bearer token
security = HTTPBearer()
# Initialize ScaleKit client
scalekit_client = ScalekitClient(
settings.SCALEKIT_ENVIRONMENT_URL,
settings.SCALEKIT_CLIENT_ID,
settings.SCALEKIT_CLIENT_SECRET
)
# Authentication middleware
class AuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if request.url.path.startswith("/.well-known/"):
return await call_next(request)
try:
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing or invalid authorization header")
token = auth_header.split(" ")[1]
request_body = await request.body()
# Parse JSON from bytes
try:
request_data = json.loads(request_body.decode('utf-8'))
except (json.JSONDecodeError, UnicodeDecodeError):
request_data = {}
try:
scalekit_client.validate_access_token(token)
except Exception as e:
raise HTTPException(status_code=401, detail="Token validation failed")
except HTTPException as e:
return JSONResponse(
status_code=e.status_code,
content={"error": "unauthorized" if e.status_code == 401 else "forbidden", "error_description": e.detail},
headers={
"WWW-Authenticate": f'Bearer realm="OAuth", resource_metadata="{settings.SCALEKIT_RESOURCE_METADATA_URL}"'
}
)
return await call_next(request)
The script sets up a FastAPI application integrated with the MCP server for stock news sentiment analysis. It first imports the necessary libraries including FastApi, CORS middleware and custom authentication middleware. Check The complete code is here.
Use an asynchronous context manager to manage application lifecycles through merged lifecycle to ensure finance_news_server.session_manager is essentially the inventory emotional logic we create that runs smoothly when the application runs. The CORS middleware is configured to allow cross-border requests, which is useful during development but should be restricted in production environments.
A new end point, /.well-now/oauth-protected-resource/mcpadd metadata found as OAUTH 2.1 protection resource. This endpoint provides important details such as supported authorization servers, carrier token methods, resource names, document URLs and supported scopes – in this case, MCP: Tools: News: News: Read.
The MCP server is created using the Finance_News_Server.Streamable_http_app() function and is installed in the root path/, so that the Core MCP function can be accessed through the main application. Through integration authmiddlewareand the script ensures that this middleware is added correctly to the server file.
Finally, the Main() function runs the application using Uvicorn and enables logging at the debug level, binding the server to Localhost on the configured port. Check The complete code is here.
import contextlib
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import json
from auth import AuthMiddleware
from config import settings
from finance import mcp as finance_news_server
# Create a combined lifespan to manage the MCP session manager
@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
async with finance_news_server.session_manager.run():
yield
app = FastAPI(lifespan=lifespan)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, specify your actual origins
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
# MCP well-known endpoint
@app.get("/.well-known/oauth-protected-resource/mcp")
async def oauth_protected_resource_metadata():
"""
OAuth 2.0 Protected Resource Metadata endpoint for MCP client discovery.
Required by the MCP specification for authorization server discovery.
"""
return {
"authorization_servers": [settings.SCALEKIT_AUTHORIZATION_SERVERS],
"bearer_methods_supported": ["header"],
"resource": settings.SCALEKIT_RESOURCE_NAME,
"resource_documentation": settings.SCALEKIT_RESOURCE_DOCS_URL,
"scopes_supported": [
"mcp:tools:news:read"
],
}
# Create and mount the MCP server with authentication
mcp_server = finance_news_server.streamable_http_app()
app.add_middleware(AuthMiddleware)
app.mount("/", mcp_server)
def main():
"""Main entry point for the MCP server."""
uvicorn.run(app, host="localhost", port=settings.PORT, log_level="debug")
if __name__ == "__main__":
main()
To run the server, execute Python Server.py, which will start the application on Localhost:10000. To test the settings, open another terminal and run:
npx @modelcontextprotocol/inspector
After the MCP inspector runs, enter As the server URL. If you try to connect without providing valid credentials, you will encounter the following error:
Connection error: Check if your MCP server is running and if the proxy token is configured correctly.


Now, provide the bearer token using the secret ID you generated in ScaleKit. After entering, you will successfully verify and can start making tool calls.




Check The complete code is here. Check out ours anytime Tutorials, codes and notebooks for github pages. Also, please stay tuned for us twitter And don’t forget to join us 100K+ ml reddit And subscribe Our newsletter.

I am a civil engineering graduate in Islamic Islam in Jamia Milia New Delhi (2022) and I am very interested in data science, especially neural networks and their applications in various fields.