A Model Context Protocol (MCP) server for iCalendar feed management and event queries. This server enables Claude Desktop and other MCP clients to interact with multiple calendar feeds for event tracking, conflict detection, and schedule management.
- Multi-Calendar Support: Monitor multiple iCalendar feeds (.ics URLs)
- Smart Event Queries: Search events by date, range, or keywords
- Conflict Detection: Advanced conflict analysis with severity levels
- Dynamic Feed Management: Add/remove calendar feeds on the fly
- Automatic Refresh: Configurable refresh intervals for feed updates
- Timezone Support: Proper timezone normalization and handling
- Redis Caching: Optional caching for improved performance
- HTTP API: Remote access via HTTP with dual-factor authentication
For production HTTP access:
# 1. Configure environment
cp .env.example .env.local
# Edit .env.local: set ICAL_FEED_CONFIGS and MCP_API_KEY
# 2. Run with Docker Compose
docker-compose up --build
# 3. Calculate your endpoint URLs
python scripts/verify_auth.py --api-key YOUR_KEY --domain localhost:8080 --no-https
# 4. Access endpoints
# Health (public): http://localhost:8080/app/health
# MCP: http://localhost:8080/app/{API_KEY}/{API_KEY_HASH}/mcpRequired:
ICAL_FEED_CONFIGS- JSON array of calendar feeds
Optional:
TIMEZONE- IANA timezone name (default:UTC)REFRESH_INTERVAL- Minutes between refreshes (default:60)DEBUG- Enable debug logging (default:false)
HTTP Mode:
MCP_API_KEY- API key for authentication (strongly recommended)MD5_SALT- Salt for MD5 hash (optional, recommended for enhanced security)HOST- Bind address (default:0.0.0.0)PORT- Listen port (default:80)
Redis Cache (Optional):
REDIS_HOST- Redis hostnameREDIS_SSL_PORT- Redis SSL port (default:6380)REDIS_KEY- Redis access key
[
{
"name": "Work",
"url": "https://calendar.google.com/calendar/ical/YOUR_ID/basic.ics"
},
{
"name": "Personal",
"url": "https://outlook.office365.com/owa/calendar/YOUR_ID/calendar.ics"
}
]Google Calendar:
- Open Google Calendar settings
- Select the calendar → "Integrate calendar"
- Copy "Secret address in iCal format"
Outlook Calendar:
- Calendar settings → "Shared calendars"
- Publish a calendar
- Copy the ICS link
Apple Calendar:
- Right-click calendar → "Share Calendar"
- Make public and copy URL (change
webcal://tohttps://)
docker-compose up --buildEntry point: server_remote.py
Port: 8080
Authentication: Dual-factor path (API key + MD5 hash)
The server uses dual-factor path authentication requiring both the API key and its MD5 hash:
https://your-domain.com/app/{API_KEY}/{API_KEY_HASH}/mcp
The hash is calculated as MD5(MD5_SALT + API_KEY) when salt is configured, or MD5(API_KEY) in legacy mode.
Python script (recommended):
python scripts/verify_auth.py --api-key YOUR_KEY --domain your-domain.comBash script:
scripts/get_mcp_url.sh YOUR_KEY your-domain.comManual calculation:
import hashlib
api_key = "your-api-key"
md5_salt = "your-salt" # Optional
hash_input = f"{md5_salt}{api_key}" if md5_salt else api_key
api_key_hash = hashlib.sha256(hash_input.encode()).hexdigest()
print(f"https://domain.com/app/{api_key}/{api_key_hash}/mcp")Anti-Brute-Force Protection:
- Invalid authentication paths trigger a 30-second delay before returning 404
- This makes brute-force attacks impractical (e.g., testing 1000 keys would take 8+ hours)
- Health check endpoint (
/app/health) is exempt from delays - Failed attempts are logged with source IP for monitoring
Best Practices:
- Use strong API keys (32+ bytes, generated with
openssl rand -base64 32) - Configure
MD5_SALTfor additional security layer - Monitor logs for repeated failed authentication attempts
- Consider IP-based rate limiting at your reverse proxy
- Always use HTTPS in production to prevent credential interception
- Health (
/health): Health check (public, no auth required) - MCP (
/app/{KEY}/{HASH}/mcp): MCP protocol endpoint (authenticated)
- Two-Factor Protection: Requires both API key and hash
- Hash Not Stored: Hash calculated at runtime
- Path Obfuscation: Even with one factor, endpoint is inaccessible
- No Token Exchange: Stateless authentication
get_events_today- Get all events for todayget_tomorrow_events- Get tomorrow's eventsget_week_events- Get this week's eventsget_month_events- Get this month's eventsget_upcoming_events- Get upcoming events (next 7 days)get_events_on_date- Get events for a specific dateget_events_between_dates- Get events within a date rangeget_events_after_date- Get events after a specific datesearch_calendar_events- Search events by keyword
get_calendar_conflicts- Detect scheduling conflicts across calendars
refresh_calendar_feeds- Manually refresh all feedsget_calendar_info- Get information about configured feedsget_calendar_feeds- List all configured feeds
Note: Calendar feeds are configured via the
ICAL_FEED_CONFIGSenvironment variable. To add or remove feeds, update the configuration and restart the server.
get_current_datetime- Get current date/time in configured timezoneget_server_status- Check server healthget_server_config- View server configuration
get_cache_stats- View cache performance metricsget_cache_info- View Redis server informationclear_cache- Clear cached datareset_cache_stats- Reset cache statistics
1. Generate Strong API Keys:
openssl rand -base64 32 # Minimum 32 bytes recommended2. Use HTTPS in Production:
- Configure TLS/SSL certificates
- Enable SSL redirect in ingress
- Use cert-manager for automatic renewal
3. Secure Storage:
- ✅ Store in environment variables or Docker secrets
- ✅ Use external secret management (Vault, AWS Secrets Manager)
- ✅ Encrypt secrets at rest
- ❌ Never commit to version control
- ❌ Never log API keys
4. Network Security:
- Use firewall rules to restrict access
- Consider reverse proxy with additional authentication
- Enable HTTPS with valid SSL certificates
5. Rate Limiting:
- Configure rate limiting at reverse proxy level
- Use tools like nginx rate limiting or fail2ban
The server automatically adds security headers:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: no-referrerCache-Control: no-store, no-cache, must-revalidate, private
Access logs are disabled to prevent API key leakage.
If API key is compromised:
# 1. Generate new key and salt
NEW_KEY=$(openssl rand -base64 32)
NEW_SALT=$(openssl rand -base64 32)
# 2. Update .env.local file
echo "MCP_API_KEY=$NEW_KEY" >> .env.local
echo "MD5_SALT=$NEW_SALT" >> .env.local
# 3. Restart the Docker container
docker-compose down
docker-compose up -d
# 4. Get new endpoint URLs
python scripts/verify_auth.py --api-key "$NEW_KEY" --md5-salt "$NEW_SALT" --domain your-domain.com
# 5. Update MCP clients with new URLs- Strong MCP_API_KEY generated (32+ bytes)
- MD5_SALT configured for enhanced security
- HTTPS/TLS configured with valid certificates
- SSL redirect enabled at reverse proxy
- Certificate auto-renewal configured
- Firewall rules configured
- Rate limiting configured at reverse proxy
- Environment variables secured
- Monitoring and alerting configured
- Key rotation schedule established
- Backup and recovery tested
- Security scanning enabled
- Access logs reviewed regularly
"What's the status of my calendar server?" "What events do I have today?" "Show me my schedule for next week" "Do I have any scheduling conflicts this month?"
# Calculate endpoint URLs (with salt for enhanced security)
python scripts/verify_auth.py --api-key "$MCP_API_KEY" --md5-salt "$MD5_SALT" --domain localhost:8080 --no-https
# Or using environment variables
export MCP_API_KEY="your-api-key"
export MD5_SALT="your-salt"
python scripts/verify_auth.py --domain localhost:8080 --no-https
# Test health check (public endpoint)
curl http://localhost:8080/app/healthEnsure ICAL_FEED_CONFIGS is set correctly with valid JSON.
- Check feed URL is publicly accessible
- Verify URL returns valid iCalendar data
- Try manual refresh:
refresh_calendar_feedstool - Check
REFRESH_INTERVALsetting
- Ensure calendar feeds include timezone information
- Server normalizes timezones to UTC
- Set
TIMEZONEenvironment variable
- Validate JSON format in
ICAL_FEED_CONFIGS - Use single quotes around JSON in environment variables
- Escape double quotes properly
# Check container logs
docker logs calendar-mcp
# Common causes:
# - Missing ICAL_FEED_CONFIGS
# - Invalid JSON in ICAL_FEED_CONFIGS
# - Missing MCP_API_KEY (HTTP mode)MCP Client
↓ (HTTP)
Docker Container
↓
MCP Server (FastMCP)
└─ server_remote.py (HTTP API)
↓
Calendar Service (services/ical.py)
├─ Feed Parser (icalendar)
├─ Recurring Events (recurring-ical-events)
└─ Auto-Refresh Timer
↓
Optional Redis Cache (services/cache.py)
↓
iCalendar Feed URLs
- Automatic Caching: Parsed events cached to reduce processing
- Background Refresh: Feeds refresh without blocking
- Redis Support: Optional Redis for multi-instance deployments
- Efficient Parsing: Only parses when feeds change
See AGENTS.md for detailed development documentation including:
- Architecture details
- Service patterns
- Testing guide
- Adding new features
- Common gotchas
This project is provided as-is for personal use.
- Issues: Open a GitHub issue
- Security: Report vulnerabilities privately
- iCalendar Spec: https://icalendar.org/