"""Entry point: runs both Telegram bot and MCP server in one process.""" import asyncio import logging import signal import uvicorn from .config import MCP_HOST, MCP_PORT, MEDIA_DIR, DB_PATH from .db import Database from .telegram_bot import BridgeBot from .mcp_server import mcp, init as init_mcp, custom_routes logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) logger = logging.getLogger("mcp_bridge") async def run_telegram_bot(bot: BridgeBot): """Run the telegram bot polling loop.""" app = bot.build_application() await app.initialize() await bot._post_init(app) await app.start() updater = app.updater await updater.start_polling(drop_pending_updates=True) logger.info("Telegram bot polling started") try: while True: await asyncio.sleep(3600) except asyncio.CancelledError: logger.info("Telegram bot shutting down...") await updater.stop() await app.stop() await app.shutdown() async def run_mcp_server(): """Run the FastMCP HTTP server with built-in OAuth.""" # Get the FastMCP app (includes OAuth routes automatically) mcp_app = mcp.http_app() # Add our custom non-auth routes mcp_app.routes.extend(custom_routes) logger.info(f"MCP server starting on {MCP_HOST}:{MCP_PORT} (OAuth via FastMCP)") config = uvicorn.Config(mcp_app, host=MCP_HOST, port=MCP_PORT, log_level="info") server = uvicorn.Server(config) await server.serve() async def main(): """Start both services.""" MEDIA_DIR.mkdir(parents=True, exist_ok=True) DB_PATH.parent.mkdir(parents=True, exist_ok=True) db = Database() logger.info(f"Database initialized at {DB_PATH}") init_mcp(db) bot = BridgeBot(db) telegram_task = asyncio.create_task(run_telegram_bot(bot)) mcp_task = asyncio.create_task(run_mcp_server()) loop = asyncio.get_event_loop() def handle_signal(): logger.info("Received shutdown signal") telegram_task.cancel() mcp_task.cancel() for sig in (signal.SIGINT, signal.SIGTERM): loop.add_signal_handler(sig, handle_signal) try: await asyncio.gather(telegram_task, mcp_task, return_exceptions=True) except asyncio.CancelledError: pass logger.info("MCP Bridge stopped") if __name__ == "__main__": asyncio.run(main())