This service combines multiple iCalendar (ICS) feeds into a single calendar, with optional per‑calendar transforms. It runs as a FastAPI app with the same path‑based authentication scheme used by the MCP servers, and supports Redis caching for source ICS files with configurable refresh TTLs.
Key features
- FastAPI app with optional dual‑factor path auth using
ICS_API_KEYandSALT(with legacyMD5_SALTsupport) - Redis‑backed caching of source ICS feeds
- Per‑calendar refresh TTL with environment overrides
- Optional show/hide filtering via query params
- Dockerfile and example env provided
Environment
- ICS_API_KEY: API key to enable path‑based auth (optional for local/dev)
- SALT: Optional salt used for API path hash (preferred)
- MD5_SALT: Legacy name for
SALT(still supported) - REDIS_HOST, REDIS_SSL_PORT (default 6380), REDIS_KEY: Redis connection
- ICS_CACHE_NAMESPACE: Optional Redis namespace. Set this when sharing one Redis instance across multiple apps. If omitted, a namespace is derived from
ICS_API_KEYwhen available. - ICS_SOURCES: JSON array of calendar configs (see example)
- ICS_NAME: Combined calendar display name
- ICS_DAYS_HISTORY: Days of history to include (int)
- CACHE_TTL_ICS_SOURCE_DEFAULT: Default TTL (seconds) for source ICS caching
- CACHE_TTL_ICS_SOURCE_LKG: Optional last-known-good fallback TTL (seconds). Leave unset or set to 0 to retain indefinitely.
- CACHE_TTL_ICS_SOURCE_FAILURE_BACKOFF: Backoff TTL (seconds) after a source fetch failure (minimum 1)
Calendar source config
Each object in ICS_SOURCES may include the following keys (compatible with calcomb):
- Id (required, int): unique numeric ID per calendar
- Url (required, string): ICS feed URL
- Duration (optional, minutes): override event duration when DTSTART is datetime
- PadStartMinutes (optional, minutes): prepend minutes and extend duration accordingly
- Prefix (optional, string): prefix for SUMMARY
- MakeUnique (optional, bool): force UID uniqueness per calendar
- FilterDuplicates (optional, bool): de‑duplicate events by UID
- RefreshSeconds (optional, int): cache TTL for this calendar’s source ICS
- Set to 0 to bypass the fresh source cache while still using failure backoff and last-known-good fallback cache when Redis is configured.
Cache behavior
- Cache keys are scoped by app namespace so multiple apps can share one Redis instance without overwriting each other's source cache. Use a stable
ICS_CACHE_NAMESPACEper app ifICS_API_KEYis not set or if several apps intentionally share an API key. - Successful source fetches store a last-known-good copy indefinitely by default. A positive
CACHE_TTL_ICS_SOURCE_LKGcan cap that retention. - Removed calendars are not emitted just because their old source data remains in Redis. The app tracks source keys per namespace and prunes cache entries for sources removed from
ICS_SOURCESon the next combine request.
Endpoints
- GET /app/health — health status (no auth)
- GET /app/{ICS_API_KEY}/{hash}/ics?show=1,2&hide=3 — combined calendar
Run locally
- Copy
.env.exampleto.env.localand set values pip install -r requirements.txtpython -m uvicorn src.server:app --host 0.0.0.0 --port 8080
Run tests
pip install -r requirements-dev.txtpython -m pytest
Docker
- Build:
docker build -t ics-combiner . - Run:
docker run --env-file .env.local -p 8080:8080 ics-combiner