Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

FastAPI x FastMCP quickstart

This guide shows you how Scalekit’s provider secures any FastAPI x FastMCP server. We’ll register a sample server as a protected resource, configure a local mcp runtime, and use Scalekit as the Authentication server. We will able confirm that every request from MCP Client carries the right scopes and validate them on the MCP Server. The full code can be found at GitHub.

  • A Scalekit workspace (https://app.scalekit.com).
  • Python or node installed
  • MCP Inspector installed locally to test the connection.
  1. Navigate to Dashboard -> MCP Servers and click Add MCP Server.
  2. Give your server a friendly name (for example, Greeting MCP).
  3. Set the resource identifier to the URL your server will use (for local tests this is http://localhost:3002/).

Greeting MCP Register

When you save, Scalekit shows the OAuth-protected resource metadata. Copy the endpoint and metadata json as is required by for implementations below.

Greeting MCP Register

Follow these steps to assemble a Google-authenticated MCP server from scratch.

Terminal window
mkdir google-mcp-python
cd google-mcp-python
python3 -m venv .venv
source .venv/bin/activate

Create requirements.txt:

Terminal window
cat <<'REQ' > requirements.txt
mcp>=1.0.0
fastapi>=0.104.0
fastmcp>=0.8.0
uvicorn>=0.24.0
pydantic>=2.5.0
python-dotenv>=1.0.0
httpx>=0.25.0
python-jose[cryptography]>=3.3.0
cryptography>=41.0.0
scalekit-sdk-python>=2.4.0
starlette>=0.27.0
REQ
pip install -r requirements.txt

Create .env with the values from your Scalekit environment (replace the placeholders). You can find these values in Dashboard -> Settings and Dashboard -> MCP Servers -> Metadata JSON

Terminal window
cat <<'ENV' > .env
PORT=3002
SK_ENV_URL=https://<your-env>.scalekit.com
SK_CLIENT_ID=<your-client-id>
SK_CLIENT_SECRET=<your-client-secret>
MCP_SERVER_ID=<mcp-server-id-from-dashboard>
PROTECTED_RESOURCE_METADATA='<resource-metadata-json>'
EXPECTED_AUDIENCE=http://localhost:3002/
ENV
open .env

Create main.py and paste the complete server implementation:

main.py
import json
import os
from fastapi import FastAPI, Request, Response
from fastmcp import FastMCP, Context
from scalekit import ScalekitClient
from scalekit.common.scalekit import TokenValidationOptions
from starlette.middleware.cors import CORSMiddleware
from dotenv import load_dotenv
load_dotenv()
PORT = int(os.getenv("PORT", "3002"))
SK_ENV_URL = os.getenv("SK_ENV_URL", "")
SK_CLIENT_ID = os.getenv("SK_CLIENT_ID", "")
SK_CLIENT_SECRET = os.getenv("SK_CLIENT_SECRET", "")
EXPECTED_AUDIENCE = os.getenv("EXPECTED_AUDIENCE", "")
PROTECTED_RESOURCE_METADATA = os.getenv("PROTECTED_RESOURCE_METADATA", "")
RESOURCE_METADATA_URL = f"http://localhost:{PORT}/.well-known/oauth-protected-resource"
WWW_HEADER = {"WWW-Authenticate": f'Bearer realm="OAuth", resource_metadata="{RESOURCE_METADATA_URL}"'}
scalekit_client = ScalekitClient(
env_url=SK_ENV_URL,
client_id=SK_CLIENT_ID,
client_secret=SK_CLIENT_SECRET,
)
mcp = FastMCP("Greeting MCP", stateless_http=True)
@mcp.tool(name="greet_user", description="Greets the user with a personalized message.")
async def greet_user(name: str, ctx: Context | None = None) -> dict:
return {"content": [{"type": "text", "text": f"Hi {name}, welcome to Scalekit!"}]}
mcp_app = mcp.http_app(path="/")
app = FastAPI(lifespan=mcp_app.lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["*"]
)
@app.middleware("http")
async def auth_middleware(request: Request, call_next):
if request.url.path in {"/health", "/.well-known/oauth-protected-resource"}:
return await call_next(request)
auth_header = request.headers.get("authorization")
if not auth_header or not auth_header.startswith("Bearer "):
return Response("{\"error\": \"Missing Bearer token\"}", 401, WWW_HEADER, media_type="application/json")
token = auth_header.split("Bearer ", 1)[1].strip()
options = TokenValidationOptions(issuer=SK_ENV_URL, audience=[EXPECTED_AUDIENCE])
try:
is_valid = scalekit_client.validate_access_token(token, options=options)
if not is_valid:
raise ValueError("Invalid token")
except Exception:
return Response("{\"error\": \"Token validation failed\"}", 401, WWW_HEADER, media_type="application/json")
return await call_next(request)
@app.get("/.well-known/oauth-protected-resource")
async def oauth_metadata():
if not PROTECTED_RESOURCE_METADATA:
return Response("{\"error\": \"PROTECTED_RESOURCE_METADATA config missing\"}", 500, media_type="application/json")
metadata = json.loads(PROTECTED_RESOURCE_METADATA)
return Response(json.dumps(metadata, indent=2), media_type="application/json")
@app.get("/health")
async def health_check():
return {"status": "healthy"}
app.mount("/", mcp_app)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=PORT)

Start the server:

Terminal window
python main.py

The MCP endpoint now runs at http://localhost:3002/ and accepts only Google-authenticated requests issued by Scalekit.

Launch MCP Inspector

Terminal window
npx @modelcontextprotocol/inspector@latest

Provide your MCP Server URL: http://localhost:3002/

MCP Inspector Google