#!/usr/bin/env python3
"""
Generate a new MCP API key and store it in the database
Usage: python scripts/generate_mcp_key.py --name "Claude Desktop" --user "user@example.com"
"""
import os
import sys
import hashlib
import secrets
import argparse
from datetime import datetime, timedelta
from pathlib import Path
# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))
import psycopg2
from psycopg2.extras import RealDictCursor
from dotenv import load_dotenv
# Load environment
env_path = Path(__file__).parent.parent / '.env'
if env_path.exists():
load_dotenv(env_path)
DB_CONFIG = {
'host': os.getenv('DB_HOST', 'localhost'),
'port': os.getenv('DB_PORT', '5432'),
'database': os.getenv('DB_NAME', 'iris'),
'user': os.getenv('DB_USER', 'postgres'),
'password': os.getenv('DB_PASSWORD', '')
}
def generate_api_key(name: str, user_email: str = None, days_valid: int = None):
"""Generate a new API key and store in database"""
# Generate random API key (32 bytes = 64 hex chars)
api_key = secrets.token_urlsafe(32)
# Hash the key for storage
key_hash = hashlib.sha256(api_key.encode()).hexdigest()
# Calculate expiry
expires_at = None
if days_valid:
expires_at = datetime.now() + timedelta(days=days_valid)
try:
# Insert into database
conn = psycopg2.connect(**DB_CONFIG)
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute("""
INSERT INTO mcp_api_keys (key_hash, key_name, user_email, expires_at)
VALUES (%s, %s, %s, %s)
RETURNING id, created_at
""", (key_hash, name, user_email, expires_at))
result = cursor.fetchone()
conn.commit()
cursor.close()
conn.close()
print("=" * 80)
print(f"β
MCP API Key Generated Successfully!")
print("=" * 80)
print(f"ID: {result['id']}")
print(f"Name: {name}")
print(f"User: {user_email or 'N/A'}")
print(f"Created: {result['created_at']}")
print(f"Expires: {expires_at or 'Never'}")
print()
print(f"π API Key (SAVE THIS - it won't be shown again!):")
print(f" {api_key}")
print()
print("=" * 80)
print("π Add this to your MCP client configuration:")
print()
print(f'Authorization: Bearer {api_key}')
print()
print("=" * 80)
return api_key
except Exception as e:
print(f"β Error generating API key: {str(e)}")
sys.exit(1)
def list_keys():
"""List all active API keys"""
try:
conn = psycopg2.connect(**DB_CONFIG)
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute("""
SELECT id, key_name, user_email, created_at, expires_at, last_used_at, is_active
FROM mcp_api_keys
ORDER BY created_at DESC
""")
keys = cursor.fetchall()
cursor.close()
conn.close()
print("=" * 120)
print(f"{'ID':<5} {'Name':<30} {'User':<30} {'Created':<20} {'Last Used':<20} {'Active':<8}")
print("=" * 120)
for key in keys:
status = "β
" if key['is_active'] else "β"
last_used = key['last_used_at'].strftime('%Y-%m-%d %H:%M') if key['last_used_at'] else 'Never'
print(f"{key['id']:<5} {key['key_name']:<30} {key['user_email'] or 'N/A':<30} "
f"{key['created_at'].strftime('%Y-%m-%d %H:%M'):<20} {last_used:<20} {status:<8}")
print("=" * 120)
except Exception as e:
print(f"β Error listing keys: {str(e)}")
sys.exit(1)
def revoke_key(key_id: int):
"""Revoke an API key"""
try:
conn = psycopg2.connect(**DB_CONFIG)
cursor = conn.cursor()
cursor.execute("""
UPDATE mcp_api_keys
SET is_active = FALSE
WHERE id = %s
RETURNING key_name
""", (key_id,))
result = cursor.fetchone()
conn.commit()
cursor.close()
conn.close()
if result:
print(f"β
API key '{result[0]}' (ID: {key_id}) has been revoked")
else:
print(f"β API key ID {key_id} not found")
except Exception as e:
print(f"β Error revoking key: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Manage MCP API Keys')
subparsers = parser.add_subparsers(dest='command', help='Command to execute')
# Generate command
gen_parser = subparsers.add_parser('generate', help='Generate a new API key')
gen_parser.add_argument('--name', required=True, help='Key name (e.g., "Claude Desktop")')
gen_parser.add_argument('--user', help='Associated user email')
gen_parser.add_argument('--days', type=int, help='Number of days until expiry (default: never)')
# List command
subparsers.add_parser('list', help='List all API keys')
# Revoke command
rev_parser = subparsers.add_parser('revoke', help='Revoke an API key')
rev_parser.add_argument('--id', type=int, required=True, help='Key ID to revoke')
args = parser.parse_args()
if args.command == 'generate':
generate_api_key(args.name, args.user, args.days)
elif args.command == 'list':
list_keys()
elif args.command == 'revoke':
revoke_key(args.id)
else:
parser.print_help()