A local macOS HTTP API gateway that exposes Apple-protected capabilities (Reminders, Messages) via a stable, agent-friendly REST API.
Modern AI agents running in VMs, containers, or remote hosts cannot access macOS-protected services due to:
- TCC Permission Enforcement — Apple restricts access to Reminders, Messages, etc.
- Sandbox Restrictions — VMs and containers can't invoke macOS CLIs
- Fragile CLI Integration — Direct CLI execution from agents is unreliable
MAG solves this by running on your Mac as a secure HTTP gateway, handling all TCC permissions and CLI execution while exposing clean REST endpoints.
In plain terms: MAG lets AI assistants work with your Apple Reminders and Messages—apps that are normally locked to your Mac—in a controlled, secure way.
- Your AI assistant can now help with real tasks — "Add a reminder for tomorrow", "Find the links Jane sent me last week", "Text me my todo list"
- Works with any AI agent — OpenClaw, Claude Code, Cursor, or any tool that can make HTTP requests
- You stay in control — Choose exactly what the AI can do: read-only access, send only to specific contacts, or full access
- No cloud required — Everything runs locally on your Mac; your data never leaves your computer
- Built on trusted tools — Uses popular open-source CLIs (remindctl, imsg) with a standard REST API on top
Example permissions you can set:
- Allow reading messages but not sending
- Allow sending only to yourself or specific contacts
- Allow reminders but disable message access entirely
Try prompts like these with your AI agent:
- "What's on my todo list for today?"
- "Find the last 20 Trulia links my wife sent me and organize them by state and city"
- "Create a reminder for tomorrow at 9am to call the dentist"
- "Text me a summary of my overdue reminders"
- "Search my messages with John for anything about the project"
- "Find the last restaurant link Jane sent me, look it up, and text me the hours and address"
See EXAMPLES.md for 21+ real-world prompts and workflows.
flowchart LR
subgraph Agents["🤖 AI Agents"]
direction TB
A1["OpenClaw"]
A2["Claude Code"]
A3["Custom HTTP Client"]
end
subgraph Gateway["🖥️ Mac Agent Gateway"]
direction TB
subgraph API["REST API :8123"]
R["/v1/reminders"]
M["/v1/messages"]
end
subgraph Services["CLI Services"]
RC["remindctl"]
IM["imsg"]
end
API --> Services
end
subgraph Apple["🍎 macOS Services"]
direction TB
REM["Reminders.app"]
MSG["Messages.app"]
end
A1 & A2 & A3 -->|"HTTP + API Key"| API
RC -->|"TCC Granted"| REM
IM -->|"TCC Granted"| MSG
style Agents fill:#e1f5fe,stroke:#01579b
style Gateway fill:#fff3e0,stroke:#e65100
style Apple fill:#f3e5f5,stroke:#7b1fa2
style API fill:#ffe0b2,stroke:#ff6f00
style Services fill:#ffcc80,stroke:#ef6c00
Key Principle: Agents never execute Apple binaries. The gateway owns all permissions and CLI execution.
MAG runs on your Mac but can be securely accessed from local VMs (like Lume) or remote VPS instances via SSH tunneling:
flowchart LR
subgraph Remote["🌐 Remote / VM"]
direction TB
VM["Local VM (Lume)"]
VPS["Remote VPS"]
Agent["AI Agent"]
end
subgraph Tunnel["🔒 SSH Tunnel"]
SSH["Port Forward - localhost:8123"]
end
subgraph Mac["🖥️ Your Mac"]
MAG["MAG Gateway - 127.0.0.1:8123"]
Apps["Reminders, Messages"]
MAG --> Apps
end
VM -->|"ssh -L 8123:localhost:8123"| SSH
VPS -->|"ssh -R 8123:localhost:8123"| SSH
SSH --> MAG
Agent --> VM
Agent --> VPS
style Remote fill:#e3f2fd,stroke:#1565c0
style Tunnel fill:#e8f5e9,stroke:#2e7d32
style Mac fill:#fff3e0,stroke:#e65100
Common scenarios:
| Scenario | SSH Command (run on...) | Result |
|---|---|---|
| Local VM → Mac | ssh -L 8123:localhost:8123 mac-host (on VM) |
VM accesses MAG at localhost:8123 |
| VPS → Mac | ssh -R 8123:localhost:8123 vps-host (on Mac) |
VPS accesses MAG at localhost:8123 |
This keeps MAG secure (localhost-only) while enabling remote agents to use it through encrypted tunnels.
Tailscale / ZeroTier (alternative):
For persistent remote access without maintaining SSH sessions, a mesh VPN like Tailscale or ZeroTier is a reliable option:
# Bind MAG to all interfaces (required for Tailscale access)
MAG_HOST=0.0.0.0 make run
# Access via your Tailscale IP (e.g., 100.x.x.x)
curl -H "X-API-Key: $KEY" http://100.x.x.x:8123/healthNote: This configuration has not been personally tested by the author, but is a well-established pattern for secure remote access.
Security consideration: Binding to
0.0.0.0exposes MAG beyond localhost. While Tailscale's private network limits exposure, anyone on your tailnet (or who guesses your API key) could access the gateway. Mitigations:
- Use a strong, randomly-generated API key (32+ characters)
- Consider Tailscale ACLs to restrict which devices can reach your Mac
- Use the send allowlist to limit message recipients even if compromised
- Apple Reminders API — Full CRUD: create, list, update, complete, delete reminders and lists
- Apple Messages API — Send/reply to iMessages, list threads, search messages, extract links, stream new messages
- Attachment Downloads — Download photos and files from messages via secure REST API
- OpenAPI/Swagger — Auto-generated docs at
/docsand/openapi.json - Agent Skills — Portable skill definitions for OpenClaw (formerly Clawdbot/Moltbot), Cursor, and other agents
- Secure by Default — Localhost-only binding, API key auth, rate limiting, CORS protection, audit logging
- PII Filtering — Automatic masking of sensitive data (SSNs, credit cards, passwords) in messages
- Extensible — Modular architecture for adding new Apple capabilities
# 1. Clone the repository
git clone https://github.com/ericblue/mac-agent-gateway.git
cd mac-agent-gateway
# 2. Install CLI dependencies (Homebrew)
make install-deps # Installs remindctl and imsg
# 3. Install MAG (creates venv and installs Python dependencies)
make install
# 4. Configure
cp .env.example .env
make generate-api-key # Generate a secure key
# Edit .env and paste your generated key
# 5. Run
make dev # Development mode with auto-reloadThe gateway is now running at http://localhost:8123. Visit /docs for the interactive API documentation.
Note:
make installcreates a virtual environment at.venv/and installs all dependencies there. Allmakecommands use this venv automatically.
- macOS (required for Apple services access)
- Python 3.11+
- Homebrew
When first running, macOS will prompt you to grant Reminders and Messages permissions to Terminal/iTerm.
make install-depsThis installs:
make installcp .env.example .env
# Generate a secure API key (recommended)
make generate-api-keyEdit .env with your generated key:
# Required (minimum 16 characters, 32+ recommended)
MAG_API_KEY=your-generated-key-here
# Server settings
MAG_HOST=127.0.0.1 # Default: localhost only
MAG_PORT=8123 # Default port
MAG_LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR
# Audit logging (optional but recommended)
MAG_LOG_DIR=./logs # Enable file logging
MAG_LOG_ACCESS=true # Log HTTP requests
# Capability restrictions (all enabled by default)
MAG_MESSAGES_SEND=true # Enable/disable sending messages
MAG_MESSAGES_READ=true # Enable/disable reading messages
MAG_MESSAGES_ATTACHMENTS=true # Enable/disable attachment downloads
MAG_REMINDERS_READ=true # Enable/disable reading reminders
MAG_REMINDERS_WRITE=true # Enable/disable writing reminders
# Security restrictions (optional)
MAG_MESSAGES_SEND_ALLOWLIST=+15551234567,user@example.com # Limit recipients
MAG_ATTACHMENT_ALLOWED_DIRS=~/Downloads,~/Pictures # Limit attachment sourcesSee .env.example for all available options.
# Development mode (auto-reload)
make dev
# Production mode
make run
# Or directly via Python
mag# Health check (no auth required)
curl http://localhost:8123/health
# List today's reminders
curl -H "X-API-Key: your-key" "http://localhost:8123/v1/reminders?filter=today"
# Create a reminder
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"title": "Call mom", "due": "tomorrow", "list": "Personal"}' \
http://localhost:8123/v1/reminders
# Send an iMessage
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"to": "+15551234567", "text": "On my way!"}' \
http://localhost:8123/v1/messages/sendTo run MAG automatically on startup, use the included launchd plist template.
1. Copy and customize the plist:
# Copy the template
cp launchd/com.ericblue.mag.plist ~/Library/LaunchAgents/
# Edit the plist to set your paths and API key
# Update: WorkingDirectory, PYTHONPATH, MAG_API_KEY
nano ~/Library/LaunchAgents/com.ericblue.mag.plist2. Load the service:
# Load and start the service
launchctl load ~/Library/LaunchAgents/com.ericblue.mag.plist
# Verify it's running
curl http://localhost:8123/health3. Manage the service:
# Stop the service
launchctl unload ~/Library/LaunchAgents/com.ericblue.mag.plist
# Restart (unload + load)
launchctl unload ~/Library/LaunchAgents/com.ericblue.mag.plist
launchctl load ~/Library/LaunchAgents/com.ericblue.mag.plist
# View logs (stored in project logs/ directory)
tail -f logs/mag.log
tail -f logs/mag.error.logOr use the Makefile shortcuts:
make service-install # Copy plist to LaunchAgents (sets secure permissions)
make service-start # Load and start the service
make service-stop # Stop the service
make service-restart # Restart the service
make service-status # Check if running + health check
make service-logs # Tail the log files
make service-uninstall # Remove the plistSecurity Notes:
- The service runs as your user (LaunchAgent), which is required for TCC permissions. Do not use LaunchDaemons (system-level) as they cannot access Reminders/Messages.
make service-installsets the plist to mode 600 (owner-only) to protect your API key.- Logs are stored in
logs/within the project directory, not world-readable/tmp.
All endpoints except /health and /openapi.json require the X-API-Key header.
Interactive API documentation is available at /docs (Swagger UI) and /redoc (ReDoc):
| Method | Path | Description |
|---|---|---|
| GET | / |
Web UI homepage |
| GET | /health |
Health check (no auth) |
| GET | /v1/capabilities |
Discover enabled capabilities |
| GET | /docs |
Swagger UI |
| GET | /redoc |
ReDoc documentation |
| GET | /openapi.json |
OpenAPI specification |
| Method | Path | Description |
|---|---|---|
| GET | /v1/reminders |
List reminders with filters |
| POST | /v1/reminders |
Create a reminder |
| PATCH | /v1/reminders/{id} |
Update a reminder |
| POST | /v1/reminders/{id}/complete |
Mark as complete |
| DELETE | /v1/reminders/{id} |
Delete a reminder |
| POST | /v1/reminders/bulk/complete |
Bulk complete |
| POST | /v1/reminders/bulk/delete |
Bulk delete |
| GET | /v1/reminders/lists |
List all reminder lists |
| POST | /v1/reminders/lists |
Create a list |
| PATCH | /v1/reminders/lists/{name} |
Rename a list |
| DELETE | /v1/reminders/lists/{name} |
Delete a list |
Filter Options:
filter—today,tomorrow,week,overdue,upcoming,completed,alldate— Specific date (YYYY-MM-DD)list— Filter by list name
Reminder Fields:
title— Reminder textlist— Target list namedue— Due date (ISO 8601 or natural:today,tomorrow,next week)notes— Additional notespriority—0(none),1(high),5(medium),9(low)
| Method | Path | Description |
|---|---|---|
| GET | /v1/messages/threads |
List message threads |
| GET | /v1/messages/threads/lookup |
Find thread by recipient |
| GET | /v1/messages/threads/{id} |
Get thread by ID |
| GET | /v1/messages/threads/{id}/messages |
Get thread messages |
| GET | /v1/messages/threads/{id}/watch |
Watch for new messages (SSE) |
| GET | /v1/messages/history |
Get messages by recipient |
| POST | /v1/messages/send |
Send an iMessage |
| POST | /v1/messages/reply |
Reply to thread or recipient |
| GET | /v1/messages/search |
Search messages |
| GET | /v1/messages/links |
Extract links from messages |
| GET | /v1/messages/attachments/download |
Download an attachment file |
| GET | /v1/messages/attachments/info |
Get attachment file info |
Attachments:
To download attachments (photos, files) from messages:
# 1. Get messages with attachment metadata
curl -H "X-API-Key: $KEY" \
"http://localhost:8123/v1/messages/history?recipient=%2B15551234567&attachments=true"
# 2. Download using the original_path from the response
curl -H "X-API-Key: $KEY" \
"http://localhost:8123/v1/messages/attachments/download?path=/Users/you/Library/Messages/Attachments/..." \
--output photo.jpgSecurity: Only files within
~/Library/Messages/Attachments/can be downloaded.
Contacts Cache:
| Method | Path | Description |
|---|---|---|
| POST | /v1/messages/contacts/upsert |
Create/update contact |
| GET | /v1/messages/contacts/resolve |
Resolve contact by phone/email/name |
| GET | /v1/messages/contacts/search |
Search contacts |
| GET | /v1/messages/contacts |
List all contacts |
| DELETE | /v1/messages/contacts/{id} |
Delete contact |
Query Parameters:
recipient— Phone number, email, or iMessage handlelimit— Maximum results to returndays_back— Days of history to search (default: 365)start/end— Date range filter (ISO 8601)
MAG includes portable skill definitions that enable AI agents to use the API without platform-specific code.
| Skill | Path | Description |
|---|---|---|
| mag-reminders | skills/mag-reminders/SKILL.md |
Apple Reminders management |
| mag-messages | skills/mag-messages/SKILL.md |
Apple Messages (iMessage) management |
Skills are compatible with:
- OpenClaw (formerly Clawdbot/Moltbot) — Via YAML frontmatter metadata
- Cursor / Claude Code — Via markdown skill format
- Any HTTP-capable agent — Via curl examples
For Claude Code:
# Install all skills to ~/.claude/skills/
make claude-skill-install
# Check installation status
make claude-skill-check
# Then in Claude Code, use:
# /add ~/.claude/skills/mag-reminders/SKILL.md
# /add ~/.claude/skills/mag-messages/SKILL.mdFor OpenClaw (Manual):
# Clone and copy to your agent's skills directory
git clone https://github.com/ericblue/mac-agent-gateway.git
cp -r mac-agent-gateway/skills/mag-reminders ~/.moltbot/skills/
# Or: cp -r mac-agent-gateway/skills/mag-reminders ~/.openclaw/skills/For OpenClaw (Agent-Assisted):
Clone the repo, then prompt your agent:
git clone https://github.com/ericblue/mac-agent-gateway.git ~/Development/mac-agent-gateway"Install the skill from ~/Development/mac-agent-gateway/skills/mag-reminders/SKILL.md"
Direct from GitHub:
Prompt your agent:
"Install the mag-reminders skill from https://github.com/ericblue/mac-agent-gateway"
For OpenClaw (formerly Clawdbot/Moltbot), there are two locations to configure:
| What | Location | Purpose |
|---|---|---|
| Skill files | ~/clawd/skills/ |
SKILL.md files with API docs |
| Credentials | ~/.clawdbot/clawdbot.json |
MAG_URL, MAG_API_KEY, enabled status |
Use the Makefile targets:
# 1. Install skill files to ~/clawd/skills/
make openclaw-skill-install
# Or specify a custom skills directory:
# make openclaw-skill-install OPENCLAW_SKILLS_DIR=~/.clawdbot/skills
# 2. Configure credentials in ~/.clawdbot/clawdbot.json
make openclaw-skill-config MAG_URL=http://localhost:8124 MAG_API_KEY=your-secret-api-key
# 3. Verify both locations
make openclaw-skill-check MAG_URL=http://localhost:8124After configuration, restart/reload OpenClaw so it picks up the changes.
Manual configuration (alternative):
Add this to ~/.clawdbot/clawdbot.json:
{
"skills": {
"entries": {
"mag-reminders": {
"enabled": true,
"env": {
"MAG_URL": "http://localhost:8124",
"MAG_API_KEY": "your-secret-api-key-here"
}
},
"mag-messages": {
"enabled": true,
"env": {
"MAG_URL": "http://localhost:8124",
"MAG_API_KEY": "your-secret-api-key-here"
}
}
}
}
}Agents can also generate tools directly from the OpenAPI specification:
curl http://localhost:8123/openapi.jsonFor detailed security information, see SECURITY.md.
By default, MAG binds to 127.0.0.1, accepting only local connections. This is the recommended configuration.
For agents running on VMs or remote hosts:
Option 1: SSH Tunnel (Recommended)
# Option A: From the agent VM, tunnel to the Mac host
# Run this ON THE VM/AGENT machine
ssh -L 8123:localhost:8123 user@mac-host
# Option B: From the Mac host, reverse tunnel to the VM
# Run this ON THE MAC HOST
ssh -R 8123:localhost:8123 user@agent-vm
# Either way, the agent can now access http://localhost:8123Option 2: Bind to Network Interface
MAG_HOST=0.0.0.0 make run
⚠️ Warning: Binding to0.0.0.0exposes the API to your network. Ensure you:
- Use a strong, unique API key
- Restrict access via firewall rules
- Consider HTTPS via a reverse proxy (nginx, Caddy)
All protected endpoints require the X-API-Key header:
curl -H "X-API-Key: your-secret-key" http://localhost:8123/v1/remindersFine-grained access control via environment variables:
| Variable | Default | Description |
|---|---|---|
MAG_MESSAGES_READ |
true | List threads, get message history |
MAG_MESSAGES_SEARCH |
true | Search messages, extract links |
MAG_MESSAGES_SEND |
true | Send messages, reply to threads |
MAG_MESSAGES_WATCH |
true | Stream new messages (SSE) |
MAG_MESSAGES_CONTACTS |
true | Manage contacts cache |
MAG_MESSAGES_ATTACHMENTS |
true | Download message attachments |
MAG_REMINDERS_READ |
true | List reminders and lists |
MAG_REMINDERS_WRITE |
true | Create, update, delete reminders |
Agents can discover enabled capabilities via GET /v1/capabilities.
The API includes rate limiting to prevent abuse:
- Global limit: 100 requests per minute per IP
- Send endpoints: 10 requests per minute per IP (for
/sendand/reply)
When rate limited, the API returns 429 Too Many Requests.
Enable file-based access logging for security monitoring:
MAG_LOG_DIR=./logs # Enable file logging
MAG_LOG_ACCESS=true # Log all HTTP requestsAccess logs record: timestamp, client IP, method, path, status, and duration.
Restrict message sending to specific recipients:
# Only allow sending to these phone numbers/emails
MAG_MESSAGES_SEND_ALLOWLIST=+15551234567,+15559876543,user@example.comIf set, any attempt to send to a recipient not in the list returns 403 Forbidden. Leave empty (default) to allow all recipients.
# Run tests
make test
# Run linter
make lint
# Format code
make format
# Clean build artifacts
make cleanmac-agent-gateway/
├── src/mag/
│ ├── main.py # FastAPI app entry point
│ ├── config.py # Configuration management
│ ├── auth.py # API key authentication
│ ├── models/ # Pydantic models
│ ├── routers/ # API route handlers
│ ├── services/ # CLI wrapper services
│ └── templates/ # Web UI templates
├── skills/ # Agent skill definitions
├── tests/ # Test suite
└── docs/ # Documentation
- v0.1 — Reminders CRUD, Messages send, API key auth
- v0.2 — Full Messages API (threads, history, search, reply, links, SSE streaming)
- v0.3 — Calendar integration
- v0.4 — Contacts integration (system contacts)
- v0.5 — MCP (Model Context Protocol) server mode
- v1.0 — Plugin registry for custom capabilities
See EXAMPLES.md for 21+ real-world prompts you can use with AI agents, including:
- Daily reminder digests
- Message search and link extraction
- Creating reminders from messages
- Sending notifications via iMessage
- Combined workflows (capture → action → notify)
If you see "access denied" errors:
- Open System Preferences → Privacy & Security → Reminders (or Messages)
- Ensure Terminal/iTerm is checked
- Restart the gateway
# Reinstall dependencies
make install-deps
# Verify installation
which remindctl
which imsg# Check if gateway is running
curl http://localhost:8123/health
# Check port availability
lsof -i :8123Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License — see LICENSE for details.
| Version | Date | Description |
|---|---|---|
| 0.3.0 | 2026-01-31 | Security hardening + Attachment downloads |
| 0.2.0 | 2026-01-31 | Enhanced Messages API |
| 0.1.0 | 2026-01-29 | Initial release |
- Attachment Download API — Download photos and files from messages via
/v1/messages/attachments/download - Rate Limiting — Global 100/min limit, 10/min for send endpoints
- CORS Protection — Restrictive cross-origin policy (localhost only by default)
- Audit Logging — Optional file-based access logging with rotation
- Input Validation — Path parameter validation to prevent command injection
- Error Sanitization — Internal error details no longer leaked to clients
- API Key Security — Minimum 16-character requirement, blocks common placeholders
- File Permissions — Contacts cache and logs created with secure permissions (600)
- Attachment Security — Downloads restricted to
~/Library/Messages/Attachments/ - Send Allowlist Privacy — Allowlist redacted in unauthenticated
/v1/capabilitiesresponses - 25 security tests — Comprehensive test coverage for all security controls
- Full Messages API: threads, history, search, reply, links extraction
- Server-Sent Events (SSE) for watching new messages
- Contacts cache with persistence
- Date range filtering with
days_back,start,endparameters - Recipient lookup by phone, email, or handle
- New
mag-messagesskill for agent integration - Capability restrictions via environment variables
- Send allowlist for restricting message recipients
- Capabilities discovery endpoint (
GET /v1/capabilities)
- Apple Reminders API with full CRUD operations
- Apple Messages API (send, list conversations)
- API key authentication
- OpenAPI/Swagger documentation
- Agent skill definitions (OpenClaw, Claude Code compatible)
- Bulk operations for reminders (complete, delete)
- Reminder list management (create, rename, delete)
- launchd service support for running on startup
- Web UI landing page
- Comprehensive test suite
Mac Agent Gateway (MAG) is a local macOS HTTP API gateway that enables AI agents running in sandboxed environments to safely access Apple-protected system services.
This project addresses a fundamental challenge: AI agents running in VMs, containers, or on remote hosts cannot access macOS TCC-protected services like Reminders and Messages. MAG bridges this gap by running on your Mac as a secure HTTP gateway, handling all permissions and CLI execution while exposing clean, agent-friendly REST endpoints.
- Security First — Localhost-only by default, API key authentication, no arbitrary command execution
- Agent Agnostic — Works with any HTTP-capable agent (OpenClaw, Claude Code, custom integrations)
- Capability Focused — Intent-level APIs (create reminder, send message) not raw CLI access
- Portable Skills — Thin HTTP skills that run anywhere, no Apple binaries required
Created by Eric Blue
Repository: github.com/ericblue/mac-agent-gateway
