How to sync Outlook ↔ Gmail calendars using AI + MCP (Amazon Quick) This article provides a step-by-step guide for syncing Outlook and Gmail calendars using Amazon Quick, an AI assistant, and a custom MCP (Model Context Protocol) server. The system uses a Python script running on a Mac to bridge Amazon Quick with the Google Calendar API via OAuth 2.0, enabling automated two-way sync with smart rules that filter which events are shared. The setup is free to use, leveraging Google Calendar's generous free tier, and requires creating a Google Cloud project, enabling the Calendar API, and configuring OAuth credentials. How I Synced My Work Calendar Outlook with My Personal Calendar Gmail Using AI + MCP The Problem Two calendars. Work life in Outlook, personal life in Gmail. Neither knows the other exists. Real scenarios that kept happening: - I'd block "Business Travel" on my work calendar 3 days in NYC but my husband had no idea I'd be gone until I told him - I'd schedule a doctor appointment for my kiddo on Gmail, then forget to block my work calendar. colleagues would book meetings over it - Weekend-only family trips didn't need a work block, but a Friday-through-Sunday trip did. I had to think about this manually every time What I wanted: 1. When I add work travel to Outlook → automatically show it on our family Gmail calendar and notify my husband 2. When I add a doctor appointment on Gmail → automatically block my work calendar as Out of Office 3. Smart rules: don't sync routine daily blocks, skip weekend-only trips, only sync what matters What I Built A daily sync agent that runs at 9 AM and keeps both calendars in sync, using: - Amazon Quick AI assistant with calendar access - A lightweight MCP server Python script on my Mac that bridges to Google Calendar via OAuth - A scheduled agent with custom sync rules Architecture Outlook Work Calendar ↕ Amazon Quick reads/writes via built-in Outlook connector Amazon Quick AI Agent ↕ Calls MCP tools over stdio Google Calendar MCP Server Python on my Mac ↕ Google Calendar REST API OAuth 2.0 Gmail Calendar Personal Prerequisites - Amazon Quick with Outlook calendar connected - A Google account with Google Calendar - Python 3.10+ on your machine - pip install google-auth google-auth-oauthlib google-api-python-client Step 1: Create a Google Cloud Project + OAuth Credentials 1. Go to console.cloud.google.com https://console.cloud.google.com/ 2. Create a new project e.g., "Calendar Sync" 3. Enable the Google Calendar API APIs & Services → Library → search "Google Calendar API" → Enable 4. Configure the OAuth consent screen: - Select External - App name: "Calendar Sync" - Add your email as a test user 5. Create OAuth credentials: - APIs & Services → Credentials → Create Credentials → OAuth client ID - Application type: Desktop app - Download the JSON file 6. Save it to ~/.config/caldav/client secret.json : bash mkdir -p ~/.config/caldav mv ~/Downloads/client secret .json ~/.config/caldav/client secret.json Cost: Free. Google Calendar API has a massive free tier 1M queries/day . Personal sync uses ~10 calls/day. Step 2: Set Up the MCP Server Save the following Python script to ~/mcp-servers/gcal mcp server.py : python /usr/bin/env python3 """ Google Calendar MCP Server OAuth 2.0 Bridges AI assistants to Gmail Calendar via Google Calendar REST API. """ import json import sys from datetime import datetime, timedelta from pathlib import Path from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google auth oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build CONFIG DIR = Path.home / ".config" / "caldav" CLIENT SECRET PATH = CONFIG DIR / "client secret.json" TOKEN PATH = CONFIG DIR / "token.json" SCOPES = "https://www.googleapis.com/auth/calendar" def get calendar service : creds = None if TOKEN PATH.exists : creds = Credentials.from authorized user file str TOKEN PATH , SCOPES if not creds or not creds.valid: if creds and creds.expired and creds.refresh token: creds.refresh Request else: flow = InstalledAppFlow.from client secrets file str CLIENT SECRET PATH , SCOPES creds = flow.run local server port=0 with open TOKEN PATH, "w" as f: f.write creds.to json return build "calendar", "v3", credentials=creds def list events start date, end date : service = get calendar service events = service.events .list calendarId="primary", timeMin=f"{start date}T00:00:00Z", timeMax=f"{end date}T23:59:59Z", singleEvents=True, orderBy="startTime", .execute .get "items", return {"summary": e.get "summary", "" , "start": e "start" .get "dateTime", e "start" .get "date" , "end": e "end" .get "dateTime", e "end" .get "date" , "id": e.get "id" , "all day": "date" in e "start" } for e in events def create event summary, start date, end date, description="", all day=True, attendees=None : service = get calendar service body = {"summary": summary, "description": description, "status": "confirmed"} if all day: body "start" = {"date": start date} body "end" = {"date": end date} else: body "start" = {"dateTime": f"{start date}T09:00:00", "timeZone": "America/Los Angeles"} body "end" = {"dateTime": f"{end date}T17:00:00", "timeZone": "America/Los Angeles"} if attendees: body "attendees" = {"email": email} for email in attendees event = service.events .insert calendarId="primary", body=body .execute return {"success": True, "id": event.get "id" , "link": event.get "htmlLink" } def delete event event id : service = get calendar service service.events .delete calendarId="primary", eventId=event id .execute return {"success": True} --- MCP JSON-RPC Server reads from stdin, writes to stdout --- TOOLS = { "caldav list events": {"description": "List Gmail calendar events in a date range.", "parameters": {"type": "object", "properties": { "start date": {"type": "string"}, "end date": {"type": "string"}}, "required": "start date", "end date" }}, "caldav create event": {"description": "Create event on Gmail calendar. end date is EXCLUSIVE for all-day events.", "parameters": {"type": "object", "properties": { "summary": {"type": "string"}, "start date": {"type": "string"}, "end date": {"type": "string"}, "description": {"type": "string", "default": ""}, "all day": {"type": "boolean", "default": True}, "attendees": {"type": "array", "items": {"type": "string"}, "default": None}}, "required": "summary", "start date", "end date" }}, "caldav delete event": {"description": "Delete event from Gmail calendar by ID.", "parameters": {"type": "object", "properties": {"event id": {"type": "string"}}, "required": "event id" }}, } def handle request request : method, params, req id = request.get "method" , request.get "params", {} , request.get "id" if method == "initialize": return {"jsonrpc": "2.0", "id": req id, "result": {"protocolVersion": "2024-11-05", "capabilities": {"tools": {}}, "serverInfo": {"name": "gcal-mcp", "version": "2.0.0"}}} elif method == "notifications/initialized": return None elif method == "tools/list": return {"jsonrpc": "2.0", "id": req id, "result": {"tools": {"name": n, "description": s "description" , "inputSchema": s "parameters" } for n, s in TOOLS.items }} elif method == "tools/call": name, args = params.get "name" , params.get "arguments", {} try: result = {"caldav list events": list events, "caldav create event": create event, "caldav delete event": delete event} name args return {"jsonrpc": "2.0", "id": req id, "result": {"content": {"type": "text", "text": json.dumps result, default=str } }} except Exception as e: return {"jsonrpc": "2.0", "id": req id, "result": {"content": {"type": "text", "text": json.dumps {"error": str e } } , "isError": True}} return {"jsonrpc": "2.0", "id": req id, "error": {"code": -32601, "message": f"Unknown: {method}"}} if name == " main ": for line in sys.stdin: if not line.strip : continue try: resp = handle request json.loads line if resp: sys.stdout.write json.dumps resp + "\n" sys.stdout.flush except json.JSONDecodeError: continue Step 3: Authorize One Time Run this once to complete the OAuth flow: bash python3 -c " from google auth oauthlib.flow import InstalledAppFlow from pathlib import Path flow = InstalledAppFlow.from client secrets file str Path.home / '.config/caldav/client secret.json' , 'https://www.googleapis.com/auth/calendar' creds = flow.run local server port=8080 token path = Path.home / '.config/caldav/token.json' with open token path, 'w' as f: f.write creds.to json print 'Token saved ' " A browser window will open. Sign in, click "Advanced" → "Go to Calendar Sync unsafe " → "Continue". Token is saved. You won't need to do this again. Step 4: Register in Amazon Quick Settings → Capabilities → MCP Servers → Add: - Name: calendar integ or any name - Command: python3 - Args: /Users/yourname/mcp-servers/gcal mcp server.py Toggle on. Should show "Connected." Step 5: Create the Scheduled Sync Agent In Amazon Quick, create a scheduled agent with: - Schedule: Daily at 9:00 AM - Tool access: Outlook read + create/delete with approval , Gmail MCP full read/write My Sync Rules Outlook → Gmail work travel → family calendar : | Trigger | Action | |---------|--------| | Title contains "Aarthi: Business Travel" | Sync + invite husband | | Title contains "Aarthi: Team event" | Sync + invite husband | | Category = "Sync to Gmail" | Sync + invite husband | | Event spans 2+ days | Sync + invite husband | Gmail → Outlook personal → work calendar : | Trigger | Action | |---------|--------| | Title contains "Kiddo doc" or "Kiddo doc appt" | Block as OOF | | Event spans 2+ days with a weekday | Block as OOF | Never sync: - Recurring daily blocks drop-off, pickup, commute . these live natively on both calendars - Weekend-only events Sat-Sun trips - Other people's OOO notifications - Workouts, birthdays, routine items Deletion sync: If a source event is deleted, the synced copy is removed too. Gotchas & Lessons Learned 1. Google CalDAV requires OAuth 2.0. App passwords don't work for CalDAV/Calendar API. Don't waste time trying. 2. Timezone matters. Always include -07:00 or your offset when creating timed events on Outlook. Bare datetimes get interpreted as UTC. 3. Recurring events don't belong in sync. If an event repeats daily/weekly, create it natively on both calendars. Syncing individual occurrences creates approval fatigue. 4. Deduplication is essential. Without it, the agent creates duplicate events on every run. 5. Outlook's create/delete tools require user approval. You can't fully automate writes to Outlook. but it's just one tap per event. 6. end date is EXCLUSIVE in Google Calendar. A trip ending June 20 needs end date = 2026-06-21 . What It Looks Like in Practice - I add "Aarthi: Business Travel — NYC Summit" to Outlook → my husband gets a Google Calendar invite automatically - I add "Kiddo doc visit" to Gmail → my Outlook shows OOF for that time - I delete the doctor appointment from Gmail → the OOF block disappears from Outlook - Weekend family trips don't clutter my work calendar - I do nothing except add/remove events where I normally would. the sync just happens. License MIT. do whatever you want with this.