Skip to main content
Glama

Ultra MCP-SS

by motebus
main.py23.5 kB
from fastapi import FastAPI, Depends, HTTPException, Response, Query from pydantic import BaseModel, Field import os import urllib.parse import httpx from fastapi_mcp import FastApiMCP from dotenv import load_dotenv app = FastAPI(title="ultra-mcp-ss") load_dotenv() YOUTUBE_API_KEY = os.environ.get("YOUTUBE_API_KEY") YOUTUBE_API_URL = "https://www.googleapis.com/youtube/v3/search" SMARTSCREEN_API_URL = "https://smartscreen.tv/api" SMARTSCREEN_SERVICE_TOKEN = os.environ.get("SS_SERVICE_TOKEN") async def search_youtube_video(query: str) -> str | None: """ Searches YouTube for the most relevant video and returns its ID. Returns None if no video is found or an error occurs. """ if not YOUTUBE_API_KEY: print("Error: YOUTUBE_API_KEY environment variable not set.") return None encoded_query = urllib.parse.quote_plus(query) params = { "part": "snippet", "q": encoded_query, "key": YOUTUBE_API_KEY, "type": "video", "maxResults": 1, "order": "relevance" } try: async with httpx.AsyncClient() as client: response = await client.get(YOUTUBE_API_URL, params=params) response.raise_for_status() # Raise an exception for 4xx or 5xx status codes data = response.json() if data.get("items"): # Safely get the videoId video_id = data["items"][0].get("id", {}).get("videoId") return video_id else: # No results found for the query return None except httpx.RequestError as exc: print(f"An error occurred while requesting {exc.request.url!r}: {exc}") return None except httpx.HTTPStatusError as exc: print(f"Error response {exc.response.status_code} while requesting {exc.request.url!r}.") print(f"Response body: {exc.response.text}") return None except Exception as e: print(f"An unexpected error occurred: {e}") return None # Pydantic models for input class DropRequest(BaseModel): display: str item: str # the item to drop x: int = 0 # coordinate (if applicable) y: int = 0 # coordinate (if applicable) class NotifyRequest(BaseModel): display: str message: str priority: str# = Field(default="normal") class ToastRequest(BaseModel): display: str message: str heading: str icon: str transition: str duration: str = "5" class MarqueeRequest(BaseModel): display: str message: str duration: str = "30" color: str size: str = "3" bgcolor: str class TextRequest(BaseModel): display: str message: str duration: str = "30" color: str size: str = "3" bgcolor: str align: str frame: str animate: str aniduration: str = "2" class AppRequest(BaseModel): display: str url: str duration: str = "0" frame: str class TouchRequest(BaseModel): display: str option: str value: str# = "" class StatusRequest(BaseModel): display: str option: str value: str# = "" class DjRequest(BaseModel): display: str option: str value: str class SmartScreenClient: """ Client for calling the real SmartScreen API. It sends a POST request to the SmartScreen API endpoint with a payload containing: - 'to': { "ddn": "yview" } (using yview as the destination target) - 'data': (a command JSON as specified) It uses httpx in async mode. """ def __init__(self): self.api_url = SMARTSCREEN_API_URL self.token = SMARTSCREEN_SERVICE_TOKEN async def drop(self, item: str, display: str, x: int, y: int) -> bool: # Construct the command payload for "drop" command = { "cmd": "drop", "type": "url", "src": [item], # Use the passed URL instead of hardcoded one "duration": "0", # 0 means play to end "frame": "main", "animate": "fade", "aniduration": "2", "pmode": "loop", "bgcolor": "white" } payload = { "to": {"name": display}, "data": command } headers = { "Content-Type": "application/json", "Authorization": f"Bearer {self.token}" # Use class variable instead of hardcoded token } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) # Check for HTTP success (200 OK) if response.status_code != 200: print(f"SmartScreen drop failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen drop error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def notify(self, message: str, display: str, priority: str) -> bool: # Construct the command payload for "notify" command = { "cmd": "notify", "msg": message, "duration": "30", # set default duration; could be a parameter in the future "color": "blue", "size": "3" } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen notify failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen notify error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def toast(self, message: str, display: str, heading: str, icon: str, transition: str, duration: str) -> bool: # Construct the command payload for "toast" command = { "cmd": "toast", "msg": message, "heading": heading, "icon": icon, "transition": transition, "duration": duration } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen toast failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen toast error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def marquee(self, message: str, display: str, duration: str, color: str, size: str, bgcolor: str) -> bool: # Construct the command payload for "marquee" command = { "cmd": "marquee", "msg": message, "duration": duration, "color": color, "size": size, "bgcolor": bgcolor } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen marquee failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen marquee error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def text(self, message: str, display: str, duration: str, color: str, size: str, bgcolor: str, align: str, frame: str, animate: str, aniduration: str) -> bool: # Construct the command payload for "text" command = { "cmd": "text", "msg": message, "duration": duration, "color": color, "size": size, "bgcolor": bgcolor, "align": align, "frame": frame, "animate": animate, "aniduration": aniduration } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen text failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen text error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def app(self, url: str, display: str, duration: str, frame: str) -> bool: # Construct the command payload for "app" command = { "cmd": "app", "url": url, "duration": duration, "frame": frame } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen app failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen app error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def touch(self, option: str, display: str, value: str) -> bool: # Construct the command payload for "touch" command = { "cmd": "touch", "option": option, "value": value } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen touch failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen touch error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def status(self, option: str, display: str, value: str) -> bool: # Construct the command payload for "status" command = { "cmd": "status", "option": option, "value": value } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen status failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen status error: {result.get('ErrMsg', 'Unknown error')}") return False return True async def dj(self, option: str, display: str, value: str) -> bool: # Construct the command payload for "dj" command = { "cmd": "dj", "option": option, "value": value } payload = { "to": {"name": display}, "data": command } headers = { "Authorization": f"Bearer {self.token}" } async with httpx.AsyncClient() as client: response = await client.post(self.api_url, headers=headers, json=payload) if response.status_code != 200: print(f"SmartScreen dj failed: {response.text}") return False result = response.json() if result.get("ErrCode", -1) != 0: print(f"SmartScreen dj error: {result.get('ErrMsg', 'Unknown error')}") return False return True # Create an instance of our SmartScreen client. smartscreen_client = SmartScreenClient() @app.head("/mcp") async def mcp_head(): return Response(status_code=200) @app.get("/search-youtube", summary="Search YouTube Video", response_description="URL of the most relevant YouTube video") async def get_youtube_video_link( query: str = Query(..., description="The search query for YouTube.") ): """ Accepts a search query and returns the YouTube link for the most relevant video found. """ if not YOUTUBE_API_KEY: raise HTTPException(status_code=500, detail="API key configuration error. Please check server logs.") video_id = await search_youtube_video(query) if video_id: # Construct the standard YouTube URL video_url = f"https://www.youtube.com/watch?v={video_id}" return {"video_url": video_url} else: # Handles cases where search failed or no results were found raise HTTPException(status_code=404, detail=f"No relevant YouTube video found for query: '{query}'") @app.get("/test", tags=["test"]) async def test_endpoint(): """ Test endpoint to check if the API is running. """ return {"message": "My name is MCP Vincent, and I am here to help you."} # API endpoint for drop command @app.post("/drop") async def drop_item(request: DropRequest): """ Drop an item onto the SmartScreen at the specified coordinates. MCP Tool Exposure: This endpoint is exposed via MCP as the 'drop' tool. What it does: Displays an image or video on the screen. Why/When to use: Use it whenever you want to show media content—like a video, slideshow, or static image—on a particular screen area. Key options: • type: "url" (URLs for images/videos) • src: one or more URLs of the media files • duration: how long to display/play (0 = play to end) • frame: on which screen area (e.g., main, t1, t2) • animate: animation style (fade, slide, etc.) • pmode: playback mode (“loop” or “random”) """ success = await smartscreen_client.drop(item=request.item,x=request.x,y=request.y,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to drop item") return {"message": "Item dropped successfully", "item": request.item, "x": request.x, "y": request.y} # API endpoint for notify command @app.post("/notify") async def notify_user(request: NotifyRequest): """ Send a notification message to the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'notify' tool. What it does: Shows a notification banner containing simple text. Why/When to use: If you want to display a short message or alert without interrupting the main screen content. Key options: • msg: notification text • duration: how long to display • color, size: appearance settings for text """ success = await smartscreen_client.notify(message=request.message,priority=request.priority,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to send notification") return {"message": "Notification sent successfully", "notification": request.message} # New API endpoints for additional SmartScreen commands @app.post("/toast") async def toast_message(request: ToastRequest): """ Display a toast message on the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'toast' tool. What it does: Shows a popup message with optional heading, icon, and transition effects. Why/When to use: Ideal for quick, transient notes (toasts) that appear and then disappear without disturbing the main content. Key options: • msg: main text content • heading: optional title/heading • icon: can be info, warning, error, or success • transition: how it appears (plain, fade, slide) • duration: how long the toast stays visible """ success = await smartscreen_client.toast(message=request.message,heading=request.heading, icon=request.icon,transition=request.transition,duration=request.duration,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to show toast message") return {"message": "Toast displayed successfully", "toast": request.message} @app.post("/marquee") async def marquee_text(request: MarqueeRequest): """ Display scrolling marquee text on the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'marquee' tool. What it does: Displays scrolling text across the screen. Why/When to use: Useful for announcements or continuously running messages (e.g., news ticker, short updates) in a marquee format. Key options: • msg: marquee text • duration: how long to keep scrolling • color, size: text styling • bgcolor: background color """ success = await smartscreen_client.marquee(message=request.message,duration=request.duration, color=request.color,size=request.size,bgcolor=request.bgcolor,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to show marquee text") return {"message": "Marquee displayed successfully", "marquee": request.message} @app.post("/text") async def display_text(request: TextRequest): """ Display text on the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'text' tool. What it does: Shows a static block of text with customizable appearance and alignment. Why/When to use: Use to render text that remains on the screen for a set duration, possibly in a specific frame or layout region. Key options: • msg: text to display • duration: display length (seconds) • color, size, bgcolor: styling • align: left, center, or right • animate: how text appears (fade, slide, etc.) """ success = await smartscreen_client.text(message=request.message,duration=request.duration, color=request.color,size=request.size,bgcolor=request.bgcolor, align=request.align,frame=request.frame,animate=request.animate, aniduration=request.aniduration,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to show text") return {"message": "Text displayed successfully", "text": request.message} @app.post("/app") async def launch_app(request: AppRequest): """ Launch a web application on the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'app' tool. What it does: Loads and displays a webpage within a specified frame. Why/When to use: If you need to embed a browser view or show dynamic web content on the SmartScreen. Key options: • url: the webpage’s URL • duration: how long to display (0 = indefinite) • frame: target screen region """ success = await smartscreen_client.app(url=request.url,duration=request.duration,frame=request.frame,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to launch app") return {"message": "App launched successfully", "url": request.url} @app.post("/touch") async def touch_command(request: TouchRequest): """ Send a touch command to control the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'touch' tool. What it does: Sends control commands like play/pause, next/previous, volume up/down, or console commands. Why/When to use: Ideal for interactive control of media playback or SmartScreen settings without reloading the main content. Key options: • option: specifies the control action (playnext, mute, console, etc.) • value: additional parameter if needed (e.g., console sub-command) """ success = await smartscreen_client.touch(option=request.option,value=request.value,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to execute touch command") return {"message": "Touch command executed successfully", "option": request.option, "value": request.value} @app.post("/status") async def status_command(request: StatusRequest): """ Get or set status information on the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'status' tool. What it does: Requests status information from the SmartScreen, such as version, frame usage, or device info. Why/When to use: If you need to check what’s currently playing, or retrieve system details (e.g., debugging, system health checks). Key options: • option: type of status (frame, ver, devinfo) • value: additional context (like which frame) """ success = await smartscreen_client.status(option=request.option,value=request.value,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to execute status command") return {"message": "Status command executed successfully", "option": request.option, "value": request.value} @app.post("/dj") async def dj_command(request: DjRequest): """ Execute a DJ command on the SmartScreen. MCP Tool Exposure: This endpoint is exposed via MCP as the 'dj' tool. What it does: Manages higher-level features like scheduling commands, changing startup behavior, toggling kiosk mode, or updating the logo. Why/When to use: When you want to schedule commands (to run at startup or a specific time), change the logo, or restart the SmartScreen service. Key options: • logo: sets a new logo URL • kiosk: toggles kiosk mode on/off • startcmd/schcmd: schedule commands for startup or specific time triggers • restart: restarts the SmartScreen. """ success = await smartscreen_client.dj(option=request.option, value=request.value,display=request.display) if not success: raise HTTPException(status_code=500, detail="Failed to execute DJ command") return {"message": "DJ command executed successfully", "option": request.option, "value": request.value} mcp = FastApiMCP( app, name="ultra-mcp-ss" ) mcp.mount() # mounts at '/mcp' by default if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/motebus/ultra-mcp-ss'

If you have feedback or need assistance with the MCP directory API, please join our Discord server