# Astro-MCP Implementation Plan **Port: 7016** (ephemeris-mcp is 7015) ## What We're Building An MCP server that consumes `ephemeris-mcp:get_sky_state` via MCP-over-SSE client, then performs pure astrological calculations (zodiac signs, houses, aspects, angles) on top of that astronomical data. No interpretation -- only structured JSON output. Includes a person database (SQLite), a Jinja2+vanillaJS dashboard, and a Docker container. ## Project Structure ``` astro-mcp/ ├── Dockerfile ├── docker-compose.yml ├── main.py # entrypoint, same pattern as ephemeris-mcp ├── requirements.txt ├── .gitignore ├── README.md ├── PROJECT.md ├── run.sh ├── killserver.sh ├── restart.sh ├── tests.sh ├── data/ # SQLite DBs at runtime ├── logs/ # PID + server.log ├── src/ │ └── astro_mcp/ │ ├── __init__.py │ ├── config.py # PORT=7016, dirs, ephemeris URL │ ├── server.py # FastAPI + FastMCP + lifespan + HTTP routes │ ├── ephemeris_client.py # MCP SSE client calling ephemeris-mcp:get_sky_state │ ├── astrology.py # pure calc: zodiac, houses, aspects, angles │ ├── models.py # Pydantic models / type aliases for chart structs │ ├── storage.py # SQLite: persons table + chart cache │ ├── dashboard.py # Jinja2 templates + dashboard routes │ └── tools.py # @mcp.tool() definitions (wiring layer) ├── templates/ │ ├── base.html │ ├── dashboard.html │ └── persons.html └── tests/ ├── conftest.py ├── test_astrology.py # unit tests for pure calc functions ├── test_tools.py # integration tests with mocked ephemeris client └── test_server.py # HTTP endpoint smoke tests ``` ## Dependencies (requirements.txt) ``` fastapi>=0.115.0 uvicorn[standard]>=0.30.0 fastmcp>=2.0.0 mcp>=1.0.0 pydantic>=2.8.0 jinja2>=3.1.0 python-dotenv>=1.0.1 pytest>=8.0.0 ``` Note: No `pyswisseph` -- all astronomical data comes from ephemeris-mcp via MCP client. No heavy native deps beyond what FastMCP/mcp pulls in. ## Key Design Decisions 1. **MCP client pattern**: Same as hermes-mcp's `argus_client.py` -- `sse_client(url)` + `ClientSession`. The ephemeris URL is configurable via `EPHEMERIS_MCP_URL` env var (default `http://ephemeris-mcp:7015/mcp/sse` for Docker, `http://127.0.0.1:7015/mcp/sse` for local dev). 2. **Async throughout**: The ephemeris client is async (`sse_client` is async context manager). The FastMCP tools are `async def`. The SQLite access uses `asyncio.to_thread()` to avoid blocking. 3. **Calculation module is pure sync**: `astrology.py` has no async, no I/O -- just math on dicts/lists. Easy to unit test without any event loop. 4. **Ephemeris client call_sky_state helper**: A single internal async function that calls `ephemeris-mcp:get_sky_state` and returns the parsed dict. All tools go through this. Timeout: 8s. 5. **House systems**: Placidus default, with `house_system` parameter on chart tools. Start with Placidus + Equal House only. The house calculation uses the sidereal time from `get_sky_state` output (`sidereal_time.local_sidereal_time`). 6. **Aspect orbs (defaults)**: conjunction 8, sextile 6, square 8, trine 8, opposition 8. Configurable per-tool via optional `orb_limits` dict. 7. **Person DB schema**: ```sql CREATE TABLE persons ( id TEXT PRIMARY KEY, name TEXT NOT NULL, nickname TEXT UNIQUE, birth_datetime TEXT NOT NULL, -- ISO 8601 UTC latitude REAL NOT NULL, longitude REAL NOT NULL, elevation REAL DEFAULT 0.0, created_at TEXT NOT NULL -- ISO 8601 UTC ); ``` 8. **Docker**: Based on `python:3.13-slim`, no build-essential needed (no pyswisseph). Health check hits `http://127.0.0.1:7016/health`. ## MCP Tool Surface (7 tools) | Tool | Description | |---|---| | `get_planetary_positions` | Wrapper around ephemeris-mcp, adds sign + degree + retrograde. Optional `bodies` filter. | | `calculate_natal_chart` | Full natal chart: planets in signs/houses, aspects (applying/separating), angles. Params: `birth_datetime`, `lat`, `lon`, optional `house_system`, `orb_limits`. | | `calculate_transit_chart` | Transit planets + natal-to-transit aspects + transiting planets in natal houses. Same params as natal + `transit_datetime`. | | `calculate_synastry_chart` | Two birth data sets -> interaspects, house overlays, composite chart (midpoint method), Davison chart. | | `get_transit_preview` | Person ID + time range -> list of events (exact aspects, ingresses, stations, lunar phases). Optional `event_types` filter. | | `person_manage` | `action` param: `add`, `get`, `list`, `update`, `delete`. Birth data in add/update. | | `list_house_systems` | Discovery tool -- returns supported house systems. | ## Docker Compose ```yaml services: astro-mcp: build: {context: ., dockerfile: Dockerfile} image: astro-mcp:latest container_name: astro-mcp restart: unless-stopped ports: ["7016:7016"] environment: ASTRO_HOST: 0.0.0.0 ASTRO_PORT: 7016 ASTRO_DATA_DIR: /app/data ASTRO_LOG_DIR: /app/logs ASTRO_DB_PATH: /app/data/astro.sqlite3 EPHEMERIS_MCP_URL: http://ephemeris-mcp:7015/mcp/sse volumes: ["./data:/app/data", "./logs:/app/logs"] healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:7016/health').read()"] interval: 30s timeout: 5s retries: 3 start_period: 20s ``` ## Implementation Order ### Phase 1 -- Scaffold - Create directory structure, `.gitignore`, `requirements.txt`, `src/astro_mcp/` package - `main.py` entry point (copy ephemeris-mcp pattern) - `config.py` (PORT=7016, dirs, EPHEMERIS_MCP_URL) - `server.py` basic FastAPI + FastMCP with `/health` and `/` - `run.sh`, `killserver.sh`, `restart.sh`, `tests.sh` - Dockerfile + docker-compose.yml - Verify: `./run.sh` starts, `curl /health` returns ok, commit ### Phase 2 -- Astrology calculation module (pure, no I/O) - `astrology.py`: zodiac sign from ecliptic longitude, house cusps (Placidus + Equal), aspect detection with orbs, angle calculation (ASC/MC from sidereal time) - Pydantic models for chart structures - `tests/test_astrology.py`: comprehensive unit tests - Verify: `pytest tests/test_astrology.py -q` passes, commit ### Phase 3 -- Ephemeris client + planetary positions tool - `ephemeris_client.py`: `call_sky_state(datetime, lat, lon)` using `sse_client` - `tools.py`: `get_planetary_positions` tool -- calls sky_state, adds sign/degree/retrograde to each body - Test with mocked client - Verify: tool returns enhanced body array, commit ### Phase 4 -- Person storage + management - `storage.py`: persons DB, same cache pattern as ephemeris-mcp but using `asyncio.to_thread` for DB access - `person_manage` tool (add/get/list/update/delete) - `tests/test_tools.py`: person CRUD tests - Verify: CRUD round-trips, commit ### Phase 5 -- Natal chart tool - Wire `calculate_natal_chart` in `tools.py` - Calls `call_sky_state` -> feeds planetary positions into astrology module -> assembles full chart dict - Test with mocked ephemeris output: verify all chart sections present (planets, houses, aspects, angles) - Verify: structured natal chart output, commit ### Phase 6 -- Transit + Synastry tools - `calculate_transit_chart`: calls sky_state twice (natal + transit datetimes), computes aspects between them - `calculate_synastry_chart`: calls sky_state twice, computes interaspects, house overlays, composite (midpoint), Davison - Tests for both - Verify, commit ### Phase 7 -- Transit preview - `get_transit_preview`: Given person_id + start/end range, iterates through time steps to find exact aspects (orb <= 0d01), ingresses (sign changes), retrograde stations (speed_lon sign changes), lunar phase changes - Uses the person DB + ephemeris client calls - This is the most algorithmically complex tool -- scan daily then refine with hourly substeps around detected events - Verify, commit ### Phase 8 -- Dashboard - `dashboard.py`: Jinja2 templates mounted on FastAPI routes (`/dashboard`, `/dashboard/persons`, `/dashboard/persons/`) - Vanilla JS for add/edit/delete persons via fetch API - Templates: base layout, person list, person detail with chart preview link - Mount routes in `server.py` lifespan or at module level - Verify: browser test, commit ### Phase 9 -- Integration + docs - Full test suite running via `tests.sh` - `README.md`: tool descriptions, example calls, Docker usage - `PROJECT.md`: technical specs, schema, architecture - Final commit and push