# SolatCal

> Turns official JAKIM e-Solat prayer times for Malaysia into an iCalendar
> (.ics) feed that any calendar app can subscribe to. 60 zones,
> no account, no key, no rate limit, free. Unofficial: not affiliated with JAKIM.

All times are Asia/Kuala_Lumpur (UTC+08:00, no DST) and come from JAKIM
e-Solat unchanged. SolatCal performs no astronomical calculation of its own.

## Pages

- [English](https://solatcal.umarrasydan.com/): the feed builder.
- [Bahasa Malaysia](https://solatcal.umarrasydan.com/ms/): the same builder in Malay.

## Calendar feed

`GET https://solatcal.umarrasydan.com/api/feed.ics?zone=<ZONE>&...`

Returns `text/calendar`. Subscribe to the URL for a feed that updates itself,
or add `&download=1` for a one-off file. Swap the scheme for `webcal:` to
make Apple Calendar and most desktop clients subscribe directly.

| Parameter | Values | Default | Meaning |
|---|---|---|---|
| `zone` | one of the 60 zone codes below | **required** | Which JAKIM zone |
| `period` | `month`, `quarter`, `year` | `year` | `month` and `quarter` run forward from today; `year` runs to 31 December |
| `prayers` | comma separated, any of `fajr`, `syuruk`, `dhuhr`, `asr`, `maghrib`, `isha` | `fajr,dhuhr,asr,maghrib,isha` | Which prayers become events |
| `align` | `exact`, `early5`, `early10`, `block15` | `exact` | Event start: at the solat time, 5 or 10 minutes before, or snapped down to the containing 15 minute block |
| `duration` | integer 5 to 120 | `15` | Event length in minutes. With `block15` the end also rounds up to the next quarter hour |
| `busy` | `busy`, `free` | `busy` | Sets `TRANSP` to `OPAQUE` or `TRANSPARENT` |
| `alarm` | `none`, `0`, `5`, `10` | `none` | Display reminder, that many minutes before the start |
| `lang` | `en`, `ms` | `en` | Language of the labels inside the calendar. Prayer names are Malay either way |
| `download` | `1` | off | Adds a `Content-Disposition: attachment` header |

Any unrecognised value returns HTTP 400 with a JSON body of the form
`{"error": "...", "status": 400}` naming the offending parameter.

### Examples

Five daily prayers for Kuala Lumpur, on time, 15 minutes:

    https://solatcal.umarrasydan.com/api/feed.ics?zone=WLY01

Selangor (Gombak and Petaling), snapped to 15 minute blocks, 30 minutes,
marked free, 10 minute reminder, Malay labels:

    https://solatcal.umarrasydan.com/api/feed.ics?zone=SGR01&align=block15&duration=30&busy=free&alarm=10&lang=ms

Download the next three months for Johor Bahru including Syuruk:

    https://solatcal.umarrasydan.com/api/feed.ics?zone=JHR02&period=quarter&prayers=fajr,syuruk,dhuhr,asr,maghrib,isha&download=1

Subscribe from Apple Calendar:

    webcal://solatcal.umarrasydan.com/api/feed.ics?zone=WLY01

### Event shape

One `VEVENT` per prayer per day. `SUMMARY` uses the Malay names Subuh,
Syuruk, Zohor, Asar, Maghrib, Isyak; on Fridays Zohor is titled Jumaat.
`UID` is `<zone>-<YYYYMMDD>-<prayer>@<host>` and never changes, so
re-importing updates events instead of duplicating them. `DESCRIPTION`
always carries the exact JAKIM time, the zone, the hijri date and a source
link, even when `align` has moved the event start.

## Other JSON endpoints

- `GET https://solatcal.umarrasydan.com/api/zones` - every zone code mapped to its districts.
- `GET https://solatcal.umarrasydan.com/api/preview?zone=<ZONE>` - today's times for one zone, already
  run through the same `align` and `duration` rules as the feed. Accepts the
  same parameters as the feed.
- `GET https://solatcal.umarrasydan.com/api/health` - service and upstream status.

## MCP server

`POST https://solatcal.umarrasydan.com/mcp` speaks Model Context Protocol over JSON-RPC 2.0. It is
stateless Streamable HTTP: no session id, no SSE required, responses are
`application/json`. `GET /mcp` returns 405.

Tools: `list_zones`, `get_prayer_times`, `get_calendar_feed`.

    curl -sX POST https://solatcal.umarrasydan.com/mcp \
      -H 'content-type: application/json' \
      -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

## Zones

60 zone codes. A few common ones:

- `WLY01` Kuala Lumpur, Putrajaya
- `SGR01` Gombak, Petaling, Sepang, Hulu Langat, Hulu Selangor, S.Alam
- `JHR02` Johor Bahru, Kota Tinggi, Mersing, Kulai
- `PNG01` Seluruh Negeri Pulau Pinang
- `SWK09` Zon Khas (Kampung Patarikan)

The full list is at `https://solatcal.umarrasydan.com/api/zones`. Codes are three letters for the
state (JHR Johor, KDH Kedah, KTN Kelantan, MLK Melaka, NGS Negeri Sembilan,
PHG Pahang, PLS Perlis, PNG Pulau Pinang, PRK Perak, SBH Sabah, SGR Selangor,
SWK Sarawak, TRG Terengganu, WLY Wilayah Persekutuan) plus a two digit number.

## Attribution

Prayer times are the property of JAKIM e-Solat: https://www.e-solat.gov.my/index.php?siteId=24&pageId=24
SolatCal only reformats them, and is not affiliated with or endorsed by JAKIM.
