diff --git a/mcpuniverse/mcp/servers/google_sheets/server.py b/mcpuniverse/mcp/servers/google_sheets/server.py index e0a9ec23..7da08df6 100644 --- a/mcpuniverse/mcp/servers/google_sheets/server.py +++ b/mcpuniverse/mcp/servers/google_sheets/server.py @@ -7,6 +7,7 @@ # pylint: disable=broad-exception-caught,unused-argument,redefined-builtin import os import json +import sys from typing import List, Dict, Any, Optional from dataclasses import dataclass from contextlib import asynccontextmanager @@ -50,16 +51,16 @@ async def spreadsheet_lifespan(server: FastMCP) -> AsyncIterator[SpreadsheetCont SERVICE_ACCOUNT_PATH, scopes=SCOPES ) - print("Using service account authentication") - print(f"Working with Google Drive folder ID: {DRIVE_FOLDER_ID or 'Not specified'}") + print("Using service account authentication", file=sys.stderr) + print(f"Working with Google Drive folder ID: {DRIVE_FOLDER_ID or 'Not specified'}", file=sys.stderr) except Exception as e: - print(f"Error using service account authentication: {e}") - print("Falling back to OAuth flow") + print(f"Error using service account authentication: {e}", file=sys.stderr) + print("Falling back to OAuth flow", file=sys.stderr) creds = None # Fall back to OAuth flow if service account auth failed or not configured if not creds: - print("Using OAuth authentication flow") + print("Using OAuth authentication flow", file=sys.stderr) if os.path.exists(TOKEN_PATH): with open(TOKEN_PATH, "r", encoding="utf-8") as token: creds = Credentials.from_authorized_user_info(json.load(token), SCOPES) @@ -531,7 +532,7 @@ async def create_spreadsheet(title: str, ctx: Context = None) -> Dict[str, Any]: ).execute() spreadsheet_id = spreadsheet.get('spreadsheetId') - print(f"Spreadsheet created with ID: {spreadsheet_id}") + print(f"Spreadsheet created with ID: {spreadsheet_id}", file=sys.stderr) # If a folder_id is specified, move the spreadsheet to that folder if folder_id: @@ -552,9 +553,9 @@ async def create_spreadsheet(title: str, ctx: Context = None) -> Dict[str, Any]: fields='id, parents' ).execute() - print(f"Spreadsheet moved to folder with ID: {folder_id}") + print(f"Spreadsheet moved to folder with ID: {folder_id}", file=sys.stderr) except Exception as e: - print(f"Warning: Could not move spreadsheet to folder: {e}") + print(f"Warning: Could not move spreadsheet to folder: {e}", file=sys.stderr) return { 'spreadsheetId': spreadsheet_id, @@ -628,9 +629,9 @@ async def list_spreadsheets(ctx: Context = None) -> List[Dict[str, str]]: # If a specific folder is configured, search only in that folder if folder_id: query += f" and '{folder_id}' in parents" - print(f"Searching for spreadsheets in folder: {folder_id}") + print(f"Searching for spreadsheets in folder: {folder_id}", file=sys.stderr) else: - print("Searching for spreadsheets in 'My Drive'") + print("Searching for spreadsheets in 'My Drive'", file=sys.stderr) # List spreadsheets results = drive_service.files().list( diff --git a/mcpuniverse/mcp/servers/jina_scrape_llm_summary/server.py b/mcpuniverse/mcp/servers/jina_scrape_llm_summary/server.py index 686f94ec..cd6becf4 100644 --- a/mcpuniverse/mcp/servers/jina_scrape_llm_summary/server.py +++ b/mcpuniverse/mcp/servers/jina_scrape_llm_summary/server.py @@ -10,6 +10,7 @@ import json import os import subprocess +import sys from typing import Any, Dict import click @@ -81,7 +82,7 @@ async def scrape_and_extract_info( if not scrape_result["success"]: print( - f"Jina Scrape and Extract Info: Scraping failed: {scrape_result['error']}" + f"Jina Scrape and Extract Info: Scraping failed: {scrape_result['error']}", file=sys.stderr ) return { "success": False, @@ -215,12 +216,13 @@ async def scrape_url_with_jina( if attempt < len(retry_delays): delay = retry_delays[attempt] print( - f"Jina Scrape: Connection timeout, {delay}s before next attempt (attempt {attempt + 1})" + f"Jina Scrape: Connection timeout, {delay}s before next attempt (attempt {attempt + 1})", + file=sys.stderr ) await asyncio.sleep(delay) continue print( - f"Jina Scrape: Connection retry attempts exhausted, url: {url}" + f"Jina Scrape: Connection retry attempts exhausted, url: {url}", file=sys.stderr ) raise e @@ -229,12 +231,12 @@ async def scrape_url_with_jina( if attempt < len(retry_delays): delay = retry_delays[attempt] print( - f"Jina Scrape: Connection error: {e}, {delay}s before next attempt" + f"Jina Scrape: Connection error: {e}, {delay}s before next attempt", file=sys.stderr ) await asyncio.sleep(delay) continue print( - f"Jina Scrape: Connection retry attempts exhausted, url: {url}" + f"Jina Scrape: Connection retry attempts exhausted, url: {url}", file=sys.stderr ) raise e @@ -243,12 +245,13 @@ async def scrape_url_with_jina( if attempt < len(retry_delays): delay = retry_delays[attempt] print( - f"Jina Scrape: Read timeout, {delay}s before next attempt (attempt {attempt + 1})" + f"Jina Scrape: Read timeout, {delay}s before next attempt (attempt {attempt + 1})", + file=sys.stderr ) await asyncio.sleep(delay) continue print( - f"Jina Scrape: Read timeout retry attempts exhausted, url: {url}" + f"Jina Scrape: Read timeout retry attempts exhausted, url: {url}", file=sys.stderr ) raise e @@ -256,12 +259,12 @@ async def scrape_url_with_jina( if attempt < len(retry_delays): print( f"Jina Scrape: HTTP error: {e}, response.text: {response.text}, url: {url}, " - f"{delay}s before next attempt (attempt {attempt + 1})" + f"{delay}s before next attempt (attempt {attempt + 1})", file=sys.stderr ) await asyncio.sleep(delay) continue print( - f"Jina Scrape: HTTP error retry attempts exhausted, url: {url}" + f"Jina Scrape: HTTP error retry attempts exhausted, url: {url}", file=sys.stderr ) raise e @@ -269,18 +272,18 @@ async def scrape_url_with_jina( if attempt < len(retry_delays): print( f"Jina Scrape: Unknown request exception: {e}, url: {url}, " - f"{delay}s before next attempt (attempt {attempt + 1})" + f"{delay}s before next attempt (attempt {attempt + 1})", file=sys.stderr ) await asyncio.sleep(delay) continue print( - f"Jina Scrape: Unknown request exception retry attempts exhausted, url: {url}" + f"Jina Scrape: Unknown request exception retry attempts exhausted, url: {url}", file=sys.stderr ) raise e except Exception as e: error_msg = f"Jina Scrape: Unexpected error occurred: {str(e)}" - print(error_msg) + print(error_msg, file=sys.stderr) return { "success": False, "filename": "", @@ -518,7 +521,7 @@ async def extract_info_with_llm( tail_50 = response.text[-50:] repeat_count = response.text.count(tail_50) if repeat_count > 5: - print("Repeat detected in extract_info_with_llm") + print("Repeat detected in extract_info_with_llm", file=sys.stderr) continue # Check if the request was successful @@ -544,12 +547,12 @@ async def extract_info_with_llm( delay = connect_retry_delays[attempt] print( f"Jina Scrape and Extract Info: Connection timeout, {delay}s before next attempt " - f"(attempt {attempt + 1})" + f"(attempt {attempt + 1})", file=sys.stderr ) await asyncio.sleep(delay) continue print( - "Jina Scrape and Extract Info: Connection retry attempts exhausted" + "Jina Scrape and Extract Info: Connection retry attempts exhausted", file=sys.stderr ) raise e @@ -559,12 +562,12 @@ async def extract_info_with_llm( delay = connect_retry_delays[attempt] print( f"Jina Scrape and Extract Info: Connection error: {e}, " - f"{delay}s before next attempt" + f"{delay}s before next attempt", file=sys.stderr ) await asyncio.sleep(delay) continue print( - "Jina Scrape and Extract Info: Connection retry attempts exhausted" + "Jina Scrape and Extract Info: Connection retry attempts exhausted", file=sys.stderr ) raise e @@ -572,19 +575,20 @@ async def extract_info_with_llm( # read timeout, LLM API is too slow, no need to retry if attempt < len(connect_retry_delays): print( - f"Jina Scrape and Extract Info: LLM API attempt {attempt} read timeout" + f"Jina Scrape and Extract Info: LLM API attempt {attempt} read timeout", file=sys.stderr ) continue print( f"Jina Scrape and Extract Info: LLM API read timeout retry attempts exhausted, " f"please check the request complexity, information to extract: {info_to_extract}, " - f"length of content: {len(content)}, url: {url}" + f"length of content: {len(content)}, url: {url}", file=sys.stderr ) raise e except httpx.HTTPStatusError as e: print( - f"Jina Scrape and Extract Info: HTTP error for LLM API: {e}, response.text: {response.text}" + f"Jina Scrape and Extract Info: HTTP error for LLM API: {e}, response.text: {response.text}", + file=sys.stderr ) raise httpx.HTTPStatusError( f"response.text: {response.text}", @@ -594,13 +598,13 @@ async def extract_info_with_llm( except httpx.RequestError as e: print( - f"Jina Scrape and Extract Info: Unknown request exception: {e}" + f"Jina Scrape and Extract Info: Unknown request exception: {e}", file=sys.stderr ) raise e except Exception as e: error_msg = f"Jina Scrape and Extract Info: Unexpected error during LLM API call: {str(e)}" - print(error_msg) + print(error_msg, file=sys.stderr) return { "success": False, "extracted_info": "", @@ -617,8 +621,8 @@ async def extract_info_with_llm( error_msg = ( f"Jina Scrape and Extract Info: Failed to parse LLM API response: {str(e)}" ) - print(error_msg) - print(f"Raw response: {response.text}") + print(error_msg, file=sys.stderr) + print(f"Raw response: {response.text}", file=sys.stderr) return { "success": False, "extracted_info": "", @@ -629,7 +633,7 @@ async def extract_info_with_llm( print( f"Jina Scrape and Extract Info: Info to extract: {info_to_extract}, " - f"LLM Response data: {response_data}" + f"LLM Response data: {response_data}", file=sys.stderr ) # Extract summary from response @@ -638,7 +642,7 @@ async def extract_info_with_llm( summary = response_data["choices"][0]["message"]["content"] except Exception as e: error_msg = f"Jina Scrape and Extract Info: Failed to get summary from LLM API response: {str(e)}" - print(error_msg) + print(error_msg, file=sys.stderr) return { "success": False, "extracted_info": "", @@ -664,7 +668,7 @@ async def extract_info_with_llm( summary = response_data["candidates"][0]["content"]["parts"][0]["text"] except Exception as e: error_msg = f"Jina Scrape and Extract Info: Failed to get summary from LLM API response: {str(e)}" - print(error_msg) + print(error_msg, file=sys.stderr) return { "success": False, "extracted_info": "", @@ -684,7 +688,7 @@ async def extract_info_with_llm( error_msg = ( f"Jina Scrape and Extract Info: LLM API error: {response_data['error']}" ) - print(error_msg) + print(error_msg, file=sys.stderr) return { "success": False, "extracted_info": "", @@ -696,7 +700,7 @@ async def extract_info_with_llm( f"Jina Scrape and Extract Info: No valid response from LLM API, " f"response data: {response_data}" ) - print(error_msg) + print(error_msg, file=sys.stderr) return { "success": False, "extracted_info": "", @@ -723,7 +727,7 @@ def main(transport: str, port: str): :param port: Port for SSE. :param transport: The transport type, e.g., `stdio` or `sse`. """ - print(f"Starting the MCP server on port {port} with transport {transport}") + print(f"Starting the MCP server on port {port} with transport {transport}", file=sys.stderr) assert transport.lower() in ["stdio", "sse"], "Transport should be stdio or sse" logger = get_logger("Service:jina_scrape_llm_summary") logger.info("Starting the MCP server") diff --git a/mcpuniverse/mcp/servers/serper_search/server.py b/mcpuniverse/mcp/servers/serper_search/server.py index 686551d5..79292b51 100644 --- a/mcpuniverse/mcp/servers/serper_search/server.py +++ b/mcpuniverse/mcp/servers/serper_search/server.py @@ -5,6 +5,7 @@ """ import os +import sys from typing import Any, Dict import click @@ -170,7 +171,7 @@ async def perform_search(search_query: str) -> tuple[list, dict]: if query_without_quotes: # Make sure we still have a valid query print( f"No results found for query with quotes: '{original_query}'. " - f"Retrying with query without quotes: '{query_without_quotes}'" + f"Retrying with query without quotes: '{query_without_quotes}'", file=sys.stderr ) organic_results, search_params = await perform_search( query_without_quotes @@ -210,7 +211,7 @@ def main(transport: str, port: str): :param port: Port for SSE. :param transport: The transport type, e.g., `stdio` or `sse`. """ - print(f"Starting the MCP server on port {port} with transport {transport}") + print(f"Starting the MCP server on port {port} with transport {transport}", file=sys.stderr) assert transport.lower() in ["stdio", "sse"], \ "Transport should be `stdio` or `sse`" logger = get_logger("Service:serper_search") diff --git a/mcpuniverse/mcp/servers/yahoo_finance/server.py b/mcpuniverse/mcp/servers/yahoo_finance/server.py index de300acc..5764a65f 100644 --- a/mcpuniverse/mcp/servers/yahoo_finance/server.py +++ b/mcpuniverse/mcp/servers/yahoo_finance/server.py @@ -4,6 +4,7 @@ """ # pylint: disable=broad-exception-caught,too-many-statements,line-too-long,too-many-return-statements,invalid-name import json +import sys from enum import Enum import click @@ -105,10 +106,10 @@ async def get_historical_stock_prices( company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting historical stock prices for {ticker}: {e}") + print(f"Error: getting historical stock prices for {ticker}: {e}", file=sys.stderr) return f"Error: getting historical stock prices for {ticker}: {e}" # If the company is found, get the historical data @@ -132,10 +133,10 @@ async def get_stock_info(ticker: str) -> str: company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting stock information for {ticker}: {e}") + print(f"Error: getting stock information for {ticker}: {e}", file=sys.stderr) return f"Error: getting stock information for {ticker}: {e}" info = company.info return json.dumps(info) @@ -159,17 +160,17 @@ async def get_yahoo_finance_news(ticker: str) -> str: company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting news for {ticker}: {e}") + print(f"Error: getting news for {ticker}: {e}", file=sys.stderr) return f"Error: getting news for {ticker}: {e}" # If the company is found, get the news try: news = company.news except Exception as e: - print(f"Error: getting news for {ticker}: {e}") + print(f"Error: getting news for {ticker}: {e}", file=sys.stderr) return f"Error: getting news for {ticker}: {e}" news_list = [] @@ -183,7 +184,7 @@ async def get_yahoo_finance_news(ticker: str) -> str: f"Title: {title}\nSummary: {summary}\nDescription: {description}\nURL: {url}" ) if not news_list: - print(f"No news found for company that searched with {ticker} ticker.") + print(f"No news found for company that searched with {ticker} ticker.", file=sys.stderr) return f"No news found for company that searched with {ticker} ticker." return "\n\n".join(news_list) @@ -201,7 +202,7 @@ async def get_stock_actions(ticker: str) -> str: try: company = yf.Ticker(ticker) except Exception as e: - print(f"Error: getting stock actions for {ticker}: {e}") + print(f"Error: getting stock actions for {ticker}: {e}", file=sys.stderr) return f"Error: getting stock actions for {ticker}: {e}" actions_df = company.actions actions_df = actions_df.reset_index(names="Date") @@ -224,10 +225,10 @@ async def get_financial_statement(ticker: str, financial_type: str) -> str: company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting financial statement for {ticker}: {e}") + print(f"Error: getting financial statement for {ticker}: {e}", file=sys.stderr) return f"Error: getting financial statement for {ticker}: {e}" if financial_type == FinancialType.income_stmt: @@ -284,10 +285,10 @@ async def get_holder_info(ticker: str, holder_type: str) -> str: company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting holder info for {ticker}: {e}") + print(f"Error: getting holder info for {ticker}: {e}", file=sys.stderr) return f"Error: getting holder info for {ticker}: {e}" if holder_type == HolderType.major_holders: @@ -319,10 +320,10 @@ async def get_option_expiration_dates(ticker: str) -> str: company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting option expiration dates for {ticker}: {e}") + print(f"Error: getting option expiration dates for {ticker}: {e}", file=sys.stderr) return f"Error: getting option expiration dates for {ticker}: {e}" return json.dumps(company.options) @@ -354,10 +355,10 @@ async def get_option_chain(ticker: str, expiration_date: str, option_type: str) company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting option chain for {ticker}: {e}") + print(f"Error: getting option chain for {ticker}: {e}", file=sys.stderr) return f"Error: getting option chain for {ticker}: {e}" # Check if the expiration date is valid @@ -394,10 +395,10 @@ async def get_recommendations(ticker: str, recommendation_type: str, months_back company = yf.Ticker(ticker) try: if company.isin is None: - print(f"Company ticker {ticker} not found.") + print(f"Company ticker {ticker} not found.", file=sys.stderr) return f"Company ticker {ticker} not found." except Exception as e: - print(f"Error: getting recommendations for {ticker}: {e}") + print(f"Error: getting recommendations for {ticker}: {e}", file=sys.stderr) return f"Error: getting recommendations for {ticker}: {e}" try: if recommendation_type == RecommendationType.recommendations: @@ -414,7 +415,7 @@ async def get_recommendations(ticker: str, recommendation_type: str, months_back latest_by_firm = upgrades_downgrades.drop_duplicates(subset=["Firm"]) return latest_by_firm.to_json(orient="records", date_format="iso") except Exception as e: - print(f"Error: getting recommendations for {ticker}: {e}") + print(f"Error: getting recommendations for {ticker}: {e}", file=sys.stderr) return f"Error: getting recommendations for {ticker}: {e}" return yfinance_server