|
| 1 | +""" |
| 2 | +CLI Interface Module |
| 3 | +
|
| 4 | +Defines the command-line interface for the perplexity-advanced-mcp package, |
| 5 | +providing API key configuration and server management functionality. |
| 6 | +""" |
| 7 | + |
| 8 | +import logging |
| 9 | +import signal |
| 10 | +import sys |
| 11 | +from typing import NoReturn |
| 12 | + |
| 13 | +import typer |
| 14 | + |
| 15 | +from perplexity_advanced_mcp.types import ProviderType |
| 16 | + |
| 17 | +from .config import PROVIDER_CONFIG, get_api_keys |
| 18 | +from .search_tool import mcp |
| 19 | + |
| 20 | +logger = logging.getLogger(__name__) |
| 21 | + |
| 22 | +app = typer.Typer() |
| 23 | + |
| 24 | +# Global flag for graceful shutdown |
| 25 | +shutdown_requested = False |
| 26 | + |
| 27 | + |
| 28 | +def handle_shutdown(signum: int, frame: object) -> None: |
| 29 | + """ |
| 30 | + Signal handler for graceful shutdown. |
| 31 | +
|
| 32 | + Args: |
| 33 | + signum: Signal number received |
| 34 | + frame: Current stack frame (unused) |
| 35 | + """ |
| 36 | + global shutdown_requested |
| 37 | + signal_name = signal.Signals(signum).name |
| 38 | + logger.warning("Received %s signal. Initiating graceful shutdown...", signal_name) |
| 39 | + shutdown_requested = True |
| 40 | + |
| 41 | + |
| 42 | +def cleanup() -> None: |
| 43 | + """Perform cleanup operations before shutdown.""" |
| 44 | + logger.info("Cleaning up resources...") |
| 45 | + # Add any necessary cleanup operations here |
| 46 | + # For example: |
| 47 | + # - Close API connections |
| 48 | + # - Save state if needed |
| 49 | + # - Release any acquired resources |
| 50 | + logger.info("Cleanup completed. Exiting...") |
| 51 | + |
| 52 | + |
| 53 | +def setup_signal_handlers() -> None: |
| 54 | + """Configure signal handlers for graceful shutdown.""" |
| 55 | + signal.signal(signal.SIGTERM, handle_shutdown) |
| 56 | + signal.signal(signal.SIGINT, handle_shutdown) |
| 57 | + |
| 58 | + |
| 59 | +def exit_gracefully() -> NoReturn: |
| 60 | + """Perform cleanup and exit.""" |
| 61 | + cleanup() |
| 62 | + sys.exit(0) |
| 63 | + |
| 64 | + |
| 65 | +@app.command() |
| 66 | +def main( |
| 67 | + ctx: typer.Context, |
| 68 | + openrouter_key: str | None = typer.Option( |
| 69 | + None, |
| 70 | + "--openrouter-api-key", |
| 71 | + "-o", |
| 72 | + help="OpenRouter API key", |
| 73 | + envvar="OPENROUTER_API_KEY", |
| 74 | + ), |
| 75 | + perplexity_key: str | None = typer.Option( |
| 76 | + None, |
| 77 | + "--perplexity-api-key", |
| 78 | + "-p", |
| 79 | + help="Perplexity API key", |
| 80 | + envvar="PERPLEXITY_API_KEY", |
| 81 | + ), |
| 82 | +) -> None: |
| 83 | + logger.info("Starting MCP server...") |
| 84 | + openrouter_key_val, perplexity_key_val = get_api_keys(openrouter_key, perplexity_key) |
| 85 | + PROVIDER_CONFIG["openrouter"]["key"] = openrouter_key_val |
| 86 | + PROVIDER_CONFIG["perplexity"]["key"] = perplexity_key_val |
| 87 | + |
| 88 | + provider: ProviderType |
| 89 | + if openrouter_key_val: |
| 90 | + provider = "openrouter" |
| 91 | + elif perplexity_key_val: |
| 92 | + provider = "perplexity" |
| 93 | + else: |
| 94 | + raise typer.Abort() |
| 95 | + |
| 96 | + logger.info("Using %s as the provider", provider) |
| 97 | + |
| 98 | + # Set up signal handlers for graceful shutdown |
| 99 | + setup_signal_handlers() |
| 100 | + |
| 101 | + while not shutdown_requested: |
| 102 | + try: |
| 103 | + mcp.run() |
| 104 | + except Exception as e: |
| 105 | + if shutdown_requested: |
| 106 | + logger.info("Shutdown requested during error recovery") |
| 107 | + break |
| 108 | + logger.error("MCP server error occurred: %s", str(e)) |
| 109 | + logger.info("Restarting MCP server...") |
| 110 | + |
| 111 | + exit_gracefully() |
| 112 | + |
| 113 | + |
| 114 | +if __name__ == "__main__": |
| 115 | + app() |
0 commit comments