Files
claude-automation/README.md
2026-02-08 17:47:49 -06:00

718 lines
20 KiB
Markdown
Executable File

# Claude Desktop Automation with MCP Control
Automatically send periodic messages to Claude Desktop on macOS, with **Claude controlling its own wake schedule** via MCP and **bidirectional Matrix integration** for AI-powered messaging.
## 🎯 How It Works
**Three-part system:**
1. **Automation Daemon** - Python process that:
- Runs continuously in the background
- Sends messages to Claude Desktop on a timer (with CMD+R refresh)
- Reads wake schedule from shared state file
- Monitors for Matrix wake requests
- Uses AppleScript for reliable message delivery
2. **Wakeup Control MCP** - MCP server that lets Claude:
- Adjust when it wants to be woken next
- Pause/resume automation
- Check status
3. **Matrix Integration** (Optional) - Bidirectional messaging:
- Monitor Matrix rooms for incoming messages
- Wake Claude when messages arrive (2-min rate limit)
- Claude can read messages and respond via MCP tools
- Supports text + images with automatic compression
**The magic:** Claude can call `next_wakeup(30)` to say "wake me in 30 minutes instead of the default 60", and Matrix messages automatically wake Claude when your friends message you!
## Architecture
```
┌──────────────────────────────────────────────┐
│ Matrix Monitor (Python/matrix-nio) │
│ - Listens for Matrix messages │
│ - Queues messages to state file │
│ - Sets matrix_wake_requested flag │
│ - 2-minute rate limiting │
└──────────────┬───────────────────────────────┘
│ (writes Matrix messages)
~/.claude-automation-state.json
│ (reads state + Matrix requests)
┌──────────────┴───────────────────────────────┐
│ Automation Daemon (Python) │
│ - Timer loop (handles own scheduling) │
│ - Monitors for Matrix wake requests │
│ - Sends messages via AppleScript │
└──────────────┬───────────────────────────────┘
│ (sends message)
┌──────────────────────┐
│ Claude Desktop UI │
└────────┬─────────────┘
│ (calls MCP tools)
┌─────────────────────────────────────────────┐
│ MCP Servers (FastMCP) │
│ │
│ Wakeup Control: │
│ - next_wakeup(minutes) │
│ - pause_automation() │
│ - resume_automation() │
│ - get_status() │
│ │
│ Matrix Control: │
│ - get_matrix_messages() │
│ - send_matrix_message(room_id, msg) │
│ - mark_messages_processed(event_ids) │
│ - list_matrix_rooms() │
│ - get_matrix_status() │
└──────────────┬──────────────────────────────┘
│ (writes state)
~/.claude-automation-state.json
```
## Requirements
**Core System:**
- macOS (tested on macOS 13+)
- Python 3 (built-in)
- Claude Desktop
- FastMCP (`pip install fastmcp`)
- AppleScript (built-in to macOS)
- Accessibility permissions (critical - see below)
**Matrix Integration (Optional):**
- Matrix account (matrix.org or self-hosted)
- matrix-nio (`pip install matrix-nio`)
- Pillow (`pip install Pillow`)
## Installation
### Quick Setup
```bash
cd ~/scripts/claude-desktop-automation
./setup.sh
```
The setup script will:
1. Check dependencies
2. Guide you through permissions
3. Install daemon (launchd)
4. Configure MCP server
5. Start everything
### Manual Setup
If you prefer manual installation:
#### 1. Install Dependencies
```bash
# Install all dependencies
pip3 install -r requirements.txt
# Or install individually:
pip3 install fastmcp
```
#### 2. Grant Accessibility Permissions (CRITICAL)
**This is required for AppleScript automation to work!**
1. Open **System Settings****Privacy & Security****Accessibility**
2. Click the **+** button
3. Add **Terminal** (or your Python interpreter)
4. Ensure the checkbox is enabled
**Testing accessibility:**
```bash
python3 send_to_claude.py "Test" --no-refresh
```
If you see "Failed to send message", accessibility permissions are not set correctly.
#### 3. Configure MCP Server
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"wakeup-control": {
"command": "python3",
"args": ["/Users/YOUR_USERNAME/scripts/claude-desktop-automation/wakeup_mcp.py"]
}
}
}
```
#### 4. Install Daemon
```bash
# Copy and customize plist
cp ~/scripts/claude-desktop-automation/com.claude.monitor.plist ~/Library/LaunchAgents/
sed -i '' "s|/Users/alex|$HOME|g" ~/Library/LaunchAgents/com.claude.monitor.plist
# Load daemon
launchctl load ~/Library/LaunchAgents/com.claude.monitor.plist
```
#### 5. Restart Claude Desktop
Close and reopen Claude Desktop to load the MCP server.
## 🔄 Chat Refresh Feature
**All wake messages now include CMD+R refresh before sending!**
This prevents messages from being overwritten when using Claude Desktop on multiple devices:
- **Timed wakes**: CMD+R ensures chat is synced before system check message
- **Matrix wakes**: CMD+R ensures you see the latest conversation context
The daemon waits **15 seconds** after sending CMD+R to ensure the refresh completes before sending the message.
**Why this matters:**
- Multi-device sync: If you're active on mobile/web, desktop chat stays current
- Prevents message overwrites: Chat refreshes before automation sends
- Better context: Claude sees all recent messages, not stale state
**Configurable delay:** Edit `REFRESH_DELAY_SECONDS` in `send_to_claude.py` line 31 if you need more/less time.
## Usage
### The Flow
1. **Daemon wakes** (default: every 60 minutes)
2. **Sends message** to Claude Desktop: "System check at [time] - use next_wakeup() if you want to change interval"
3. **Claude responds** and optionally calls MCP tools
4. **Daemon sleeps** until next wake time (uses MCP-set time or default)
### MCP Tools
#### `next_wakeup(minutes)`
Set when you want to be woken next. **Overrides default for one cycle only.**
```
Claude, use next_wakeup(15) to wake me in 15 minutes
Claude, use next_wakeup(120) to wake me in 2 hours
Claude, use next_wakeup(1440) to skip until tomorrow
```
**Use cases:**
- High activity period → `next_wakeup(15)` for frequent checks
- Low activity → `next_wakeup(180)` for infrequent checks
- One-time skip → `next_wakeup(1440)` for 24 hours
#### `pause_automation()`
Stop wake messages completely.
```
Claude, pause the automation - I don't need monitoring right now
```
#### `resume_automation()`
Resume wake messages.
```
Claude, resume the automation
```
#### `get_status()`
Check current state.
```
Claude, what's the automation status?
```
Returns:
- Running/paused state
- Last wake time
- Next wake time
- Current interval
### Example Conversation
**Daemon:** (sends at 9:00 AM) "System check at 2025-01-15 09:00:00 - please analyze recent activity and use next_wakeup() if you want to change the monitoring interval"
**You:** "Claude, everything looks normal. Check back in 2 hours instead of 1 hour."
**Claude:** "I'll adjust the wake schedule. Using next_wakeup(120)..."
[calls `next_wakeup(120)`]
**Claude:** "✓ Next wake scheduled for 2025-01-15 11:00:00 (in 120 minutes)"
**Daemon:** (wakes at 11:00 AM instead of 10:00 AM)
## Configuration
### Change Default Interval
Edit `automation_daemon.py` line 32:
```python
DEFAULT_INTERVAL_MINUTES = 60 # Change to your preferred default
```
### Change Message Text
Edit `automation_daemon.py` line 166-169:
```python
def generate_message() -> str:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return f"Your custom message template with {timestamp}"
```
## Monitoring
### Logs
```bash
# Daemon logs (timer, messages sent)
tail -f /tmp/claude-automation-daemon.log
# MCP logs (tool calls from Claude)
tail -f /tmp/wakeup-mcp.log
# launchd stdout/stderr
tail -f /tmp/claude-monitor.out
tail -f /tmp/claude-monitor.err
```
### Check Daemon Status
```bash
# Is it running?
launchctl list | grep com.claude.monitor
# View details
launchctl print gui/$(id -u)/com.claude.monitor
# Check process
ps aux | grep automation_daemon.py
```
### State File
The shared state is stored in `~/.claude-automation-state.json`:
```bash
cat ~/.claude-automation-state.json
```
Example:
```json
{
"interval_minutes": 60,
"paused": false,
"last_wake": "2025-01-15T09:00:00",
"next_wake_timestamp": "2025-01-15T11:00:00"
}
```
## Troubleshooting
### Messages not sending
**Check Claude Desktop is running:**
```bash
ps aux | grep Claude
```
**Check daemon is running:**
```bash
launchctl list | grep com.claude.monitor
tail -20 /tmp/claude-automation-daemon.log
```
**Check Accessibility permissions:**
System Settings → Privacy & Security → Accessibility → Terminal should be enabled
**Test AppleScript accessibility:**
```bash
python3 /path/to/send_to_claude.py "Test message" --no-refresh
```
If you see "Failed to send CMD+R" or "Failed to send message", check accessibility permissions:
1. System Settings → Privacy & Security → Accessibility
2. Find Terminal (or Python) in the list
3. Enable it (toggle on)
4. Restart Terminal
5. Test again
**Refresh delay:**
The default 10-second delay after CMD+R should be sufficient for most cases. If your Claude Desktop takes longer to refresh, edit `send_to_claude.py` line 31:
```python
REFRESH_DELAY_SECONDS = 15 # Increase if needed
```
### MCP tools not available
**Restart Claude Desktop** after adding MCP server to config
**Check MCP server config:**
```bash
cat ~/Library/Application\ Support/Claude/claude_desktop_config.json
```
**Check MCP logs:**
```bash
tail -f /tmp/wakeup-mcp.log
```
### `next_wakeup()` not working
**Check grace period** - Daemon waits 30 seconds after sending message before rescheduling
**Check state file** - Should update when you call the tool:
```bash
cat ~/.claude-automation-state.json
```
**Check MCP logs** - Confirms tool was called:
```bash
tail /tmp/wakeup-mcp.log
```
### Daemon keeps restarting
**Check for errors:**
```bash
tail -50 /tmp/claude-automation-daemon.log
```
**Temporarily stop it:**
```bash
launchctl unload ~/Library/LaunchAgents/com.claude.monitor.plist
```
### Timed wake-ups not appearing (but Matrix wakes work)
This typically happens when the **screen saver is active**. Matrix wakes work because they're reactive (screen is likely active), but timed wakes happen on schedule when the screen saver is often running.
**Solution:** The AppleScript now uses `caffeinate -u` to wake the screen before sending keystrokes:
```applescript
-- Wake the screen if screen saver is active
do shell script "caffeinate -u -t 1"
delay 0.5
```
This simulates user activity and dismisses the screen saver, allowing keyboard automation to work.
**To verify it's working:**
```bash
tail -f /tmp/claude-automation-daemon.log
# Look for: "Waking screen if needed..." in AppleScript logs
```
**Alternative:** If you want to prevent screen saver entirely on the Claude Desktop machine:
```bash
# Disable screen saver
defaults -currentHost write com.apple.screensaver idleTime 0
```
## Matrix Integration
The automation supports **bidirectional Matrix messaging** - Claude can receive messages from Matrix rooms and respond to them automatically.
### Setup
```bash
cd ~/scripts/claude-desktop-automation
./setup_matrix.sh
```
The setup script will:
1. Install matrix-nio dependency
2. Authenticate with your Matrix homeserver
3. Save credentials securely (chmod 600)
4. Configure room whitelist (optional)
5. Add Matrix MCP to Claude config
6. Start Matrix monitor
### How It Works
1. **Matrix Monitor** runs in background, listening for messages
2. New messages are queued to `~/.claude-automation-state.json`
3. Monitor sets `matrix_wake_requested` flag (respects 2-min rate limit)
4. **Automation Daemon** detects flag and wakes Claude
5. Claude uses **Matrix MCP tools** to read and respond
6. **Messages auto-marked as processed** when retrieved (won't trigger duplicate wakes)
### Matrix MCP Tools
#### `get_matrix_messages(limit=10, include_processed=False)`
Retrieve queued Matrix messages (text + images).
**✨ Messages are automatically marked as processed when retrieved!**
```
Claude, check my Matrix messages
Claude, get the last 20 Matrix messages
```
Returns messages with:
- Room name and sender
- Message text or inline image
- Timestamp and event ID
- Auto-marked as processed (won't trigger future wakes)
#### `send_matrix_message(room_id, message)`
Send a message to a Matrix room.
```
Claude, send "Hello!" to room !abc123:matrix.org
```
#### `mark_messages_processed(event_ids)` [OPTIONAL]
Manually mark messages as processed (usually not needed).
**Note:** Messages are automatically marked when you call `get_matrix_messages()`,
so you typically don't need to call this tool manually.
```
Claude, mark those messages as processed
```
#### `list_matrix_rooms()`
List all Matrix rooms the bot is in.
```
Claude, show my Matrix rooms
```
#### `get_matrix_status()`
Check Matrix integration status.
```
Claude, check Matrix status
```
Shows:
- Connection status
- Queued messages
- Last wake time
- Rate limit status
### Configuration Files
**Credentials:** `~/.matrix-credentials.json` (chmod 600)
```json
{
"homeserver": "https://matrix.org",
"user_id": "@user:matrix.org",
"access_token": "...",
"device_id": "...",
"room_whitelist": ["!room1:matrix.org", "!room2:matrix.org"]
}
```
**Room Whitelist:** (Optional)
- Empty array = Monitor all rooms
- Specific IDs = Monitor only those rooms
### Rate Limiting
Matrix wakes respect a **2-minute minimum** between wakes to prevent spam:
- Messages arriving within 2 minutes are queued
- Claude processes them in batch at next wake
- Reduces interruptions while ensuring responsiveness
### Image Support
Matrix images are automatically:
1. Downloaded from homeserver
2. Compressed to fit 1MB limit (accounting for base64 overhead)
3. Displayed inline in Claude Desktop
4. Queued just like text messages
### Logs
```bash
# Matrix monitor logs
tail -f /tmp/matrix-integration.log
# Matrix MCP logs
tail -f /tmp/matrix-mcp.log
# Check state file
cat ~/.claude-automation-state.json | jq '.matrix_messages'
```
### Troubleshooting
**Messages not appearing:**
- Check Matrix monitor is running: `ps aux | grep matrix_integration.py`
- Check logs: `tail -20 /tmp/matrix-integration.log`
- Verify credentials: `cat ~/.matrix-credentials.json`
**Claude not waking for Matrix:**
- Check state file has messages: `cat ~/.claude-automation-state.json`
- Check `matrix_wake_requested` flag
- Verify 2-minute rate limit hasn't blocked wake
- Check daemon logs: `tail -20 /tmp/claude-automation-daemon.log`
**Can't send messages:**
- Verify room ID is correct (use `list_matrix_rooms()`)
- Check MCP logs: `tail /tmp/matrix-mcp.log`
- Test credentials with `get_matrix_status()`
### Example Workflow
1. **Friend sends Matrix message**: "Hey, how's it going?"
2. **Matrix Monitor** detects message:
- Queues to state file
- Sets wake flag (if rate limit OK)
3. **Automation Daemon** wakes Claude:
- "Matrix wake at 2025-01-15 14:32:00 - you have 1 new Matrix message(s)"
4. **You tell Claude**: "Check my Matrix messages and respond"
5. **Claude**:
- Calls `get_matrix_messages()`
- Sees friend's message
- Calls `send_matrix_message(room_id, "I'm doing great! Thanks for asking!")`
- Calls `mark_messages_processed([event_id])`
6. **Friend receives** Claude's response in Matrix room
## Advanced Usage
### Multiple Messages Per Day
Use `next_wakeup()` strategically:
```
Morning: next_wakeup(180) # 3 hours (light monitoring)
Workday: next_wakeup(30) # 30 min (active monitoring)
Evening: next_wakeup(360) # 6 hours (minimal monitoring)
```
### Conditional Wake Logic
Modify `generate_message()` to include context:
```python
def generate_message() -> str:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Check system state
disk_usage = get_disk_usage()
cpu_load = get_cpu_load()
return f"System check at {timestamp} - Disk: {disk_usage}%, CPU: {cpu_load}%"
```
### Event-Driven Wakeup
Have other scripts write to the state file to trigger immediate wake:
```bash
# External script
echo '{"next_wake_timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%S)'"}' > ~/.claude-automation-state.json
```
## Uninstalling
```bash
# Stop daemon
launchctl unload ~/Library/LaunchAgents/com.claude.monitor.plist
rm ~/Library/LaunchAgents/com.claude.monitor.plist
# Stop Matrix monitor (if running)
pkill -f matrix_integration.py
# Remove MCP servers from Claude config
# (Edit ~/Library/Application Support/Claude/claude_desktop_config.json)
# Remove both "wakeup-control" and "matrix-control" entries
# Remove scripts (optional)
rm -rf ~/scripts/claude-desktop-automation
# Remove state and credentials
rm ~/.claude-automation-state.json
rm ~/.matrix-credentials.json
rm -rf ~/.matrix-data
# Remove logs (optional)
rm /tmp/claude-automation-daemon.log
rm /tmp/wakeup-mcp.log
rm /tmp/matrix-integration.log
rm /tmp/matrix-mcp.log
```
## Project Structure
```
claude-desktop-automation/
├── automation_daemon.py # Main daemon with timer loop + Matrix integration
├── wakeup_mcp.py # MCP server for wake control
├── matrix_integration.py # Matrix monitor (listens for messages)
├── matrix_mcp.py # MCP server for Matrix control
├── send_to_claude.scpt # AppleScript for UI automation (with screen wake)
├── com.claude.monitor.plist # launchd config (keeps daemon alive)
├── setup.sh # Interactive installation
├── setup_matrix.sh # Matrix integration setup
└── README.md # This file
```
## Why This Architecture?
**Before (launchd scheduling):**
- Fixed intervals
- No flexibility
- launchd does timing
**After (Python timer + MCP control):**
- Dynamic intervals
- Claude has agency
- Can adjust based on activity
- Bidirectional communication
- Much more elegant!
## Limitations
⚠️ **Still fragile** - UI automation breaks if Claude Desktop UI changes significantly
⚠️ **Active chat only** - Sends to whichever chat is currently open (CMD+R refreshes it first)
⚠️ **macOS only** - Uses AppleScript and launchd (Linux port would require xdotool/ydotool)
⚠️ **Requires Accessibility** - Special macOS permissions for UI automation
⚠️ **Screen saver handled** - Uses `caffeinate` to wake screen automatically
⚠️ **Fixed refresh delay** - Waits 15 seconds after CMD+R (configurable if needed)
## Future Plans
🔮 **Linux support** - Port to Linux using xdotool/ydotool for UI automation
🔮 **Windows support** - Port to Windows using pyautogui or AutoIt
🔮 **Wayland support** - Use ydotool for newer Linux distros
🔮 **Smarter refresh** - Detect if refresh is needed before sending CMD+R
## License
Public domain / Use at your own risk
---
**Built with Python + FastMCP + matrix-nio + AppleScript for intelligent automation** 🤖