Skip to main content
Glama
drewster99

xcode-mcp-server (drewster99)

by drewster99
applescript.py6.38 kB
#!/usr/bin/env python3 """AppleScript execution and notification utilities""" import subprocess import sys import datetime from typing import Tuple, List, Dict # Global notification setting - initialized by CLI NOTIFICATIONS_ENABLED = True # Global notification history - stores all notifications posted NOTIFICATION_HISTORY: List[Dict[str, str]] = [] def set_notifications_enabled(enabled: bool): """Set the global notification setting""" global NOTIFICATIONS_ENABLED NOTIFICATIONS_ENABLED = enabled def get_notification_history() -> List[Dict[str, str]]: """Get the notification history""" return NOTIFICATION_HISTORY.copy() def clear_notification_history(): """Clear the notification history""" global NOTIFICATION_HISTORY NOTIFICATION_HISTORY = [] def escape_applescript_string(s: str) -> str: """ Escape a string for safe use in AppleScript. Args: s: String to escape Returns: Escaped string safe for AppleScript """ # Escape backslashes first, then quotes s = s.replace("\\", "\\\\") s = s.replace('"', '\\"') return s def run_applescript(script: str) -> Tuple[bool, str]: """Run an AppleScript and return success status and output""" try: result = subprocess.run(['osascript', '-e', script], capture_output=True, text=True, check=True) return True, result.stdout.strip() except subprocess.CalledProcessError as e: return False, e.stderr.strip() def show_notification(title: str, subtitle: str = None, message: str = None, sound: bool = False): """Show a macOS notification if notifications are enabled Args: title: Notification title subtitle: Optional subtitle (shown below title) message: Notification message body sound: Whether to play a sound (for errors/important events) """ # Record in history (always, even if notifications are disabled) global NOTIFICATION_HISTORY NOTIFICATION_HISTORY.append({ 'timestamp': datetime.datetime.now().isoformat(), 'title': title, 'subtitle': subtitle or '', 'message': message or '', 'sound': str(sound) }) # Check global setting first if not NOTIFICATIONS_ENABLED: return # Check if we're in a tool context and if that tool has notifications disabled try: from xcode_mcp_server.config_manager import get_active_tool_context, ConfigManager context = get_active_tool_context() if context: # We're in a tool execution context tool_name = context.get('tool_name') project_path = context.get('project_path') if tool_name: config = ConfigManager() # Check if this specific tool should show notifications if not config.should_show_notification(tool_name, project_path): return except ImportError: # If we can't import, just use global setting pass # Show the notification try: # Build AppleScript command - message is required by AppleScript msg = message or subtitle or title escaped_msg = escape_applescript_string(msg) escaped_title = escape_applescript_string(title) script = f'display notification "{escaped_msg}" with title "{escaped_title}"' if subtitle: escaped_subtitle = escape_applescript_string(subtitle) script += f' subtitle "{escaped_subtitle}"' if sound: script += ' sound name "Frog"' subprocess.run(['osascript', '-e', script], capture_output=True) except: pass # Ignore notification errors def show_error_notification(message: str, details: str = None): """Show an error notification with sound""" show_notification("Drew's Xcode MCP", subtitle=details, message=f"❌ {message}", sound=True) def show_warning_notification(message: str, details: str = None): """Show a warning notification""" show_notification("Drew's Xcode MCP", subtitle=details, message=f"⚠️ {message}") def show_access_denied_notification(message: str, details: str = None): """Show an access denied notification with sound""" show_notification("Drew's Xcode MCP", subtitle=details, message=f"⛔ {message}", sound=True) def show_result_notification(message: str, details: str = None): """Show a result notification""" show_notification("Drew's Xcode MCP", subtitle=details, message=message) def show_persistent_alert(title: str, message: str, button_text: str = "OK") -> subprocess.Popen: """ Show a persistent macOS alert dialog that stays on screen until dismissed. Returns a Popen object representing the background process. The process will exit when the user clicks the button, allowing you to detect dismissal. Args: title: Alert dialog title message: Alert dialog message body (newlines are supported) button_text: Text for the button (default "OK") Returns: subprocess.Popen object for the alert process (None if notifications disabled) """ if NOTIFICATIONS_ENABLED: try: # Escape strings for AppleScript escaped_title = escape_applescript_string(title) escaped_button = escape_applescript_string(button_text) # Handle newlines in message - replace \n with AppleScript's 'return' # First escape the message normally, then handle newlines escaped_message = escape_applescript_string(message) # Replace escaped newlines with AppleScript return concatenation escaped_message = escaped_message.replace('\\n', '" & return & "') # Build AppleScript for alert dialog script = f'display dialog "{escaped_message}" with title "{escaped_title}" buttons {{"{escaped_button}"}} default button "{escaped_button}" with icon caution' # Run in background (non-blocking) and return the process return subprocess.Popen( ['osascript', '-e', script], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL ) except Exception as e: print(f"Warning: Failed to show alert: {e}", file=sys.stderr) return None return None

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/drewster99/xcode-mcp-server'

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