A self-hosted calendar aggregator that consumes multiple ICS feeds, expands recurring events, and exposes both a web dashboard and a JSON API. Includes an MCP (Model Context Protocol) server so AI assistants like Claude can query your calendar with natural language.
- Multi-feed ICS aggregation — Subscribe to any number of ICS calendar feeds (Google Calendar, Outlook/Exchange, iCloud, etc.)
- Recurring event expansion — RRULE, RDATE, and EXDATE are fully expanded using ical4j
- Web dashboard — Agenda view with feed color-coding, tentative event indicators, hide/unhide, and per-event notes
- JSON API — Full REST API for querying events, feeds, free time, and office locations
- MCP server — 7 tools for AI assistant integration (get events, find free time, manage office location, etc.)
- Event hiding — Hide events from the API and MCP server directly in the dashboard. Useful for shared calendars where you're subscribed to a family member's schedule — you can see their events for awareness but hide them so they don't clutter your free time calculations or AI assistant responses
- Per-event notes — Add notes to any event from the dashboard, visible in the API and to AI assistants
- Free time finder — Computes available time slots accounting for all calendars (respects hidden events)
- Office location tracking — Configurable office locations with day-of-week defaults and per-day overrides
- Timezone support — Events stored in UTC, displayed in a configurable timezone
- Java 21, Maven
- Javalin — HTTP framework
- JTE — Template engine
- MariaDB / MySQL — Database
- ical4j — ICS parsing and RRULE expansion
- HikariCP — Connection pooling
- Flyway — Database migrations
- Gson — JSON serialization
- Java 21+
- MariaDB or MySQL
- Maven (for building)
-
Create the database:
CREATE DATABASE eventstream CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'eventstream'@'%' IDENTIFIED BY 'your-password'; GRANT ALL PRIVILEGES ON eventstream.* TO 'eventstream'@'%';
-
Configure:
cp application.properties.example application.properties # Edit application.properties with your database credentials -
Build and run:
mvn clean package java -jar target/eventstream-1.0-SNAPSHOT.jar
The app starts on port 8080. Database tables are created automatically by Flyway on first run.
-
Add a calendar feed: Open
http://localhost:8080/feedsand add an ICS feed URL.
All settings can be configured in application.properties, overridden by an external file in the working directory, or set via environment variables.
| Property | Env Var | Default | Description |
|---|---|---|---|
db.url |
DB_URL |
— | JDBC connection string |
db.user |
DB_USER |
— | Database username |
db.password |
DB_PASSWORD |
— | Database password |
server.port |
SERVER_PORT |
8080 |
HTTP port |
display.timezone |
DISPLAY_TIMEZONE |
System default | Display timezone |
sync.interval.minutes |
SYNC_INTERVAL_MINUTES |
60 |
Sync interval |
sync.expansion.months |
SYNC_EXPANSION_MONTHS |
6 |
RRULE expansion window |
prune.retention.days |
PRUNE_RETENTION_DAYS |
90 |
Delete events older than N days |
Configure office locations and day-of-week defaults:
# Available locations
office.locations=HQ,Branch Office,Home
# Default when no day-specific default matches
office.default=Home
# Day-of-week defaults
office.default.MONDAY=HQ
office.default.TUESDAY=HQ
office.default.WEDNESDAY=Branch Office
# Aliases (case-insensitive, comma-separated)
office.aliases.HQ=Headquarters,Main Office
office.aliases.Home=WFH,RemoteSee API.md for the full API reference.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/events |
Query events with filters |
| GET | /api/v1/events/today |
Today's events |
| GET | /api/v1/events/upcoming?days=7 |
Next N days |
| GET | /api/v1/events/summary?days=7 |
Plain text agenda |
| GET | /api/v1/events/free-time?date=2026-03-16 |
Find free time slots |
| GET | /api/v1/office?date=2026-03-16&days=5 |
Office location schedule |
| GET | /api/v1/health |
Health check |
All event endpoints support Accept: text/plain for LLM-friendly output.
EventStream includes an MCP server for AI assistant integration. The server exposes 7 tools:
get_events— Query events by date range, calendar, or search termget_today— Today's eventsget_upcoming— Events for the next N daysfind_free_time— Available time slots on a datelist_calendars— Calendar feeds and sync statusget_office_location— Where you're working fromset_office_location— Set office for a date
Install mcp-remote and add to your Claude Desktop config:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"calendar": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8080/mcp/sse"
]
}
}
}If connecting to a remote server over HTTP (non-localhost), add "--allow-http" to the args.
Restart Claude Desktop after saving. The calendar tools will appear in the tools list.
- "What's on my calendar today?"
- "When am I free for 90 minutes on Friday?"
- "Do I have anything with [person] this week?"
- "Where am I working from on Monday?"
- "I'll be downtown on Wednesday"
[Unit]
Description=EventStream Calendar Aggregator
After=network.target
[Service]
Type=simple
User=eventstream
WorkingDirectory=/opt/eventstream
ExecStart=/usr/bin/java -jar /opt/eventstream/eventstream.jar
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.targetcalendar.example.com {
reverse_proxy localhost:8080
}
When deploying, copy the JTE templates alongside the jar:
/opt/eventstream/
eventstream.jar
application.properties
jte/
layout/main.jte
dashboard/index.jte
feeds/index.jte
MIT — see LICENSE.