Spaces:
Running
Running
| import json | |
| import warnings | |
| import asyncio | |
| from textblob import TextBlob | |
| import gradio as gr | |
| from mcp import Tool | |
| from mcp.server import Server | |
| from mcp.types import TextContent | |
| import uvicorn | |
| import threading | |
| # ============================================================ | |
| # Suppress harmless asyncio warnings from Gradio | |
| # ============================================================ | |
| warnings.filterwarnings("ignore", message=".*Invalid file descriptor.*") | |
| # ============================================================ | |
| # Shared sentiment analysis function | |
| # ============================================================ | |
| def sentiment_analysis(text: str) -> dict: | |
| """Analyze the sentiment of the given text.""" | |
| if not text or not text.strip(): | |
| return { | |
| "error": "No text provided", | |
| "polarity": 0, | |
| "subjectivity": 0, | |
| "assessment": "neutral" | |
| } | |
| blob = TextBlob(text) | |
| sentiment = blob.sentiment | |
| return { | |
| "polarity": round(sentiment.polarity, 2), | |
| "subjectivity": round(sentiment.subjectivity, 2), | |
| "assessment": ( | |
| "positive" if sentiment.polarity > 0 else | |
| "negative" if sentiment.polarity < 0 else | |
| "neutral" | |
| ) | |
| } | |
| # ============================================================ | |
| # Gradio wrapper for JSON output | |
| # ============================================================ | |
| def sentiment_analysis_gradio(text: str) -> str: | |
| result = sentiment_analysis(text) | |
| return json.dumps(result, indent=2) | |
| # ============================================================ | |
| # Create MCP server | |
| # ============================================================ | |
| mcp_server = Server("sentiment-analysis") | |
| async def handle_list_tools() -> list[Tool]: | |
| return [ | |
| Tool( | |
| name="analyze_sentiment", | |
| description=( | |
| "Analyze the sentiment of text using TextBlob. " | |
| "Returns polarity (-1 to 1), subjectivity (0 to 1), " | |
| "and assessment (positive/negative/neutral)." | |
| ), | |
| inputSchema={ | |
| "type": "object", | |
| "properties": { | |
| "text": { | |
| "type": "string", | |
| "description": "The text to analyze for sentiment" | |
| } | |
| }, | |
| "required": ["text"] | |
| } | |
| ) | |
| ] | |
| async def handle_call_tool(name: str, arguments: dict) -> list[TextContent]: | |
| if name == "analyze_sentiment": | |
| text = arguments.get("text", "") | |
| result = sentiment_analysis(text) | |
| return [TextContent(type="text", text=json.dumps(result, indent=2))] | |
| return [TextContent(type="text", text=f"Unknown tool: {name}")] | |
| # ============================================================ | |
| # MCP ASGI app (Server object is already ASGI-compatible) | |
| # ============================================================ | |
| mcp_asgi_app = mcp_server | |
| # ============================================================ | |
| # Gradio interface | |
| # ============================================================ | |
| demo = gr.Interface( | |
| fn=sentiment_analysis_gradio, | |
| inputs=gr.Textbox( | |
| placeholder="Enter text to analyze...", | |
| label="Input Text", | |
| lines=5 | |
| ), | |
| outputs=gr.Textbox( | |
| label="Sentiment Analysis Result (JSON)", | |
| lines=10 | |
| ), | |
| title="Text Sentiment Analysis", | |
| description="Analyze the sentiment of text using TextBlob. MCP server available at /sse", | |
| examples=[ | |
| ["I absolutely love this product! It's amazing and works perfectly."], | |
| ["This is the worst experience I've ever had. Terrible service."], | |
| ["The weather today is cloudy with a chance of rain."], | |
| ] | |
| ) | |
| # # ============================================================ | |
| # # Main async runner for MCP + Gradio | |
| # # ============================================================ | |
| # async def main(): | |
| # # Run MCP server | |
| # print("main line 0") | |
| # config = uvicorn.Config(mcp_asgi_app, host="0.0.0.0", port=7860, log_level="info") | |
| # print("main line 1") | |
| # server = uvicorn.Server(config) | |
| # print("main line 2") | |
| # mcp_task = asyncio.create_task(server.serve()) | |
| # print("main line 3") | |
| # # Launch Gradio UI | |
| # demo.launch( | |
| # server_name="0.0.0.0", | |
| # server_port=8000, | |
| # prevent_thread_lock=True, | |
| # ssr_mode=False | |
| # ) | |
| # print("main line 4") | |
| # # Keep MCP server alive | |
| # await mcp_task | |
| # print("main line 5") | |
| # # ============================================================ | |
| # # Entry point | |
| # # ============================================================ | |
| # if __name__ == "__main__": | |
| # # print("=" * 60) | |
| # # print("Starting Sentiment Analysis Services") | |
| # # print("=" * 60) | |
| # # print("π MCP Server: http://0.0.0.0:7860/sse") | |
| # # print("π Gradio UI : http://0.0.0.0:8000") | |
| # # print("=" * 60) | |
| # asyncio.run(main()) | |
| def run_mcp_server(): | |
| async def start(): | |
| config = uvicorn.Config(mcp_asgi_app, host="0.0.0.0", port=8000) | |
| server = uvicorn.Server(config) | |
| await server.serve() | |
| loop = asyncio.new_event_loop() | |
| asyncio.set_event_loop(loop) | |
| loop.run_until_complete(start()) | |
| if __name__ == "__main__": | |
| threading.Thread(target=run_mcp_server, daemon=True).start() | |
| demo.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False) | |