chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
from pydantic import AnyHttpUrl
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
from mcp.server.auth.settings import AuthSettings
|
||||
from mcp.server.fastmcp.server import FastMCP
|
||||
from typing import Any, Literal
|
||||
from starlette.middleware import Middleware
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.responses import Response
|
||||
from starlette.applications import Starlette
|
||||
from starlette.routing import Mount
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
from util import validate_token
|
||||
load_dotenv()
|
||||
|
||||
settings = {
|
||||
"host": "localhost",
|
||||
"port": 8000,
|
||||
"mcp_scope": "mcp:read",
|
||||
"server_url": AnyHttpUrl("http://localhost:8000"),
|
||||
}
|
||||
|
||||
users = ["User Userson", "Admin Adminson"]
|
||||
|
||||
def is_user(token: str) -> bool:
|
||||
decodedToken = validate_token(token[7:])
|
||||
if not decodedToken:
|
||||
return False
|
||||
return decodedToken["name"] in users
|
||||
|
||||
def has_scope(token: str, scope: str) -> bool:
|
||||
token = token[7:]
|
||||
token = validate_token(token)
|
||||
|
||||
if not token:
|
||||
return False
|
||||
# very naive scope check, in real life parse the token and check scopes properly
|
||||
return scope in token["scopes"]
|
||||
|
||||
def validate_jwt(token: str) -> bool:
|
||||
token = token[7:]
|
||||
# print("Validating token:", token)
|
||||
return validate_token(token) != None
|
||||
|
||||
|
||||
class CustomHeaderMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request, call_next):
|
||||
|
||||
has_header = request.headers.get("Authorization")
|
||||
# print("Authorization header:", has_header)
|
||||
if not has_header:
|
||||
print("-> Missing Authorization header!")
|
||||
return Response(status_code=401, content="Unauthorized")
|
||||
|
||||
if not validate_jwt(has_header):
|
||||
print("-> Invalid token!")
|
||||
return Response(status_code=403, content="Forbidden")
|
||||
|
||||
print("Valid token, proceeding...")
|
||||
|
||||
if not is_user(has_header):
|
||||
print("-> User does not exist!")
|
||||
return Response(status_code=403, content="Forbidden - user does not exist")
|
||||
print("User exists, proceeding...")
|
||||
|
||||
if not has_scope(has_header, "Admin.Write"):
|
||||
print("-> Missing required scope!")
|
||||
return Response(status_code=403, content="Forbidden - insufficient scopes")
|
||||
|
||||
print("User has required scope, proceeding...")
|
||||
|
||||
print(f"-> Received {request.method} {request.url}")
|
||||
response = await call_next(request)
|
||||
response.headers['Custom'] = 'Example'
|
||||
return response
|
||||
|
||||
app = FastMCP(
|
||||
name="MCP Resource Server",
|
||||
instructions="Resource Server that validates tokens via Authorization Server introspection",
|
||||
host=settings["host"],
|
||||
port=settings["port"],
|
||||
debug=True
|
||||
)
|
||||
|
||||
@app.tool()
|
||||
async def get_time() -> dict[str, Any]:
|
||||
"""
|
||||
Get the current server time.
|
||||
|
||||
This tool demonstrates that system information can be protected
|
||||
by OAuth authentication. User must be authenticated to access it.
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
return {
|
||||
"current_time": now.isoformat(),
|
||||
"timezone": "UTC", # Simplified for demo
|
||||
"timestamp": now.timestamp(),
|
||||
"formatted": now.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
|
||||
async def setup(app) -> None:
|
||||
"""Run the server using StreamableHTTP transport."""
|
||||
|
||||
starlette_app = app.streamable_http_app()
|
||||
return starlette_app
|
||||
|
||||
async def run(starlette_app):
|
||||
import uvicorn
|
||||
config = uvicorn.Config(
|
||||
starlette_app,
|
||||
host=app.settings.host,
|
||||
port=app.settings.port,
|
||||
log_level=app.settings.log_level.lower(),
|
||||
)
|
||||
server = uvicorn.Server(config)
|
||||
await server.serve()
|
||||
|
||||
async def main():
|
||||
print("Running MCP Resource Server...")
|
||||
starlette_app = await setup(app)
|
||||
print("Adding custom middleware...")
|
||||
starlette_app.add_middleware(CustomHeaderMiddleware)
|
||||
|
||||
await run(starlette_app)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user