Skip to main content
Glama
cli.py17.8 kB
#!/usr/bin/env python3 """CLI for telegram-mcp-server setup and configuration""" import os import sys import json import asyncio from pathlib import Path def get_claude_config_path(scope="user"): """ Get Claude Code MCP config path based on scope Args: scope: "user" (default), "project", or "local" Returns: Path to config file """ if scope == "user": return Path.home() / ".claude.json" elif scope == "project": return Path.cwd() / ".mcp.json" elif scope == "local": return Path.cwd() / ".claude.json" else: raise ValueError(f"Invalid scope: {scope}") def get_claude_settings_path(): """Get Claude Code settings.json path (for environment variables)""" return Path.home() / ".claude" / "settings.json" def update_claude_settings(env_vars): """ Update Claude Code settings.json with environment variables Intelligently merges with existing env configuration Args: env_vars: Dictionary of environment variables to add/update """ settings_path = get_claude_settings_path() settings_path.parent.mkdir(parents=True, exist_ok=True) # Load existing settings or create new if settings_path.exists(): with open(settings_path, 'r') as f: settings = json.load(f) else: settings = {} # Ensure env key exists if "env" not in settings: settings["env"] = {} # Merge new environment variables settings["env"].update(env_vars) # Save settings with open(settings_path, 'w') as f: json.dump(settings, f, indent=2) return settings_path def get_codex_config_path(): """Get Codex config path""" return Path.home() / ".codex" / "config.toml" def get_gemini_config_path(scope="user"): """ Get Gemini CLI config path based on scope Args: scope: "user" (default) or "project" Returns: Path to config file """ if scope == "user": return Path.home() / ".gemini" / "settings.json" elif scope == "project": return Path.cwd() / ".gemini" / "settings.json" else: raise ValueError(f"Invalid scope: {scope}") def check_tool_installed(tool_name): """ Check if a CLI tool is installed Args: tool_name: Name of the tool (claude, codex, gemini) Returns: bool: True if installed, False otherwise """ import shutil return shutil.which(tool_name) is not None async def interactive_setup(): """Interactive setup wizard""" print("🤖 Telegram MCP Server - Setup Wizard") print("=" * 50) print() # Step 0: Detect installed tools and choose configuration print("Step 0: Detect and Choose Configuration") print("-" * 50) # Detect installed tools installed_tools = [] if check_tool_installed("claude"): installed_tools.append("Claude Code") if check_tool_installed("codex"): installed_tools.append("Codex") if check_tool_installed("gemini"): installed_tools.append("Gemini CLI") if installed_tools: print(f"✅ Detected installed tools: {', '.join(installed_tools)}") print() print("Which AI coding assistant do you want to configure?") print(" 1. Claude Code (Anthropic)") print(" 2. Codex (OpenAI)") print(" 3. Gemini CLI (Google)") print(" 4. Multiple tools") print() client_choice = input("Enter choice [1]: ").strip() or "1" # Determine which tools to configure configure_claude = client_choice in ["1", "4"] configure_codex = client_choice in ["2", "4"] configure_gemini = client_choice in ["3", "4"] # Get scope for Claude Code claude_scope = None if configure_claude: print() print("Choose configuration scope for Claude Code:") print(" 1. User scope (global, ~/.claude.json)") print(" 2. Project scope (shared, .mcp.json in project root)") print(" 3. Local scope (project-specific, .claude.json)") print() print("💡 Recommendation:") print(" - User scope: Personal use across all projects") print(" - Project scope: Team collaboration (checked into git)") print(" - Local scope: Project-specific, not shared") print() scope_choice = input("Enter choice [1]: ").strip() or "1" scope_map = {"1": "user", "2": "project", "3": "local"} claude_scope = scope_map.get(scope_choice, "user") # Get scope for Gemini CLI gemini_scope = None if configure_gemini: print() print("Choose configuration scope for Gemini CLI:") print(" 1. User scope (global, ~/.gemini/settings.json)") print(" 2. Project scope (.gemini/settings.json in project root)") print() scope_choice = input("Enter choice [1]: ").strip() or "1" scope_map = {"1": "user", "2": "project"} gemini_scope = scope_map.get(scope_choice, "user") if configure_codex: print() print("ℹ️ Note: Codex only supports global configuration (~/.codex/config.toml)") print(" All projects will share the same MCP configuration.") print() # Step 1: Bot Token print("Step 1: Telegram Bot Token") print("-" * 50) print("1. Open Telegram and search for @BotFather") print("2. Send: /newbot") print("3. Follow instructions to create your bot") print("4. Copy the Bot Token (format: 123456789:ABCdef...)") print() bot_token = input("Enter your Bot Token: ").strip() if not bot_token: print("❌ Bot Token is required") sys.exit(1) # Validate token format if ":" not in bot_token: print("⚠️ Warning: Token format looks incorrect (should contain ':')") # Step 2: Verify bot print() print("Step 2: Verifying bot...") print("-" * 50) try: from telegram import Bot bot = Bot(token=bot_token) bot_info = await bot.get_me() print(f"✅ Bot verified: @{bot_info.username}") except Exception as e: print(f"❌ Failed to verify bot: {e}") print("Please check your Bot Token and try again") sys.exit(1) # Step 3: Chat ID print() print("Step 3: Get Chat ID") print("-" * 50) print("1. Open Telegram and search for your bot") print("2. Click START or send any message") print("3. Press Enter here to auto-detect your Chat ID") print() input("Press Enter after sending a message to your bot...") try: updates = await bot.get_updates() if updates: chat_id = str(updates[-1].message.chat.id) print(f"✅ Chat ID detected: {chat_id}") else: print("⚠️ No messages found. Please enter manually:") print(" Visit: https://api.telegram.org/bot{}/getUpdates".format(bot_token)) print(" Find: \"chat\":{\"id\":123456789}") chat_id = input("Enter your Chat ID: ").strip() except Exception as e: print(f"⚠️ Auto-detection failed: {e}") print("Please enter manually:") chat_id = input("Enter your Chat ID: ").strip() if not chat_id: print("❌ Chat ID is required") sys.exit(1) # Step 4: Generate config print() print("Step 4: Generating configuration") print("-" * 50) # Detect installation method if os.path.exists(Path.home() / ".local" / "bin" / "uvx"): command = "uvx" args = ["telegram-mcp-server"] else: command = sys.executable args = ["-m", "telegram_mcp_server"] # Configure Claude Code if configure_claude: config_path = get_claude_config_path(claude_scope) config_path.parent.mkdir(parents=True, exist_ok=True) # Load existing config or create new if config_path.exists(): with open(config_path, 'r') as f: config = json.load(f) else: config = {} # Ensure mcpServers key exists if "mcpServers" not in config: config["mcpServers"] = {} # Add telegram server config config["mcpServers"]["telegram"] = { "command": command, "args": args, "env": { "TELEGRAM_BOT_TOKEN": bot_token, "TELEGRAM_CHAT_ID": chat_id } } # Save config with open(config_path, 'w') as f: json.dump(config, f, indent=2) print(f"✅ Claude Code MCP configuration saved to: {config_path}") # Update settings.json with MCP_TOOL_TIMEOUT for 7-day unattended mode settings_path = update_claude_settings({ "MCP_TOOL_TIMEOUT": "604800000" # 7 days in milliseconds }) print(f"✅ Claude Code settings updated: {settings_path}") print(" - MCP_TOOL_TIMEOUT set to 7 days (604800000 ms)") if claude_scope == "project": print("💡 Remember to commit .mcp.json to version control for team sharing") # Configure Gemini CLI if configure_gemini: gemini_config_path = get_gemini_config_path(gemini_scope) # Only create if tool is installed or user confirms if not check_tool_installed("gemini"): print() print("⚠️ Gemini CLI not detected on your system") create_anyway = input("Create configuration anyway? (y/N): ").strip().lower() if create_anyway != 'y': print("⏭️ Skipping Gemini CLI configuration") configure_gemini = False if configure_gemini: gemini_config_path.parent.mkdir(parents=True, exist_ok=True) # Load existing config or create new if gemini_config_path.exists(): with open(gemini_config_path, 'r') as f: gemini_config = json.load(f) else: gemini_config = {} # Ensure mcpServers key exists if "mcpServers" not in gemini_config: gemini_config["mcpServers"] = {} # Add telegram server config gemini_config["mcpServers"]["telegram"] = { "command": command, "args": args, "env": { "TELEGRAM_BOT_TOKEN": bot_token, "TELEGRAM_CHAT_ID": chat_id }, "timeout": 604800000 # 7 days in milliseconds } # Save config with open(gemini_config_path, 'w') as f: json.dump(gemini_config, f, indent=2) print(f"✅ Gemini CLI configuration saved to: {gemini_config_path}") print(" - Timeout set to 7 days (604800000 ms)") if gemini_scope == "project": print("💡 Remember to commit .gemini/settings.json to version control for team sharing") # Configure Codex if configure_codex: try: import toml except ImportError: print("⚠️ Installing toml package for Codex configuration...") import subprocess subprocess.check_call([sys.executable, "-m", "pip", "install", "toml"]) import toml codex_config_path = get_codex_config_path() codex_config_path.parent.mkdir(parents=True, exist_ok=True) # Load existing config or create new if codex_config_path.exists(): with open(codex_config_path, 'r') as f: codex_config = toml.load(f) else: codex_config = {} # Ensure mcp_servers section exists if "mcp_servers" not in codex_config: codex_config["mcp_servers"] = {} # Add telegram server config codex_config["mcp_servers"]["telegram"] = { "command": command, "args": args, "tool_timeout_sec": 604800, # 7 days for unattended mode "env": { "TELEGRAM_BOT_TOKEN": bot_token, "TELEGRAM_CHAT_ID": chat_id } } # Save config with open(codex_config_path, 'w') as f: toml.dump(codex_config, f) print(f"✅ Codex configuration saved to: {codex_config_path}") # Step 5: Test connection print() print("Step 5: Testing connection") print("-" * 50) try: await bot.send_message( chat_id=chat_id, text="✅ Telegram MCP Server configured successfully!\n\n" "You can now start using Claude Code with Telegram integration." ) print("✅ Test message sent to Telegram") except Exception as e: print(f"⚠️ Failed to send test message: {e}") # Done print() print("=" * 50) print("🎉 Setup complete!") print() print("Next steps:") print(" 1. Start your AI assistant:") if configure_claude: print(" - Claude Code: claude --permission-mode bypassPermissions") if configure_codex: print(" - Codex: codex --dangerously-bypass-approvals-and-sandbox") if configure_gemini: print(" - Gemini CLI: gemini") print() print(" 2. Check MCP connection: /mcp") print(" 3. Test: Use telegram_notify to send a message") print() print(" In Telegram:") print(" - Send: /help") print(" - Try: 'Enter unattended mode. Task: analyze project'") print() # Show mcp add commands for other tools print() print("=" * 50) print("📋 Or add to other tools using mcp add commands:") print() if not configure_claude: print("Claude Code:") print(f" claude mcp add \\") print(f" --transport stdio \\") print(f" telegram \\") print(f" --env TELEGRAM_BOT_TOKEN={bot_token} \\") print(f" --env TELEGRAM_CHAT_ID={chat_id} \\") print(f" -- \\") print(f" uvx telegram-mcp-server") print() if not configure_codex: print("Codex:") print(f" codex mcp add telegram \\") print(f" --env TELEGRAM_BOT_TOKEN={bot_token} \\") print(f" --env TELEGRAM_CHAT_ID={chat_id} \\") print(f" -- \\") print(f" npx -y telegram-mcp-server") print() if not configure_gemini: print("Gemini CLI:") print(f" gemini mcp add telegram uvx telegram-mcp-server \\") print(f" -e TELEGRAM_BOT_TOKEN={bot_token} \\") print(f" -e TELEGRAM_CHAT_ID={chat_id}") print() print("Documentation: https://github.com/batianVolyc/telegram-mcp-server") def show_config(): """Show current configuration""" config_path = get_claude_config_path() if not config_path.exists(): print("❌ No configuration found") print(f"Expected location: {config_path}") print() print("Run: telegram-mcp-server --setup") return with open(config_path, 'r') as f: config = json.load(f) if "telegram" not in config.get("mcpServers", {}): print("❌ Telegram MCP Server not configured") print() print("Run: telegram-mcp-server --setup") return telegram_config = config["mcpServers"]["telegram"] print("📋 Current Configuration") print("=" * 50) print(f"Config file: {config_path}") print() print(f"Command: {telegram_config.get('command')}") print(f"Args: {telegram_config.get('args')}") print() env = telegram_config.get("env", {}) bot_token = env.get("TELEGRAM_BOT_TOKEN", "") chat_id = env.get("TELEGRAM_CHAT_ID", "") if bot_token: print(f"Bot Token: {bot_token[:10]}...{bot_token[-5:]}") else: print("Bot Token: ❌ Not set") if chat_id: print(f"Chat ID: {chat_id}") else: print("Chat ID: ❌ Not set") print() print("=" * 50) def main(): """Main CLI entry point""" if len(sys.argv) > 1: arg = sys.argv[1] if arg in ["--version", "-v", "version"]: from . import __version__ print(f"telegram-mcp-server version {__version__}") print("https://github.com/batianVolyc/telegram-mcp-server") elif arg in ["--setup", "-s", "setup"]: asyncio.run(interactive_setup()) elif arg in ["--config", "-c", "config"]: show_config() elif arg in ["--help", "-h", "help"]: print("Telegram MCP Server - CLI") print() print("Usage:") print(" telegram-mcp-server Run MCP server") print(" telegram-mcp-server --version Show version") print(" telegram-mcp-server --setup Interactive setup wizard") print(" telegram-mcp-server --config Show current configuration") print(" telegram-mcp-server --help Show this help") print() print("Documentation: https://github.com/batianVolyc/telegram-mcp-server") else: print(f"Unknown option: {arg}") print("Run: telegram-mcp-server --help") sys.exit(1) else: # Run MCP server from . import __main__ asyncio.run(__main__.main()) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/batianVolyc/telegram-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server