Astro MCP
README.md
# Astro MCP
A Vedic (sidereal) astrology engine exposed as an [MCP](https://modelcontextprotocol.io) server, built so an LLM such as Claude can answer questions like "what dasha am I running?" or "how is Saturn transiting for me?". Answers come from real calculations, returned in a compact format that uses few tokens.
- **Calculations** are done with [PyJHora](https://github.com/naturalstupid/PyJHora) and the Swiss Ephemeris, behind a small backend-agnostic API (`engine.py`).
- **Validated against JHora desktop:**
- Planets and nodes agree within 0.03″ and the Lagna within 0.1″, on a public chart (1926, Los Angeles) and a private reference chart.
- Vimshottari boundaries agree within ~40 s on the private chart.
- **Defaults:** True Chitra ayanamsa (what JHora calls "Lahiri"), mean nodes, whole-sign houses, and solar-return dasha years.
## MCP tools
| Tool | What it returns |
|---|---|
| `create_chart` | a `chart_id` for the other tools, the resolved birth details (with any caveats to relay), and D1 positions |
| `now_snapshot` | the running maha > antar > pratyantar dasha with end dates and the next period; transits with houses counted from both Lagna and Moon, plus retrograde station dates |
| `get_dasha` | Vimshottari periods at depth 1–3, windowed by default |
| `get_planets` | natal positions for the requested bodies |
| `get_transits` | transits on a given date, with nakshatra and houses counted from both Lagna and Moon |
The full tool schema set is about 600 tokens. Typical responses are 35–200 tokens, for example:
```
D1 TRUE_LAHIRI mean-node whole-sign
As Cn 20:27 Ashlesha2 H1
Mo Cp 26:17 Dhanishta1 H7
Sa Li 28:37 Vishakha3 H4 R
```
- **Bodies:** `As Su Mo Ma Me Ju Ve Sa Ra Ke`. **Signs:** `Ar..Pi`.
- **`Ashlesha2`** means nakshatra plus pada.
- **`H`** is the whole-sign house counted from the Lagna; **`M`** is the house counted from the Moon.
- **`R`** means retrograde. **`R→D <date>`** is when it turns direct.
## Design notes
- **Nothing personal is stored.** Charts live only in an in-memory LRU cache.
- A `chart_id` is the birth data plus settings, encrypted with AES-SIV under a persistent server key. After a restart the server rebuilds the chart from the id alone.
- Without the key an id reveals nothing, and it can't be checked against guessed birth data.
- **No guessing:**
- An ambiguous place name ("Springfield", "Nizamabad, India") returns the list of candidates, and the model is told to ask the user.
- A time that falls in a DST gap or overlap returns both possible UTC offsets, and the model is told to ask the user.
- The model is instructed to pass the place exactly as the user said it.
- **Historical timezones** come from tzdata and timezonefinder, which covers war time, rule changes and local mean time before standard time was adopted.
- **Geocoding is offline** (GeoNames `cities500`). A place name resolves to the city center, and the output says so.
## Setup
Requires Python 3.12.
```bash
python -m venv .venv
.venv/Scripts/python -m pip install -r requirements.txt # Windows; use .venv/bin/python elsewhere
.venv/Scripts/python scripts/fetch_ephe.py # Swiss Ephemeris files -> data/ephe/ (~100 MB)
.venv/Scripts/python scripts/fetch_geonames.py # GeoNames -> data/geonames/ + SQLite index (~140 MB)
.venv/Scripts/python -m pytest tests # tests
```
**Chart-id key:** in production, set `ASTRO_ENGINE_CHART_KEY` to a hex string of at least 32 bytes. If it isn't set, a key is generated once in `.secrets/chart_id.key`, which is gitignored. Changing the key invalidates every issued `chart_id`.
**Claude Desktop** (`claude_desktop_config.json`; the Microsoft Store build keeps it under `%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\`):
```json
{
"mcpServers": {
"astro-engine": {
"command": "C:\\path\\to\\astro-engine\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\astro-engine\\mcp_server.py"]
}
}
}
```
## Layout
| File | Role |
|---|---|
| `engine.py` | chart, dasha and retrograde-station calculations (PyJHora + Swiss Ephemeris) |
| `geo.py` | offline geocoding |
| `tz.py` | historical UTC offsets |
| `birth.py` | turns raw user input into `BirthData` |
| `formatter.py` | compact text output |
| `cache.py` | encrypted chart ids, in-memory chart cache, ephemeris-by-date cache |
| `mcp_server.py` | the MCP server (stdio) |
`tests/private/` and `scratch/` are gitignored. They hold owner-specific reference data.
## License and attribution
- **This project:** [GNU AGPL-3.0](LICENSE), as required by its dependencies.
- **[PyJHora](https://github.com/naturalstupid/PyJHora):** AGPL-3.0.
- **[Swiss Ephemeris](https://www.astro.com/swisseph/):** AGPL-3.0, or a commercial license from Astrodienst.
- **Geographic data:** © [GeoNames](https://www.geonames.org), [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues