File size: 5,524 Bytes
81fc224
4ca99ed
3700e85
 
4ca99ed
81fc224
 
 
4ca99ed
29c0bbe
81fc224
4ca99ed
 
 
 
3700e85
4ca99ed
 
 
81fc224
4ca99ed
81fc224
 
 
 
 
4ca99ed
81fc224
 
 
 
 
3700e85
81fc224
 
3700e85
4ca99ed
 
 
 
81fc224
 
4ca99ed
 
 
81fc224
4ca99ed
 
81fc224
4ca99ed
 
 
81fc224
 
 
4ca99ed
81fc224
 
 
4ca99ed
 
 
 
 
81fc224
 
 
 
 
4ca99ed
81fc224
 
4ca99ed
 
81fc224
 
 
 
4ca99ed
81fc224
4ca99ed
 
81fc224
 
 
4ca99ed
b68b1e6
4ca99ed
b68b1e6
3700e85
4ca99ed
 
 
9d3b6d9
81fc224
 
3700e85
4ca99ed
 
81fc224
 
 
4ca99ed
81fc224
 
4ca99ed
81fc224
4ca99ed
 
 
 
81fc224
 
29c0bbe
 
 
 
 
 
 
 
 
 
 
 
bafe35d
29c0bbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5c1b83
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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")

@mcp_server.list_tools()
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"]
            }
        )
    ]

@mcp_server.call_tool()
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)