File size: 11,241 Bytes
d4b8525 |
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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
import gradio as gr
import requests
import json
import pandas as pd
from datetime import datetime
# Your Modal MCP Server URL
MODAL_MCP_URL = "https://koyeliaghoshroy1--mcp-stock-analysis-server-web-app.modal.run"
def test_connection():
"""Test connection to Modal MCP server"""
try:
response = requests.get(f"{MODAL_MCP_URL}/health", timeout=10)
if response.status_code == 200:
return "β
Connected to Modal MCP Server", response.json()
else:
return f"β Connection failed: {response.status_code}", {}
except Exception as e:
return f"β Connection error: {str(e)}", {}
def get_available_tools():
"""Get available MCP tools from Modal server"""
try:
response = requests.get(f"{MODAL_MCP_URL}/tools", timeout=10)
if response.status_code == 200:
data = response.json()
tools = data.get("tools", [])
return "β
Tools loaded successfully", tools
else:
return f"β Failed to load tools: {response.status_code}", []
except Exception as e:
return f"β Tools error: {str(e)}", []
def get_stock_price(symbol):
"""Call Modal MCP server to get stock price"""
if not symbol.strip():
return "β Please enter a stock symbol", {}
try:
response = requests.post(
f"{MODAL_MCP_URL}/call",
json={
"name": "get_stock_price",
"arguments": {"symbol": symbol.strip().upper()}
},
timeout=30
)
if response.status_code == 200:
data = response.json()
if data.get("success"):
# Parse the result from the MCP server
result_str = data.get("result", ["{}"])[0]
result = json.loads(result_str)
if "error" in result:
return f"β {result['error']}", result
else:
status = f"β
Stock data retrieved for {result.get('symbol', symbol)}"
return status, result
else:
return f"β {data.get('error', 'Unknown error')}", data
else:
return f"β Request failed: {response.status_code}", {}
except Exception as e:
return f"β Error: {str(e)}", {}
def analyze_stock_comprehensive(symbol):
"""Call Modal MCP server for comprehensive stock analysis"""
if not symbol.strip():
return "β Please enter a stock symbol", {}
try:
response = requests.post(
f"{MODAL_MCP_URL}/call",
json={
"name": "analyze_stock_comprehensive",
"arguments": {"symbol": symbol.strip().upper()}
},
timeout=30
)
if response.status_code == 200:
data = response.json()
if data.get("success"):
# Parse the result from the MCP server
result_str = data.get("result", ["{}"])[0]
result = json.loads(result_str)
if "error" in result:
return f"β {result['error']}", result
else:
status = f"β
Analysis complete for {result.get('symbol', symbol)}"
return status, result
else:
return f"β {data.get('error', 'Unknown error')}", data
else:
return f"β Request failed: {response.status_code}", {}
except Exception as e:
return f"β Error: {str(e)}", {}
def format_stock_data(data):
"""Format stock data for better display"""
if not data or "error" in data:
return data
# Create a formatted summary
summary = {
"Company": data.get("company_name", "N/A"),
"Symbol": data.get("symbol", "N/A"),
"Current Price": f"${data.get('current_price', 0):.2f}",
"Market Cap": f"${data.get('market_cap', 0):,}" if data.get('market_cap') else "N/A",
"Sector": data.get("sector", "N/A")
}
# Add analysis-specific fields if they exist
if "investment_score" in data:
summary.update({
"Investment Score": f"{data.get('investment_score', 0)}/100",
"Recommendation": data.get("recommendation", "N/A"),
"YTD Return": f"{data.get('ytd_return', 0):.2f}%",
"P/E Ratio": data.get("pe_ratio", "N/A")
})
return summary
# Custom CSS for better styling
custom_css = """
.gradio-container {
font-family: 'Arial', sans-serif;
}
.tab-nav button {
font-weight: bold;
}
.json-display {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 0.375rem;
padding: 1rem;
}
"""
# Create the Gradio interface
with gr.Blocks(
title="MCP Stock Analysis - Hackathon Entry",
theme=gr.themes.Soft(),
css=custom_css
) as demo:
# Header
gr.Markdown("""
# π MCP Stock Analysis Server
## Hugging Face Gradio MCP Hackathon Entry
**Architecture**: Gradio Frontend (Hugging Face Spaces) + Modal MCP Backend
This application demonstrates the **Model Context Protocol (MCP)** by connecting a Gradio interface
to a Modal-hosted MCP server for real-time stock analysis.
""")
# Connection Status
with gr.Row():
connection_btn = gr.Button("π Test Connection", variant="secondary")
connection_status = gr.Textbox(label="Connection Status", interactive=False)
# Main Interface Tabs
with gr.Tabs():
# Stock Price Tab
with gr.TabItem("π Stock Price Lookup"):
gr.Markdown("Get real-time stock price and basic company information.")
with gr.Row():
with gr.Column(scale=2):
price_symbol = gr.Textbox(
label="Stock Symbol",
placeholder="Enter symbol (e.g., AAPL, TSLA, GOOGL)",
value="AAPL"
)
with gr.Column(scale=1):
price_btn = gr.Button("Get Price", variant="primary", size="lg")
price_status = gr.Textbox(label="Status", interactive=False)
price_result = gr.JSON(label="Stock Price Data")
price_summary = gr.JSON(label="Formatted Summary")
# Stock Analysis Tab
with gr.TabItem("π Comprehensive Analysis"):
gr.Markdown("Get detailed stock analysis with investment scoring and recommendations.")
with gr.Row():
with gr.Column(scale=2):
analysis_symbol = gr.Textbox(
label="Stock Symbol",
placeholder="Enter symbol (e.g., AAPL, TSLA, GOOGL)",
value="TSLA"
)
with gr.Column(scale=1):
analysis_btn = gr.Button("Analyze Stock", variant="primary", size="lg")
analysis_status = gr.Textbox(label="Status", interactive=False)
analysis_result = gr.JSON(label="Full Analysis Data")
analysis_summary = gr.JSON(label="Investment Summary")
# MCP Tools Tab
with gr.TabItem("π οΈ MCP Tools"):
gr.Markdown("View available MCP tools and server information.")
tools_btn = gr.Button("Load Available Tools", variant="secondary")
tools_status = gr.Textbox(label="Status", interactive=False)
tools_result = gr.JSON(label="Available MCP Tools")
# About Tab
with gr.TabItem("βΉοΈ About"):
gr.Markdown("""
## About This Project
### π Hackathon Entry
- **Event**: Hugging Face Gradio MCP Hackathon
- **Architecture**: Distributed MCP implementation
- **Frontend**: Gradio on Hugging Face Spaces
- **Backend**: Modal MCP Server with stock analysis tools
### π§ Technical Implementation
- **MCP Protocol**: Implements Model Context Protocol for tool discovery and execution
- **Real-time Data**: Uses yfinance for live stock market data
- **Cloud Architecture**: Scalable backend on Modal with Gradio frontend
- **RESTful API**: Standard HTTP/JSON communication between components
### π Features
- **Stock Price Lookup**: Real-time price, market cap, sector information
- **Investment Analysis**: Scoring algorithm with buy/hold/sell recommendations
- **Smart Ticker Search**: Company name to ticker symbol mapping
- **Error Handling**: Robust error handling and user feedback
### π οΈ MCP Tools Available
1. `get_stock_price` - Retrieve current stock price and basic info
2. `analyze_stock_comprehensive` - Full analysis with investment scoring
3. `smart_ticker_search` - Convert company names to ticker symbols
### π Live Endpoints
- **Modal MCP Server**: `{MODAL_MCP_URL}`
- **Health Check**: `{MODAL_MCP_URL}/health`
- **Tools Discovery**: `{MODAL_MCP_URL}/tools`
- **Tool Execution**: `{MODAL_MCP_URL}/call`
### π‘ Usage Examples
Try these stock symbols: **AAPL**, **TSLA**, **GOOGL**, **MSFT**, **AMZN**, **META**
Or company names: **apple**, **tesla**, **microsoft**, **amazon**
""")
# Event Handlers
# Connection test
def handle_connection_test():
status, data = test_connection()
return status
connection_btn.click(
fn=handle_connection_test,
outputs=connection_status
)
# Stock price lookup
def handle_price_lookup(symbol):
status, data = get_stock_price(symbol)
summary = format_stock_data(data) if data else {}
return status, data, summary
price_btn.click(
fn=handle_price_lookup,
inputs=price_symbol,
outputs=[price_status, price_result, price_summary]
)
# Stock analysis
def handle_analysis(symbol):
status, data = analyze_stock_comprehensive(symbol)
summary = format_stock_data(data) if data else {}
return status, data, summary
analysis_btn.click(
fn=handle_analysis,
inputs=analysis_symbol,
outputs=[analysis_status, analysis_result, analysis_summary]
)
# Tools discovery
def handle_tools_discovery():
status, tools = get_available_tools()
return status, tools
tools_btn.click(
fn=handle_tools_discovery,
outputs=[tools_status, tools_result]
)
# Auto-test connection on load
demo.load(
fn=handle_connection_test,
outputs=connection_status
)
# Launch the app
if __name__ == "__main__":
demo.launch(
share=False,
server_name="0.0.0.0",
server_port=7860
) |