Skip to main content
Glama
README.md
# Sky Compass MCP

An [MCP](https://modelcontextprotocol.io) server that tells you where a celestial object is in the sky for your location. Give it a name such as **Moon**, **Mars**, or **Andromeda Galaxy** and it returns:

- the **magnetic compass bearing** in degrees and as a 16-point direction (N, NNE, … ENE … W, NW, …)
- the **altitude** (vertical angle above the horizon) to look up to
- whether the object is above the horizon right now

It also converts raw sky coordinates (right ascension and declination) into the same output.

It is built on the official [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) (FastMCP, pinned to `mcp>=1.2,<2`).

## Install

```bash
pip install sky-compass-mcp
```

Or run it without installing, using [uv](https://docs.astral.sh/uv/):

```bash
uvx sky-compass-mcp
```

## Use with an MCP client

Add the server to your client's MCP configuration, for example:

```json
{
  "mcpServers": {
    "sky-compass": {
      "command": "uvx",
      "args": ["sky-compass-mcp"]
    }
  }
}
```

If `uvx` is not available, use `"command": "sky-compass-mcp"` after installing with pip.

## Tools

| Tool | Purpose |
|------|---------|
| `locate_celestial_object` | Object name (solar system, catalogue, or SIMBAD identifier) + observer → bearing and altitude |
| `locate_by_coordinates` | RA/Dec (degrees, ICRS J2000) + observer → bearing and altitude |
| `track_object` | Streams timed samples with altitude and bearing rates (degrees per second) |
| `list_supported_objects` | Lists built-in solar-system and catalogue objects |

### Streaming

`track_object` takes `start_time_utc`, `duration_s` (up to 3600), and `interval_s` (0.1 to 60). When the client sends a `progressToken` in `_meta`, each sample is delivered as a standard `notifications/progress` message, paced in real time. The final result holds the sample count and the first and last samples. Without a token, the result contains all samples.

Each sample carries `magnetic_bearing_deg`, `altitude_deg`, `altitude_rate_deg_s`, and `magnetic_bearing_rate_deg_s`, which a telescope controller can compare against its gyroscope, accelerometer, and magnetometer readings.

### Example

Request:

```json
{
  "object_name": "Mars",
  "latitude": 28.6139,
  "longitude": 77.2090,
  "elevation_m": 216,
  "time_utc": "2026-11-02T20:00:00Z",
  "magnetic_declination_deg": 0.5
}
```

Response (abridged):

```json
{
  "object": "Mars",
  "altitude_deg": 13.654,
  "above_horizon": true,
  "magnetic_bearing_deg": 77.9,
  "compass_direction": "ENE",
  "compass_direction_name": "East-northeast"
}
```

### Supported objects

- **Solar system:** Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune
- **Fixed objects:** Andromeda Galaxy (M31), Triangulum Galaxy (M33), Whirlpool Galaxy (M51), Orion Nebula (M42), Pleiades (M45), Large Magellanic Cloud, Galactic Center (Sagittarius A\*), Polaris, Sirius, Vega, Betelgeuse
- **Anything else:** names are looked up in SIMBAD when remote lookup is enabled. Results are cached in the local SQLite catalog.

Pluto is not included.

## Configuration

| Option / variable | Default | Purpose |
|-------------------|---------|---------|
| `--db` / `SKY_COMPASS_DB` | `~/.cache/sky-compass-mcp/catalog.sqlite` | Local catalogue file |
| `--no-remote-catalog` / `SKY_COMPASS_SIMBAD=0` | off | Disable SIMBAD lookups |
| `--simbad-timeout` / `SKY_COMPASS_SIMBAD_TIMEOUT` | `10` | SIMBAD timeout, seconds |
| `--simbad-url` / `SKY_COMPASS_SIMBAD_URL` | Harvard, then CDS mirror | Comma-separated SIMBAD TAP endpoints, tried in order |
| `--transport http --host --port` | `stdio`, `127.0.0.1:8765` | Streamable HTTP with SSE, bound to localhost |
| `--no-realtime` | off | Do not pace streams in wall-clock time (useful for tests) |

SIMBAD lookups need internet access. The seeded catalogue works offline.

## Scope: read-only

This server only computes angles. It does not control motors or read sensors. Sensor fusion and motor control belong in a separate client that applies its own limits and safety checks.

## Magnetic declination

A magnetic compass points to magnetic north, which differs from true north by the local **magnetic declination**. This server computes bearings from true north, then subtracts the declination you pass in `magnetic_declination_deg` (east positive). It does not bundle a magnetic model, so the default is `0`, which means magnetic equals true. Look up your location's declination for the current date, for example with [NOAA's calculator](https://www.ngdc.noaa.gov/geomag/calculators/magcalc.shtml), before using the bearing with a real compass.

## Accuracy

| Object type | Typical accuracy |
|-------------|------------------|
| Sun and planets | about 0.1° |
| Moon | about 0.3° |
| Fixed objects | catalog precision; proper motion, precession, and refraction are ignored |

Altitude is geometric. Atmospheric refraction raises low objects by up to about 0.5°. This is suitable for finding objects by eye and is not for navigation or professional observation.

## Run from source

```bash
python -m sky_compass_mcp          # starts the stdio server
python -m unittest discover -s tests -v
SKY_COMPASS_LIVE=1 python -m unittest tests.test_simbad_live -v   # live SIMBAD (needs internet)
```

## Protocol

Built on the official MCP Python SDK (`FastMCP`). Served over stdio by default, or over Streamable HTTP at `POST /mcp` with `--transport http`. Logs go to stderr only.

The fixed-object coordinates are approximate and should be checked against SIMBAD before publishing. The SIMBAD client tries each configured mirror in turn. Live tests (`SKY_COMPASS_LIVE=1`) pass through the Harvard mirror when the CDS host is unreachable, but each unreachable mirror adds its full timeout to a lookup.

## Publishing

1. Build: `python -m build`
2. Check: `twine check dist/*`
3. Upload to PyPI: `twine upload dist/*`
4. Register in the [MCP Registry](https://github.com/modelcontextprotocol/registry) with `mcp-publisher publish` using `server.json`. Update the `repository` URL and the `name` namespace to match your GitHub account first.

## License

MIT