import requests
from datetime import date,datetime
from fastmcp import FastMCP
mcp = FastMCP("hebcal")
def get_hebcal_info(city_id: int = 293397):
url = "https://www.hebcal.com/hebcal"
params = {
"v": 1,
"cfg": "json",
"maj": "on",
"min": "on",
"mod": "on",
"nx": "on",
"year": "now",
"month": "x",
"ss": "on",
"mf": "on",
"c": "on",
"geo": "geoname",
"geonameid": city_id,
"M": "on",
"s": "on"
}
response = requests.get(url, params=params)
if response.status_code != 200:
return f"Error: ({response.status_code}): {response.text}"
return response
def parse_date(date_str):
if "T" in date_str:
return datetime.strptime(date_str.split("+")[0], "%Y-%m-%dT%H:%M:%S").date()
else:
return datetime.strptime(date_str, "%Y-%m-%d").date()
CITY_GEO_IDS = {
"jerusalem": 281184,
"tel aviv": 293397,
"haifa": 294801,
"beer sheva": 295530,
"ashdod": 295629,
"eilat": 295277,
}
@mcp.tool()
def get_shabbat_times(city: str):
"""
Returns candle lighting and Havdalah times for the upcoming Shabbat in an Israeli city.
Args:
city (str): City name in English (jerusalem, tel aviv, haifa, beer sheva, ashdod, eilat)
Returns:
list[str]: A list with two items:
[candle lighting time, Havdalah time]
For example:
["2025-11-14 Candle lighting: 16:22", "2025-11-15 Havdalah: 17:34"]
or an error message if the city is not supported.
"""
city_lower = city.lower()
if city_lower not in CITY_GEO_IDS:
return [f"City '{city}' not supported. Choose from: {', '.join(CITY_GEO_IDS.keys())}"]
city_id = CITY_GEO_IDS[city_lower]
resoponse = get_hebcal_info(city_id)
data = resoponse.json().get("items", [])
events = []
for item in data:
if parse_date(item["date"]) >= date.today():
if (item["category"] == "candles" and item["memo"].startswith("Parashat")) or item["category"] == "havdalah":
events.append(f"{parse_date(item["date"])} {item["title"]}")
if len(events) == 2:
return events
@mcp.tool()
def get_all_shabbat_times(city: str):
"""
Returns all candle lighting and Havdalah times until the end of the current year.
Args:
city (str): City name in English (jerusalem, tel aviv, haifa, beer sheva, ashdod, eilat)
Returns:
list[str]: A list of all Shabbat candle lighting and Havdalah times from today until the end of the year.
For example:
["2025-11-14 Candle lighting: 16:22", "2025-11-15 Havdalah: 17:34", ...]
or an error message if the city is not supported.
"""
city_lower = city.lower()
if city_lower not in CITY_GEO_IDS:
return [f"City '{city}' not supported. Choose from: {', '.join(CITY_GEO_IDS.keys())}"]
city_id = CITY_GEO_IDS[city_lower]
resoponse = get_hebcal_info(city_id)
data = resoponse.json().get("items", [])
events = []
for item in data:
if parse_date(item["date"]) >= date.today():
if (item["category"] == "candles" and item["memo"].startswith("Parashat")) or item["category"] == "havdalah":
events.append(f"{parse_date(item["date"])} {item["title"]}")
return events
@mcp.tool()
def get_the_next_holiday():
"""
Returns the next upcoming Jewish holiday according to the Hebrew calendar.
Returns:
list[str]: A list with only one holiday (the next one),
For example:
["2025-12-25 Hanukkah – 1st Candle"]
"""
resoponse = get_hebcal_info()
data = resoponse.json().get("items", [])
events = []
for item in data:
if parse_date(item["date"]) >= date.today():
if item["category"] == "holiday":
events.append(f"{parse_date(item["date"])} {item["title"]}")
if len(events) == 1:
return events
@mcp.tool()
def get_all_holiday_times():
"""
Returns all Jewish holiday dates from today until the end of the year.
Returns:
list[str]: A list of all upcoming holidays according to the Hebrew calendar,
For example:
["2025-12-25 Hanukkah – 1st Candle", "2026-03-24 Purim", ...]
"""
resoponse = get_hebcal_info()
data = resoponse.json().get("items", [])
events = []
for item in data:
if parse_date(item["date"]) >= date.today():
if item["category"] == "holiday":
events.append(f"{parse_date(item["date"])} {item["title"]}")
return events
if __name__ == "__main__":
mcp.run(transport='stdio')