"""
Video Games MCP Server
A Model Context Protocol server that provides access to video game information
using the RAWG Video Games Database API through the FastMCP framework.
"""
import asyncio
import logging
from fastmcp import FastMCP
from typing import List, Dict, Any, Optional
from rawg_service import get_rawg_client, cleanup_rawg_client
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Create FastMCP instance
mcp = FastMCP("VideoGames MCP Server", version="1.0.0")
@mcp.tool
async def search_game_by_title(title: str, limit: int = 10) -> List[Dict[str, Any]]:
"""
Search for video games by title.
Args:
title: The game title to search for (e.g., "The Witcher 3", "Cyberpunk 2077")
limit: Maximum number of results to return (default: 10, max: 40)
Returns:
List of games with basic information including id, name, released date,
rating, platforms, and background image.
"""
try:
client = await get_rawg_client()
result = await client.search_games(title, limit)
games = []
for game in result.get("results", []):
game_info = {
"id": game.get("id"),
"name": game.get("name"),
"released": game.get("released"),
"rating": game.get("rating"),
"metacritic": game.get("metacritic"),
"platforms": [
p.get("platform", {}).get("name") for p in game.get("platforms", [])
],
"genres": [g.get("name") for g in game.get("genres", [])],
"background_image": game.get("background_image"),
"short_screenshots": [
s.get("image") for s in game.get("short_screenshots", [])
][:3],
}
games.append(game_info)
return games
except Exception as e:
logger.error(f"Error searching games: {str(e)}")
return [{"error": f"Failed to search games: {str(e)}"}]
@mcp.tool
async def get_game_details(game_id: int) -> Dict[str, Any]:
"""
Get detailed information about a specific video game.
Args:
game_id: The unique ID of the game to get details for
Returns:
Detailed game information including description, developers, publishers,
release dates, platforms, ratings, genres, tags, and screenshots.
"""
try:
client = await get_rawg_client()
game = await client.get_game_details(game_id)
game_details = {
"id": game.get("id"),
"name": game.get("name"),
"description": game.get("description_raw", "").strip(),
"released": game.get("released"),
"rating": game.get("rating"),
"rating_top": game.get("rating_top"),
"ratings_count": game.get("ratings_count"),
"metacritic": game.get("metacritic"),
"playtime": game.get("playtime"),
"developers": [d.get("name") for d in game.get("developers", [])],
"publishers": [p.get("name") for p in game.get("publishers", [])],
"platforms": [
p.get("platform", {}).get("name") for p in game.get("platforms", [])
],
"genres": [g.get("name") for g in game.get("genres", [])],
"tags": [t.get("name") for t in game.get("tags", [])][:10], # Limit tags
"esrb_rating": (
game.get("esrb_rating", {}).get("name")
if game.get("esrb_rating")
else None
),
"website": game.get("website"),
"background_image": game.get("background_image"),
"background_image_additional": game.get("background_image_additional"),
}
return game_details
except Exception as e:
logger.error(f"Error getting game details: {str(e)}")
return {"error": f"Failed to get game details: {str(e)}"}
@mcp.tool
async def list_genres() -> List[Dict[str, Any]]:
"""
Get a list of all available video game genres.
Returns:
List of genres with their ID, name, and game count.
"""
try:
client = await get_rawg_client()
result = await client.list_genres()
genres = []
for genre in result.get("results", []):
genre_info = {
"id": genre.get("id"),
"name": genre.get("name"),
"games_count": genre.get("games_count"),
"image_background": genre.get("image_background"),
}
genres.append(genre_info)
return genres
except Exception as e:
logger.error(f"Error listing genres: {str(e)}")
return [{"error": f"Failed to list genres: {str(e)}"}]
@mcp.tool
async def get_popular_games(
limit: int = 10, genre: Optional[str] = None
) -> List[Dict[str, Any]]:
"""
Get popular/highly-rated video games.
Args:
limit: Maximum number of games to return (default: 10, max: 40)
genre: Optional genre name to filter by (e.g., "Action", "RPG", "Strategy")
Returns:
List of popular games with basic information.
"""
try:
client = await get_rawg_client()
if genre:
# First get genre ID
genres_result = await client.list_genres()
genre_id = None
for g in genres_result.get("results", []):
if g.get("name", "").lower() == genre.lower():
genre_id = g.get("id")
break
if genre_id:
result = await client.get_games_by_genre(genre_id, limit)
else:
return [{"error": f"Genre '{genre}' not found"}]
else:
result = await client.get_popular_games(limit)
games = []
for game in result.get("results", []):
game_info = {
"id": game.get("id"),
"name": game.get("name"),
"released": game.get("released"),
"rating": game.get("rating"),
"metacritic": game.get("metacritic"),
"platforms": [
p.get("platform", {}).get("name") for p in game.get("platforms", [])
],
"genres": [g.get("name") for g in game.get("genres", [])],
"background_image": game.get("background_image"),
}
games.append(game_info)
return games
except Exception as e:
logger.error(f"Error getting popular games: {str(e)}")
return [{"error": f"Failed to get popular games: {str(e)}"}]
async def cleanup():
"""Cleanup resources when server shuts down"""
await cleanup_rawg_client()
if __name__ == "__main__":
try:
# Set up cleanup on exit
import atexit
atexit.register(lambda: asyncio.run(cleanup()))
# Run the MCP server
mcp.run()
except KeyboardInterrupt:
logger.info("Server stopped by user")
except Exception as e:
logger.error(f"Server error: {e}")
finally:
asyncio.run(cleanup())