deploy1 / echo_server.py
manmo12's picture
mdr
b3efdb3
from fastapi import FastAPI, Request, HTTPException
from fastmcp import FastMCP
from authlib.integrations.starlette_client import OAuth
from starlette.middleware.sessions import SessionMiddleware
import os
from typing import Dict, Any, Optional
# Validation des variables d'environnement
required_env_vars = ["CLIENT_ID", "CLIENT_SECRET"]
missing_vars = [var for var in required_env_vars if not os.getenv(var)]
if missing_vars:
raise ValueError(f"Variables d'environnement manquantes: {', '.join(missing_vars)}")
app = FastAPI()
app.add_middleware(
SessionMiddleware,
secret_key="CHANGE_ME"
)
# --- Config OAuth Twitter ---
oauth = OAuth()
oauth.register(
name='twitter',
client_id=os.getenv("CLIENT_ID"),
client_secret=os.getenv("CLIENT_SECRET"),
authorize_url="https://twitter.com/i/oauth2/authorize",
access_token_url="https://api.twitter.com/2/oauth2/token",
client_kwargs={
"scope": "tweet.read users.read offline.access",
"token_endpoint_auth_method": "client_secret_post"
}
)
# --- Fonction d'authentification pour MCP ---
async def mcp_auth(request: Request) -> Optional[Dict[str, Any]]:
"""
Fonction d'authentification pour FastMCP
Retourne les données utilisateur si authentifié, None sinon
"""
user = request.session.get('user')
if not user:
return None
return user
# --- FastMCP Server avec auth intégrée ---
mcp = FastMCP(
name="TwitterMCP",
stateless_http=True,
auth=mcp_auth
)
@mcp.tool()
async def hello() -> str:
"""Fonction de test MCP"""
return "Hello depuis FastMCP 🎉"
@mcp.tool()
async def get_current_user(request: Request) -> Dict[str, Any]:
"""Retourne les informations de l'utilisateur authentifié"""
user = request.session.get('user', {})
return {
"username": user.get('username'),
"id": user.get('id'),
"name": user.get('name')
}
@mcp.tool()
async def tweet_something(request: Request, message: str) -> Dict[str, str]:
"""
Exemple d'outil qui pourrait tweeter (nécessiterait les bonnes permissions)
"""
user = request.session.get('user', {})
# Ici on pourrait utiliser le token stocké pour poster un tweet
return {
"status": "success",
"message": f"Tweet simulé de @{user.get('username', 'unknown')}: {message}"
}
# --- Routes OAuth ---
@app.get("/login")
async def login(request: Request):
"""Initie le processus d'authentification Twitter"""
try:
redirect_uri = request.url_for("auth_callback")
return await oauth.twitter.authorize_redirect(request, redirect_uri)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur lors de l'initialisation OAuth: {str(e)}")
@app.get("/callback")
async def auth_callback(request: Request) -> Dict[str, str]:
"""Gère le callback OAuth et stocke les infos utilisateur"""
try:
token = await oauth.twitter.authorize_access_token(request)
# Récupère les infos de l'utilisateur
user_req = await oauth.twitter.get(
"https://api.twitter.com/2/users/me",
token=token
)
if user_req.status_code != 200:
raise HTTPException(
status_code=400,
detail="Impossible de récupérer les informations utilisateur"
)
userinfo = user_req.json().get('data', {})
if not userinfo:
raise HTTPException(
status_code=400,
detail="Données utilisateur invalides"
)
# Stocke les infos utilisateur et le token en session
request.session['user'] = userinfo
request.session['token'] = token
return {"message": f"Connecté en tant que @{userinfo.get('username', 'utilisateur')}"}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur lors de l'authentification: {str(e)}")
@app.get("/logout")
async def logout(request: Request):
"""Déconnecte l'utilisateur"""
request.session.clear()
return {"message": "Déconnecté avec succès"}
@app.get("/profile")
async def get_profile(request: Request):
"""Retourne le profil de l'utilisateur connecté"""
user = request.session.get('user')
if not user:
raise HTTPException(status_code=401, detail="Non authentifié")
return {"user": user}
# --- Route de santé ---
@app.get("/health")
async def health_check():
"""Vérification de l'état de l'application"""
return {"status": "ok", "message": "Service opérationnel"}
# --- Intégration FastMCP dans FastAPI ---
app.mount("/mcp", mcp.app)