| name | macos-mcp-integration |
| description | Patterns for building MCP servers that integrate with macOS system APIs via PyObjC. Auto-activates when building MCPs for Calendar, Contacts, Reminders, Notes, or other macOS frameworks. Trigger keywords: EventKit, pyobjc, macOS MCP, iCal, Calendar API, Contacts framework, NSDate, EKEventStore. |
| license | MIT |
| metadata | {"version":"1.0.1","hermes":{"tags":["macOS","MCP","PyObjC","EventKit","Calendar","Contacts"],"related_skills":["macos-applescript-integration"]}} |
macOS MCP integration patterns
Guide for creating MCP servers that access macOS system APIs via Python/PyObjC.
Project structure
project-name/
├── pyproject.toml
├── CLAUDE.md
└── src/project_name/
├── __init__.py
├── __main__.py # Entry: from .server import main; main()
└── server.py # MCP server implementation
pyproject.toml template
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "project-name"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"mcp>=1.0.0",
"pyobjc-framework-EventKit>=10.0",
]
[tool.hatch.build.targets.wheel]
packages = ["src/project_name"]
MCP configuration (.mcp.json)
{
"mcpServers": {
"server-name": {
"command": "uv",
"args": ["run", "--directory", "/path/to/project", "python", "-m", "project_name"]
}
}
}
Location: ~/.claude/.mcp.json (user-level) or .mcp.json (project-level).
Permission request pattern
import EventKit
_store = None
def get_store():
global _store
if _store is None:
_store = EventKit.EKEventStore.alloc().init()
granted = [False]
def callback(success, err):
granted[0] = success
_store.requestAccessToEntityType_completion_(
EventKit.EKEntityTypeEvent, callback
)
import time
time.sleep(0.5)
if not granted[0]:
raise PermissionError(
"Access not granted. Enable in System Settings > "
"Privacy & Security > [Category]"
)
return _store
NSDate conversion
from Foundation import NSDate
from datetime import datetime
start = datetime.now()
start_ns = NSDate.dateWithTimeIntervalSince1970_(start.timestamp())
date_str = start_ns.description()
Common macOS frameworks
| Framework | PyObjC package | Use case |
|---|
| EventKit | pyobjc-framework-EventKit | Calendar, Reminders |
| Contacts | pyobjc-framework-Contacts | Address Book |
| CoreLocation | pyobjc-framework-CoreLocation | Location services |
| Photos | pyobjc-framework-Photos | Photo library |
| CloudKit | pyobjc-framework-CloudKit | iCloud data |
Testing
uv run python -c "from project_name import main; print('OK')"
npx @modelcontextprotocol/inspector uv run python -m project_name
Key rules
- Always use
uv run --directory instead of hardcoded paths or manual venv activation.
- Never skip permission requests — silent failures when access isn't granted.
- Never block the event loop with synchronous permission requests in async context.
- Foundation import needed for
NSDate and other Foundation classes.
- Use correct entity type:
EKEntityTypeEvent vs EKEntityTypeReminder.
- Permission callbacks need
time.sleep(0.5) to complete.
- Reference implementation: a local single-file FastMCP server project.
- For general MCP server-authoring patterns, see the MCP builder docs.