{
  "markdown": "# Malaysia Calendar API\n\nMalaysia's most complete calendar API — public holidays, school calendar, exam schedules, and MCP server for AI tools.\n\n[![npm: mycal-core](https://img.shields.io/npm/v/@catlabtech/mycal-core?label=mycal-core)](https://www.npmjs.com/package/@catlabtech/mycal-core)\n[![npm: mycal-sdk](https://img.shields.io/npm/v/@catlabtech/mycal-sdk?label=mycal-sdk)](https://www.npmjs.com/package/@catlabtech/mycal-sdk)\n[![npm: mycal-mcp-server](https://img.shields.io/npm/v/@catlabtech/mycal-mcp-server?label=mycal-mcp-server)](https://www.npmjs.com/package/@catlabtech/mycal-mcp-server)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)\n[![Deploys on Cloudflare](https://img.shields.io/badge/deploys%20on-Cloudflare-F38020?logo=cloudflare&logoColor=white)](https://mycal-web.pages.dev)\n\n**Data source**: Official government gazette (JPM BKPP), JAKIM, KPM, MPM. Not scraped from third-party websites.\n\n## Links\n\n- **Website + demo**: https://mycal-web.pages.dev\n- **Developer docs**: https://mycal-web.pages.dev/docs\n- **API base URL**: https://mycal-api.huijun00100101.workers.dev/v1\n- **GitHub**: https://github.com/Junhui20/malaysia-calendar-api\n\n## Packages\n\nPublished to npm under the `@catlabtech` scope:\n\n| Package | Purpose | Install |\n|---------|---------|---------|\n| [`@catlabtech/mycal-core`](https://www.npmjs.com/package/@catlabtech/mycal-core) | Shared types, Zod schemas, and calendar/business-day logic | `npm install @catlabtech/mycal-core` |\n| [`@catlabtech/mycal-sdk`](https://www.npmjs.com/package/@catlabtech/mycal-sdk) | TypeScript client SDK for the REST API | `npm install @catlabtech/mycal-sdk` |\n| [`@catlabtech/mycal-mcp-server`](https://www.npmjs.com/package/@catlabtech/mycal-mcp-server) | MCP server exposing 13 calendar tools for AI agents | `npm install @catlabtech/mycal-mcp-server` |\n\n## Features\n\n- **49 public holidays** for 2026 from official gazette (Warta Kerajaan) — federal + state-specific\n- **16 states + 3 Federal Territories** with aliases (KL, JB, Penang, etc.)\n- **Weekend-aware** — Kedah/Kelantan/Terengganu use Fri-Sat (Kumpulan A), all others use Sat-Sun (Kumpulan B), with Johor's historical switch tracked\n- **Cuti ganti** (replacement holiday) auto-calculation per state weekend config\n- **Business day calculator** — per-state, holiday-aware\n- **School calendar** — terms, holidays, KPM cuti perayaan (Lampiran A/B/C)\n- **Exam schedules** — SPM, STPM, MUET, PT3\n- **iCal subscription feeds** — per-state `.ics` feeds\n- **MCP Server** for AI agents — 13 tools for Claude, ChatGPT, and other assistants\n- **TypeScript SDK** (`@catlabtech/mycal-sdk`) with typed responses\n- **OpenAPI 3.1 spec** + interactive docs\n- **Trilingual** — Bahasa Melayu, English, Chinese names (三语支持)\n\n## Quick Start\n\n```bash\n# Clone and install\ngit clone https://github.com/Junhui20/malaysia-calendar-api.git\ncd malaysia-calendar-api\npnpm install\n\n# Build shared packages\npnpm --filter @catlabtech/mycal-core build\npnpm --filter @catlabtech/mycal-sdk build\n\n# Run API locally (http://localhost:8787)\ncd packages/api && npx wrangler dev\n\n# Run web site locally (http://localhost:4321)\npnpm --filter @mycal/web dev\n\n# Validate data\npnpm validate\n\n# Run tests\npnpm test\n```\n\n## API Examples\n\nBase URL: `https://mycal-api.huijun00100101.workers.dev/v1`\n\n### List holidays\n\n```bash\n# All holidays for Selangor in 2026\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/holidays?year=2026&state=selangor\"\n\n# Islamic holidays only\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/holidays?year=2026&type=islamic\"\n\n# March holidays for KL\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/holidays?year=2026&state=KL&month=3\"\n```\n\n### Check a date\n\n```bash\n# Is March 21 a holiday/weekend/working day?\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/holidays/check?date=2026-03-21&state=KL\"\n```\n\nResponse:\n```json\n{\n  \"data\": {\n    \"date\": \"2026-03-21\",\n    \"dayOfWeek\": \"Saturday\",\n    \"isHoliday\": true,\n    \"isWeekend\": true,\n    \"isWorkingDay\": false,\n    \"isSchoolDay\": false,\n    \"holidays\": [\n      {\n        \"id\": \"2026-hari-raya-aidilfitri-1\",\n        \"name\": { \"ms\": \"Hari Raya Aidilfitri\", \"en\": \"Eid al-Fitr\", \"zh\": \"开斋节\" },\n        \"type\": \"islamic\",\n        \"status\": \"confirmed\"\n      }\n    ]\n  }\n}\n```\n\n### Business days\n\n```bash\n# Count working days in March for Selangor\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/business-days?start=2026-03-01&end=2026-03-31&state=selangor\"\n\n# Add 10 business days to a date\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/business-days/add?date=2026-03-01&days=10&state=selangor\"\n```\n\n### School calendar\n\n```bash\n# Is this a school day?\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/school/is-school-day?date=2026-03-21&state=selangor\"\n\n# School holidays for Kumpulan B\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/school/holidays?year=2026&group=B\"\n\n# Exam schedule\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/school/exams?year=2026&type=spm\"\n```\n\n### Next holiday\n\n```bash\n# Next holiday for Penang\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/holidays/next?state=penang\"\n```\n\n### State resolution\n\n```bash\n# Resolve alias\ncurl \"https://mycal-api.huijun00100101.workers.dev/v1/states/resolve?q=kl\"\n# -> { \"data\": { \"canonical\": \"kuala-lumpur\", \"group\": \"B\" } }\n```\n\n## Full API Reference\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /v1/holidays` | List holidays (filter by year, state, type, status, month) |\n| `GET /v1/holidays/check` | Is this date a holiday/weekend/working day/school day? |\n| `GET /v1/holidays/today` | Today's holiday status |\n| `GET /v1/holidays/next` | Next upcoming holiday |\n| `GET /v1/holidays/between` | Holidays in date range |\n| `GET /v1/holidays/leave-optimizer` | Suggest annual-leave days to spend for the longest consecutive break |\n| `GET /v1/business-days` | Count business days between dates |\n| `GET /v1/business-days/add` | Add N business days to a date |\n| `GET /v1/states` | All 16 states + 3 FTs with weekend config |\n| `GET /v1/states/resolve` | Resolve alias (KL, penang, jb) to canonical code |\n| `GET /v1/school/terms` | School term dates + day counts |\n| `GET /v1/school/holidays` | School holidays + KPM cuti perayaan |\n| `GET /v1/school/exams` | SPM, STPM, MUET, PT3 schedule |\n| `GET /v1/school/is-school-day` | Is this a school day? |\n| `GET /v1/feed/ical/:state` | iCal subscription feed |\n| `GET /v1/feed/csv/:state` | CSV/spreadsheet export of holidays |\n| `GET /v1/data/*` | Raw, cacheable open-data snapshots — `manifest`, `states`, `holidays/:year`, `all` (no API key required) |\n| `GET /v1/prayer-times/:zone` | JAKIM prayer times for a zone (MYT-formatted); `GET /v1/prayer-times/zones` lists the zone directory |\n| `POST /v1/keys` | Issue an API key (admin-gated for now; `GET /v1/keys/whoami` checks the caller's tier) |\n\nSee the full [OpenAPI 3.1 spec](./openapi.yaml) for request/response schemas.\n\n## SDK Usage\n\n```typescript\nimport { MyCalClient } from \"@catlabtech/mycal-sdk\";\n\nconst cal = new MyCalClient();\n\n// Check if a date is a working day\nconst result = await cal.check(\"2026-03-21\", \"selangor\");\nconsole.log(result.isWorkingDay); // false\n\n// List holidays\nconst holidays = await cal.holidays({ year: 2026, state: \"KL\" });\n\n// Business days\nconst workDays = await cal.businessDays(\"2026-03-01\", \"2026-03-31\", \"selangor\");\nconsole.log(workDays.businessDays); // 22\n\n// School calendar\nconst terms = await cal.school.terms({ year: 2026, group: \"B\" });\nconst exams = await cal.school.exams({ year: 2026, type: \"spm\" });\nconst isSchool = await cal.school.isSchoolDay(\"2026-03-21\", \"selangor\");\n```\n\n## MCP Server\n\nConnect the Malaysia Calendar API to Claude, ChatGPT, or any MCP-compatible AI assistant.\n\n### Setup with Claude Desktop / Claude Code\n\nAdd to your MCP configuration:\n\n```json\n{\n  \"mcpServers\": {\n    \"malaysia-calendar\": {\n      \"command\": \"npx\",\n      \"args\": [\"@catlabtech/mycal-mcp-server\"]\n    }\n  }\n}\n```\n\n### Available Tools (13)\n\n| Tool | Description |\n|------|-------------|\n| `get_malaysia_holidays` | Get public holidays (filter by year, state, type) |\n| `check_malaysia_holiday` | Check if a date is a holiday or working day |\n| `next_malaysia_holiday` | Find the next upcoming holiday |\n| `malaysia_business_days` | Count working days between two dates |\n| `malaysia_long_weekends` | Find long weekends (3+ days) |\n| `malaysia_leave_optimizer` | Find the most efficient leave days to take to maximise consecutive days off |\n| `list_malaysia_states` | List all states with weekend config |\n| `resolve_malaysia_state` | Resolve alias (KL, JB) to canonical code |\n| `malaysia_holiday_changes` | Recent data changes |\n| `malaysia_school_terms` | School term dates and day counts |\n| `malaysia_school_holidays` | School holidays (cuti penggal, cuti perayaan) |\n| `malaysia_exams` | SPM, STPM, MUET, PT3 exam schedule |\n| `malaysia_is_school_day` | Check if a date is a school day |\n\n## State Codes\n\n| Code | Aliases | Group | Weekend |\n|------|---------|-------|---------|\n| `johor` | jhr, jb | B | Sat-Sun (was Fri-Sat 2014-2024) |\n| `kedah` | kd, kdh | A | Fri-Sat |\n| `kelantan` | kel, kb | A | Fri-Sat |\n| `terengganu` | trg, kt | A | Fri-Sat |\n| `perak` | prk, ipoh | B | Sat-Sun |\n| `pulau-pinang` | penang, pg | B | Sat-Sun |\n| `selangor` | sel, sgr | B | Sat-Sun |\n| `negeri-sembilan` | ns, n9 | B | Sat-Sun |\n| `melaka` | mlk, malacca | B | Sat-Sun |\n| `pahang` | phg, kuantan | B | Sat-Sun |\n| `perlis` | pls, kangar | B | Sat-Sun |\n| `sabah` | sbh, kk | B | Sat-Sun |\n| `sarawak` | swk, kuching | B | Sat-Sun |\n| `kuala-lumpur` | kl | B | Sat-Sun |\n| `wp-putrajaya` | putrajaya, pjy | B | Sat-Sun |\n| `wp-labuan` | labuan, lbn | B | Sat-Sun |\n\nState aliases are case-insensitive. Use `GET /v1/states/resolve?q=kl` to resolve any alias to the canonical code.\n\n## Project Structure\n\n```\nmalaysia-calendar-api/\n├── data/                        # JSON data files (source of truth / 数据源)\n│   ├── holidays/\n│   │   ├── 2024.json            # Holiday data per year\n│   │   ├── 2025.json\n│   │   └── 2026.json\n│   ├── school/\n│   │   ├── terms-2026.json      # School terms (Kumpulan A + B)\n│   │   ├── holidays-2026.json   # School holidays + KPM cuti perayaan\n│   │   └── exams-2026.json      # SPM, STPM, MUET, PT3 schedules\n│   ├── states.json              # 16 states + 3 FT, aliases, weekend history\n│   └── known-fixed-holidays.json\n├── packages/\n│   ├── core/                    # Shared business logic (types, schemas, utils)\n│   │   └── src/\n│   │       ├── types.ts         # Holiday, State, SchoolTerm, Exam interfaces\n│   │       ├── schemas.ts       # Zod validation schemas\n│   │       ├── filter.ts        # Query filtering logic\n│   │       ├── replacement.ts   # Cuti ganti calculation\n│   │       ├── state-resolver.ts\n│   │       ├── business-days.ts\n│   │       └── school.ts        # School term/holiday/exam logic\n│   ├── api/                     # Hono API on Cloudflare Workers\n│   ├── mcp-server/              # MCP Server (13 tools)\n│   ├── sdk/                     # TypeScript client SDK (@catlabtech/mycal-sdk)\n│   └── web/                     # Astro + Starlight — marketing site, demos, docs\n├── scripts/\n│   ├── validate-data.ts         # 5-layer data validation pipeline\n│   └── sync-to-kv.ts           # JSON -> Cloudflare KV denormalization\n├── openapi.yaml                 # OpenAPI 3.1 spec (spec-first)\n├── pnpm-workspace.yaml\n└── turbo.json\n```\n\n## Data Sources\n\nAll data is sourced from official Malaysian government publications:\n\n| Source | Data | URL |\n|--------|------|-----|\n| **JPM BKPP** | Federal Gazette / Warta Kerajaan (public holidays) | kabinet.gov.my |\n| **JAKIM** | Takwim Hijri-Miladi (Islamic calendar) | e-solat.gov.my |\n| **KPM** | Kalendar Akademik / school calendar (Lampiran A/B/C) | moe.gov.my |\n| **MPM** | STPM & MUET exam schedules | mpm.edu.my |\n| **State Portals** | State-specific holidays (16 states) | *.gov.my |\n\nHoliday data includes gazette references (e.g., `P.U.(B) 305/2025`) for traceability.\n\n## Deploy\n\nTwo parts deploy independently:\n\n### API → Cloudflare Workers\n\n```bash\npnpm --filter @catlabtech/mycal-core build\ncd packages/api && npx wrangler deploy\n```\n\n### Website → Cloudflare Pages\n\n```bash\npnpm --filter @catlabtech/mycal-core build\npnpm --filter @catlabtech/mycal-sdk build\npnpm --filter @mycal/web build\n\n# Direct upload via wrangler (requires CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID)\ncd packages/web\nnpx wrangler pages deploy dist --project-name=mycal-web\n```\n\nFirst-time Pages setup:\n1. Create a Pages project named `mycal-web` in the Cloudflare dashboard.\n2. Either connect the GitHub repo for automatic builds, or rely on the GitHub Actions workflow (`.github/workflows/deploy.yml`) to push via `wrangler pages deploy`.\n3. Required GitHub secrets: `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`.\n\n### CI/CD\n\nGitHub Actions handles:\n1. **PR gate** — Zod schema validation + cross-source checks on every PR\n2. **Deploy** — On merge to `main`: build and deploy API to Workers + Web to Pages in parallel\n3. **Daily scrape** — Government portal monitoring for updates\n4. **Rukyah monitor** — Islamic date confirmation tracking\n\n## Contributing\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md) for how to:\n- Report a missing holiday or cuti peristiwa\n- Fix data errors\n- Add new features\n\n## License\n\nMIT\n",
  "bytes": 13402,
  "sha": "0a9424144396b95241d0092f1425d590e64001be060a7cda00a1aa7fac4c152a",
  "repo_slug": "junhui20/malaysia-calendar-api",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_junhui20_mycal_0c528ab8/readme"
}