#!/usr/bin/env python3
"""Interactive MFA setup script for Vivint accounts with 2FA enabled."""
import asyncio
import sys
import os
import logging
import getpass
from datetime import datetime
# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from config import config
from vivint_client import VivintMCPClient
from token_manager import token_manager
from vivintpy.account import Account
from vivintpy.exceptions import VivintSkyApiMfaRequiredError, VivintSkyApiAuthenticationError
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
logger = logging.getLogger(__name__)
async def interactive_mfa_setup():
"""Interactive MFA setup with real-time code input."""
print("π Vivint MFA Setup Assistant")
print("=" * 50)
print()
# Check credentials
if not config.username or not config.password:
print("β VIVINT_USERNAME and VIVINT_PASSWORD must be set in .env file")
return False
print(f"π§ Account: {config.username}")
print(f"πΎ Token file: {config.refresh_token_file}")
print()
# Check for existing tokens
existing_tokens = await token_manager.load_tokens()
if existing_tokens and token_manager.is_token_for_user(config.username):
print("β
Found existing valid tokens!")
print("π‘ You can start the server directly: python src/server.py")
choice = input("π Do you want to refresh tokens anyway? (y/N): ").lower()
if choice != 'y':
return True
print("π Attempting to connect to Vivint...")
# Create Account object directly for better control
account = Account(username=config.username, password=config.password)
try:
# Try initial connection - this will trigger MFA if needed
print("π Authenticating with username/password...")
await account.connect()
# If we get here without MFA, great!
print("β
Authentication successful (no MFA required)!")
except VivintSkyApiMfaRequiredError:
print()
print("π Two-Factor Authentication Required")
print("π± Please check your Vivint app or email for a 6-digit code")
print()
# Get fresh MFA code interactively
while True:
try:
mfa_code = input("π’ Enter your 6-digit MFA code: ").strip()
if len(mfa_code) != 6 or not mfa_code.isdigit():
print("β Please enter exactly 6 digits")
continue
print(f"π Verifying code: {mfa_code}")
# Verify MFA code
await account.verify_mfa(mfa_code)
print("β
MFA verification successful!")
break
except Exception as e:
error_msg = str(e).lower()
if "incorrect" in error_msg or "invalid" in error_msg:
print("β Invalid MFA code. Please try again.")
print("π‘ Make sure you're using the most recent code from your app/email")
continue
else:
print(f"β MFA verification failed: {e}")
return False
except VivintSkyApiAuthenticationError as e:
print(f"β Authentication failed: {e}")
print("π‘ Check your username and password in .env file")
return False
except Exception as e:
print(f"β Connection failed: {e}")
return False
# Save tokens if we have them
if hasattr(account.api, 'tokens') and account.api.tokens:
print("πΎ Saving refresh tokens...")
success = await token_manager.save_tokens(account.api.tokens)
if success:
print("β
Tokens saved successfully!")
print(f"π Location: {config.refresh_token_file}")
print()
print("π Setup Complete!")
print("π You can now start the server: python src/server.py")
print("π‘ Future startups won't require MFA (tokens will be reused)")
else:
print("β οΈ Failed to save tokens - you'll need MFA each time")
else:
print("β οΈ No tokens received from Vivint API")
# Test basic functionality
print()
print("π§ͺ Testing connection...")
try:
# Load systems to verify connection works
await account.refresh()
if account.systems:
system = account.systems[0]
print(f"β
Connected to system: {getattr(system, 'name', 'Unknown')} (ID: {system.id})")
print(f"π‘οΈ Armed state: {getattr(system, 'arm_state', 'unknown')}")
else:
print("β οΈ No systems found in account")
except Exception as e:
print(f"β οΈ Test failed: {e}")
# Cleanup
await account.disconnect()
return True
async def main():
"""Main entry point."""
print("π Vivint MCP Server - MFA Setup")
print()
try:
success = await interactive_mfa_setup()
if success:
print()
print("π― Next steps:")
print("1. Start server: python src/server.py")
print("2. Generate JWT token: python src/generate_token.py --type token")
print("3. Test with MCP Inspector: npx @modelcontextprotocol/inspector")
else:
print()
print("β Setup failed. Please check your credentials and try again.")
sys.exit(1)
except KeyboardInterrupt:
print()
print("β οΈ Setup cancelled by user")
sys.exit(1)
except Exception as e:
print(f"β Unexpected error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())