diff --git a/src/mcp_synology_container/__main__.py b/src/mcp_synology_container/__main__.py new file mode 100644 index 0000000..4fb694f --- /dev/null +++ b/src/mcp_synology_container/__main__.py @@ -0,0 +1,6 @@ +"""Allow running as: python -m mcp_synology_container.""" + +from mcp_synology_container.cli import main + +if __name__ == "__main__": + main() diff --git a/src/mcp_synology_container/cli.py b/src/mcp_synology_container/cli.py index 278c71c..3db6b33 100644 --- a/src/mcp_synology_container/cli.py +++ b/src/mcp_synology_container/cli.py @@ -284,34 +284,56 @@ async def _run_check(config_path: str | None) -> bool: @click.option("--config", "-c", "config_path", type=click.Path(), help="Config file path") def serve(config_path: str | None) -> None: """Start the MCP server in stdio mode.""" + sys.stderr.write("SERVER STARTING\n") + sys.stderr.flush() _configure_logging("warning") - asyncio.run(_run_serve(config_path)) + # Use anyio.run() — FastMCP uses anyio internally (anyio.create_task_group), + # and anyio.run() ensures the correct backend context is set up. + # asyncio.run() can cause issues on Windows (ProactorEventLoop + anyio). + import anyio + anyio.run(_run_serve, config_path) async def _run_serve(config_path: str | None) -> None: """Initialize and run the MCP server.""" + # Eagerly import all modules so any ImportError surfaces immediately on stderr + # instead of silently killing the process. from mcp_synology_container.auth import AuthManager from mcp_synology_container.config import load_config - from mcp_synology_container.dsm_client import DsmClient + from mcp_synology_container.dsm_client import DsmClient, SynologyError from mcp_synology_container.server import create_server + logger.debug("Loading config from: %s", config_path or "default path") try: config = load_config(config_path) except (FileNotFoundError, ValueError) as e: - click.echo(click.style(f"Config error: {e}", fg="red"), err=True) - sys.exit(1) + sys.stderr.write(f"Config error: {e}\n") + sys.stderr.flush() + return - async with DsmClient(config.base_url, config.connection.verify_ssl) as client: - await client.query_api_info() - auth = AuthManager(config) - client.set_auth_manager(auth) - # Login on startup - try: - client.sid = await auth.login(client) - except Exception as e: - logger.error("Initial login failed: %s", e) - sys.exit(1) + logger.debug("Connecting to %s", config.base_url) + try: + async with DsmClient(config.base_url, config.connection.verify_ssl) as client: + await client.query_api_info() + auth = AuthManager(config) + client.set_auth_manager(auth) - mcp_server = create_server(config, client) - logger.info("MCP server starting (stdio mode)") - await mcp_server.run_stdio_async() + try: + client.sid = await auth.login(client) + except Exception as e: + sys.stderr.write(f"Login failed: {e}\n") + sys.stderr.flush() + return + + logger.debug("Login OK, creating MCP server") + mcp_server = create_server(config, client) + sys.stderr.write("MCP server ready\n") + sys.stderr.flush() + await mcp_server.run_stdio_async() + except SynologyError as e: + sys.stderr.write(f"DSM error during startup: {e}\n") + sys.stderr.flush() + except Exception as e: + sys.stderr.write(f"Fatal error: {e}\n") + sys.stderr.flush() + raise