Vladt-Tempest commited on
Commit
d3a578b
·
1 Parent(s): 881d8d0

Prueba en HF

Browse files
README.md CHANGED
@@ -1,16 +1,72 @@
1
- ---
2
- title: MCP Crypto
3
- emoji: 💻
4
- colorFrom: gray
5
- colorTo: indigo
6
- sdk: gradio
7
- sdk_version: 5.32.1
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- short_description: Agents-MCP-Hackathon
12
- tags:
13
- - mcp-server-track
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  ---
15
 
16
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ # Financial Analyst con Claude (Anthropic) y MCP
2
+
3
+ Una aplicación de análisis financiero que utiliza servidores MCP para análisis de acciones y generación de reportes, con interfaz web Gradio y modelo Claude (Anthropic).
4
+
5
+ ## Casos de uso
6
+
7
+ - Obtener precios de acciones en tiempo real, datos históricos y recomendaciones de analistas
8
+ - Generar reportes financieros breves y profesionales
9
+ - Guardar reportes en archivos
10
+
11
+ ## Arquitectura
12
+
13
+ ```
14
+ ├── mcp_servers/
15
+ │ ├── stdio_yfinance_server.py # Datos de Yahoo Finance (precios, históricos)
16
+ │ ├── stdio_report_gen_server.py # Generación de reportes con Claude (Anthropic)
17
+ │ └── stdio_save_report_server.py # Guardado de reportes
18
+ ├── app.py # Interfaz Gradio (Hugging Face compatible)
19
+ ├── requirements.txt # Dependencias
20
+ ```
21
+
22
+ ### Aplicación principal
23
+ - **app.py**: Interfaz Gradio que se comunica con los MCP servers por stdio. Permite ingresar información financiera y obtener un reporte generado por Claude.
24
+
25
+ ### MCP Servers
26
+ - **stdio_yfinance_server.py**: Obtiene datos de Yahoo Finance (precios, históricos, recomendaciones)
27
+ - **stdio_report_gen_server.py**: Genera reportes usando Claude (Anthropic)
28
+ - **stdio_save_report_server.py**: Guarda reportes en archivos locales
29
+
30
+ ## Instalación
31
+
32
+ ```bash
33
+ pip install -r requirements.txt
34
+ ```
35
+
36
+ ## Configuración de entorno
37
+
38
+ Debes tener una API Key de Anthropic:
39
+
40
+ ```bash
41
+ set ANTHROPIC_API_KEY="tu_api_key_de_anthropic"
42
+ ```
43
+
44
+ (En Linux/Mac usa `export` en vez de `set`)
45
+
46
+ ## Uso
47
+
48
+ 1. Ejecuta la aplicación:
49
+ ```bash
50
+ python app.py
51
+ ```
52
+ 2. Se abrirá una interfaz web Gradio donde puedes ingresar información financiera y obtener el reporte generado.
53
+
54
+ ## Integración MCP
55
+
56
+ La aplicación utiliza los MCP servers ubicados en `mcp_servers/` para cada función:
57
+ - **Análisis de datos financieros:** `stdio_yfinance_server.py`
58
+ - **Generación de reportes:** `stdio_report_gen_server.py` (usa Claude)
59
+ - **Guardado de reportes:** `stdio_save_report_server.py`
60
+
61
+ Puedes lanzar cada servidor MCP individualmente si lo deseas:
62
+ ```bash
63
+ python mcp_servers/stdio_yfinance_server.py
64
+ python mcp_servers/stdio_report_gen_server.py
65
+ python mcp_servers/stdio_save_report_server.py
66
+ ```
67
+
68
+ La interfaz Gradio (`app.py`) se comunica con el servidor de generación de reportes por stdio. Puedes adaptar la integración para usar los otros MCP servers según tus necesidades.
69
+
70
  ---
71
 
72
+ **Desarrollado para Hugging Face Spaces y entornos compatibles.**
app.py CHANGED
@@ -1,34 +1,89 @@
 
1
  import gradio as gr
