mcp-test / app.py
alan5543
update
bcfd645
# hf_spaces_mcp_server.py
from mcp.server.fastmcp import FastMCP
from gradio_client import Client
import os
import sys
import asyncio
import logging
import uvicorn # Correctly imported
# Configure basic logging to see server activity
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
logger = logging.getLogger(__name__)
# The port that Hugging Face Spaces expects our app to listen on.
# It injects this as an environment variable 'PORT'.
# Default to 7860 if PORT is not set (e.g., for local testing outside HF Spaces).
HOST_PORT = int(os.getenv("PORT", 7860))
HOST_ADDRESS = os.getenv("HOST", "0.0.0.0") # Bind to all interfaces
# Initialize FastMCP. The host/port settings passed here will be used by
# mcp.streamable_http_app() to configure its internal server if not mounted to FastAPI.
# However, when passed to uvicorn.run(), uvicorn's host/port will take precedence.
# It's good practice to keep them consistent or omit from here if uvicorn dictates.
# For simplicity, we'll keep them here as they are also part of MCP server's self-description.
mcp = FastMCP(
name="GradioSpacesTools",
host=HOST_ADDRESS,
port=HOST_PORT
)
clients = {}
def get_client(space_id: str) -> Client:
"""Get or create a Gradio client for the specified space."""
if space_id not in clients:
logger.info(f"Creating Gradio client for Space ID: {space_id}")
clients[space_id] = Client(space_id)
return clients[space_id]
@mcp.tool()
async def generate_image(prompt: str, space_id: str = "ysharma/SanaSprint") -> str:
"""Generate an image using Flux.
Args:
prompt (str): Text prompt describing the image to generate
space_id (str): HuggingFace Space ID to use
Returns:
str: The URL of the generated image.
"""
logger.info(f"Generating image for prompt: '{prompt}' using Space: '{space_id}'")
client = get_client(space_id)
try:
result = await client.predict(
prompt=prompt,
model_size="1.6B",
seed=0,
randomize_seed=True,
width=1024,
height=1024,
guidance_scale=4.5,
num_inference_steps=2,
api_name="/infer"
)
logger.info(f"Image generation successful. Result: {result}")
return result
except Exception as e:
logger.error(f"Error generating image: {e}")
return f"Error generating image: {e}"
@mcp.tool()
async def run_dia_tts(prompt: str, space_id: str = "ysharma/Dia-1.6B") -> str:
"""Text-to-Speech Synthesis.
Args:
prompt (str): Text prompt describing the conversation between speakers S1, S2
space_id (str): HuggingFace Space ID to use
Returns:
str: The URL of the generated audio.
"""
logger.info(f"Performing TTS for prompt: '{prompt}' using Space: '{space_id}'")
client = get_client(space_id)
try:
result = await client.predict(
text_input=f"""{prompt}""",
audio_prompt_input=None,
max_new_tokens=3072,
cfg_scale=3,
temperature=1.3,
top_p=0.95,
cfg_filter_top_k=30,
speed_factor=0.94,
api_name="/generate_audio"
)
logger.info(f"TTS successful. Result: {result}")
return result
except Exception as e:
logger.error(f"Error performing TTS: {e}")
return f"Error performing TTS: {e}"
if __name__ == "__main__":
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
logger.info(f"Starting GradioSpacesTools MCP server via Uvicorn on http://{HOST_ADDRESS}:{HOST_PORT}")
# The correct way to get the FastMCP ASGI application for streamable-http
# as indicated by the article and FastMCP's internal structure.
# Uvicorn will then run this ASGI application.
uvicorn.run(
mcp.streamable_http_app(), # <-- Use this method! It returns the ASGI app.
host=HOST_ADDRESS,
port=HOST_PORT,
log_level="info", # Set logging level for Uvicorn itself
loop="asyncio" # Ensure Uvicorn uses the asyncio event loop
)
logger.info("GradioSpacesTools MCP server stopped.")