2
- from textblob import TextBlob
3
-
4
- def sentiment_analysis(text: str) -> dict:
5
- """
6
- Analyze the sentiment of the given text.
7
-
8
- Args:
9
- text (str): The text to analyze
10
-
11
- Returns:
12
- dict: A dictionary containing polarity, subjectivity, and assessment
13
- """
14
- blob = TextBlob(text)
15
- sentiment = blob.sentiment
16
-
17
- return {
18
- "polarity": round(sentiment.polarity, 2), # -1 (negative) to 1 (positive)
19
- "subjectivity": round(sentiment.subjectivity, 2), # 0 (objective) to 1 (subjective)
20
- "assessment": "positive" if sentiment.polarity > 0 else "negative" if sentiment.polarity < 0 else "neutral"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- # Create the Gradio interface
24
- demo = gr.Interface(
25
- fn=sentiment_analysis,
26
- inputs=gr.Textbox(placeholder="Enter text to analyze..."),
27
- outputs=gr.JSON(),
28
- title="Text Sentiment Analysis",
29
- description="Analyze the sentiment of text using TextBlob"
30
- )
31
 
32
- # Launch the interface and MCP server
33
  if __name__ == "__main__":
34
- demo.launch(mcp_server=True)
 
1
+ import os
2
  import gradio as gr
3
+ import subprocess
4
+ import sys
5
+ import json
6
+ import threading
7
+ import time
8
+
9
+ # Ruta al server MCP
10
+ SERVER_PATH = os.path.abspath(os.path.join("mcp_servers", "stdio_report_gen_server.py"))
11
+
12
+ # Mantener el proceso del servidor MCP abierto durante toda la sesión
13
+ def start_mcp_server():
14
+ proc = subprocess.Popen(
15
+ [sys.executable, SERVER_PATH],
16
+ stdin=subprocess.PIPE,
17
+ stdout=subprocess.PIPE,
18
+ stderr=subprocess.PIPE,
19
+ text=True,
20
+ bufsize=1
21
+ )
22
+ print(f"[DEBUG] MCP STDIN: {proc.stdin}, STDOUT: {proc.stdout}, STDERR: {proc.stderr}")
23
+ return proc
24
+
25
+ mcp_proc = start_mcp_server()
26
+ lock = threading.Lock() # Para evitar condiciones de carrera en stdio
27
+
28
+ # Lee stderr de MCP en un hilo y guarda los logs
29
+ stderr_logs = []
30
+ def read_stderr(proc):
31
+ while True:
32
+ line = proc.stderr.readline()
33
+ if not line:
34
+ break
35
+ stderr_logs.append(line)
36
+ print("[MCP STDERR]", line.strip())
37
+
38
+ stderr_thread = threading.Thread(target=read_stderr, args=(mcp_proc,), daemon=True)
39
+ stderr_thread.start()
40
+
41
+ # Comunicación usando JSON-RPC mínimo con timeout y logs
42
+
43
+ def call_mcp_server(prompt):
44
+ request = {
45
+ "jsonrpc": "2.0",
46
+ "id": 1,
47
+ "method": "get_current_stock_price", # Cambiado para probar el método
48
+ "params": [prompt]
49
  }
50
+ try:
51
+ with lock:
52
+ print(f"[APP] Enviando petición al MCP: {json.dumps(request)}")
53
+ mcp_proc.stdin.write(json.dumps(request) + "\n")
54
+ mcp_proc.stdin.flush()
55
+ # Esperar respuesta con timeout
56
+ start_time = time.time()
57
+ response_line = ''
58
+ while True:
59
+ if mcp_proc.stdout.closed:
60
+ return "El servidor MCP se cerró inesperadamente."
61
+ if mcp_proc.stdout.readable():
62
+ response_line = mcp_proc.stdout.readline()
63
+ print(f"[APP] Recibido de MCP: {response_line}")
64
+ if response_line.strip():
65
+ break
66
+ if time.time() - start_time > 30:
67
+ return f"Timeout esperando respuesta del servidor MCP. Logs recientes: {''.join(stderr_logs[-10:])}"
68
+ time.sleep(0.1)
69
+ if not response_line:
70
+ return "No se recibió respuesta del servidor MCP."
71
+ response = json.loads(response_line)
72
+ if "result" in response:
73
+ return response["result"]
74
+ elif "error" in response:
75
+ return f"Error del servidor MCP: {response['error']}\nLogs recientes: {''.join(stderr_logs[-10:])}"
76
+ else:
77
+ return f"Respuesta inesperada del servidor MCP. Logs recientes: {''.join(stderr_logs[-10:])}"
78
+ except Exception as e:
79
+ return f"Error comunicando con el servidor MCP: {str(e)}\nLogs recientes: {''.join(stderr_logs[-10:])}"
80
 
81
+ with gr.Blocks() as demo:
82
+ gr.Markdown("# Generador de Reportes Financieros (Claude + MCP)")
83
+ prompt = gr.Textbox(label="Información financiera de la empresa")
84
+ output = gr.Textbox(label="Reporte generado")
85
+ btn = gr.Button("Generar reporte")
86
+ btn.click(fn=call_mcp_server, inputs=prompt, outputs=output)
 
 
87
 
 
88
  if __name__ == "__main__":
89
+ demo.launch()
mcp_servers/stdio_report_gen_server.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mcp.server.fastmcp import FastMCP
2
+ import logging
3
+ import os
4
+ from typing import List
5
+ import anthropic
6
+ import sys
7
+ import asyncio
8
+
9
+ # Configure logging to only show errors
10
+ logging.basicConfig(level=logging.ERROR)
11
+
12
+ # Initialize FastMCP server for report generation
13
+ mcp = FastMCP("test_server")
14
+
15
+ print("[MCP] Servidor de generación de reportes iniciado y esperando peticiones...", file=sys.stderr)
16
+
17
+ # Initialize Anthropic client for report content generation
18
+ client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) # Usa variable de entorno
19
+
20
+ # System prompt to guide the LLM in generating financial reports
21
+ system_prompt = "You are a professional financial analyst. Generate a very short report based on following information regarding different companies."
22
+
23
+ @mcp.tool()
24
+ def get_current_stock_price(symbol: str) -> str:
25
+ print(f"[MCP] Petición recibida: {symbol}", file=sys.stderr)
26
+ return f"Echo: {symbol}"
27
+
28
+ def run_report_server():
29
+ """Start the report generation MCP server using stdio transport"""
30
+ mcp.run(transport="stdio")
31
+
32
+ if __name__ == "__main__":
33
+ run_report_server()
mcp_servers/stdio_save_report_server.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mcp.server.fastmcp import FastMCP
2
+ import logging
3
+
4
+ # Configure logging to only show errors
5
+ logging.basicConfig(level=logging.ERROR)
6
+
7
+ # Initialize FastMCP server for saving reports
8
+ mcp = FastMCP("save-report")
9
+
10
+ @mcp.tool()
11
+ async def save_report(report: str, file_name: str="report.md") -> None:
12
+ """
13
+ Save the generated financial report to a file
14
+
15
+ Args:
16
+ report (str): The report content to be saved
17
+ file_name (str): The filename to save the report to. Defaults to "report.md"
18
+ """
19
+ # Append the report content to the specified file
20
+ with open(file_name, "a") as file:
21
+ file.write(report + "\n")
22
+ print("Report saved successfully!")
23
+
24
+ def run_save_report_server():
25
+ """Start the report saving MCP server using stdio transport"""
26
+ mcp.run(transport="stdio")
27
+
28
+ if __name__ == "__main__":
29
+ run_save_report_server()
mcp_servers/stdio_yfinance_server.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mcp.server.fastmcp import FastMCP
2
+ import logging
3
+ import yfinance as yf
4
+ import json
5
+
6
+ # Configure logging to only show errors
7
+ logging.basicConfig(level=logging.ERROR)
8
+
9
+ # Initialize FastMCP server for Yahoo Finance integration
10
+ mcp = FastMCP("yfinance")
11
+
12
+ # Tools are adapted from https://github.com/agno-agi/agno/blob/main/libs/agno/agno/tools/yfinance.py
13
+ @mcp.tool()
14
+ async def get_current_stock_price(symbol: str) -> str:
15
+ """
16
+ Use this function to get the current stock price for a given symbol.
17
+
18
+ Args:
19
+ symbol (str): The stock symbol.
20
+
21
+ Returns:
22
+ str: The current stock price or error message.
23
+ """
24
+ try:
25
+ stock = yf.Ticker(symbol)
26
+ current_price = stock.info.get("regularMarketPrice", stock.info.get("currentPrice"))
27
+ return f"{current_price:.4f}" if current_price else f"Could not fetch current price for {symbol}"
28
+ except Exception as e:
29
+ return f"Error fetching current price for {symbol}: {e}"
30
+
31
+ @mcp.tool()
32
+ async def get_historical_stock_prices(symbol: str, period: str = "1mo", interval: str = "1d") -> str:
33
+ """
34
+ Use this function to get the historical stock price for a given symbol.
35
+
36
+ Args:
37
+ symbol (str): The stock symbol.
38
+ period (str): The period for which to retrieve historical prices. Defaults to "1mo".
39
+ Valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
40
+ interval (str): The interval between data points. Defaults to "1d".
41
+ Valid intervals: 1d,5d,1wk,1mo,3mo
42
+
43
+ Returns:
44
+ str: The historical stock price or error message.
45
+ """
46
+ try:
47
+ stock = yf.Ticker(symbol)
48
+ historical_price = stock.history(period=period, interval=interval)
49
+ return historical_price.to_json(orient="index")
50
+ except Exception as e:
51
+ return f"Error fetching historical prices for {symbol}: {e}"
52
+
53
+ @mcp.tool()
54
+ async def get_analyst_recommendations(symbol: str) -> str:
55
+ """
56
+ Use this function to get analyst recommendations for a given stock symbol.
57
+
58
+ Args:
59
+ symbol (str): The stock symbol.
60
+
61
+ Returns:
62
+ str: JSON containing analyst recommendations or error message.
63
+ """
64
+ try:
65
+ stock = yf.Ticker(symbol)
66
+ recommendations = stock.recommendations
67
+ return recommendations.to_json(orient="index")
68
+ except Exception as e:
69
+ return f"Error fetching analyst recommendations for {symbol}: {e}"
70
+
71
+ def run_yfinance_server():
72
+ """Start the Yahoo Finance MCP server using stdio transport"""
73
+ mcp.run(transport="stdio")
74
+
75
+ if __name__ == "__main__":
76
+ run_yfinance_server()
requirements.txt CHANGED
@@ -1,2 +1,4 @@
1
- "gradio[mcp]"
2
- textblob
 
 
 
1
+ gradio
2
+ anthropic
3
+ mcp
4
+ yfinance