Skip to main content
Glama
README.md
# QuakeScope — Earthquake Activity Explorer for Athena

An [Athena AI Agent SDK](https://athenachat.bot/docs) app that lets you explore recent
earthquakes by **magnitude, depth, place and time range**, rendered as an interactive
world map inside the Athena chat.

- **Data source:** [USGS Earthquake Hazards Program](https://earthquake.usgs.gov/fdsnws/event/1/)
  FDSN event API (live, public, no key required).
- **Protocol:** Model Context Protocol over Streamable HTTP.
- **UI:** a `text/html+skybridge` widget resource driven by `window.openai`.

## MCP endpoint

```
https://<your-deployment>.vercel.app/mcp
```

## Tools

| Tool | Purpose |
| --- | --- |
| `search_earthquakes` | Query the live USGS feed by `time_window` (`hour`/`day`/`week`/`month`) or explicit `start_time`/`end_time`, `min_magnitude`, `max_magnitude`, `min_depth_km`, `max_depth_km`, named `region`, `place_contains` and `limit`. Returns structured stats plus the event list, and renders the interactive map. |
| `get_earthquake_details` | Full USGS record for one event id — felt reports, PAGER alert, tsunami flag, official event page. |
| `assess_earthquake_impact` | Impact and recovery assessment for one event, for government damage assessment and reconstruction budgeting. Renders the impact widget. |

### Official vs. derived — read this before using the impact tool

`assess_earthquake_impact` returns two deliberately separated objects.

**`official`** is [USGS PAGER](https://earthquake.usgs.gov/data/pager/) — real, published,
citable. Estimated direct economic loss (USD), estimated fatalities, alert level, the full
loss probability distribution, population exposure by shaking intensity (MMI), a per-country
breakdown, and USGS's own narrative comments. Nothing here is computed by this server.

**`reconstruction_scenario`** is **derived, and clearly labelled as such**. USGS does not
publish reconstruction cost or rebuild duration — no authoritative source does — so this is
transparent parametric sizing built on top of the official loss figure, following the
damage→needs convention used in World Bank / GFDRR post-disaster needs assessments:
reconstruction need = direct damage × build-back-better multiplier, with duration taken from
a severity-tier band driven by population exposed to damaging shaking. Every coefficient is
returned in `assumptions` and is caller-overridable; outputs are ranges, never point forecasts.

It is planning arithmetic to scope a response. It does not replace a field-based PDNA, it
excludes tsunami, landslide and liquefaction damage (PAGER is shaking-only), and it excludes
indirect losses such as lost output and trade disruption. The widget renders the two layers in
visually distinct sections so they cannot be confused, and the tool description instructs the
model to state which figures are which.

`search_earthquakes` is marked `openai/widgetAccessible`, so the widget itself can
re-invoke it — changing the time range in the UI is a real MCP round trip, not a
client-side filter.

## Widget interactions

1. **Magnitude slider** — drag to raise the M threshold; map, list and summary stats
   recompute live.
2. **Depth-band chips** — toggle shallow (&lt;70 km) / intermediate (70–300 km) /
   deep (&gt;300 km); markers are colour-coded by band.
3. **Linked selection** — click a marker on the map *or* a row in the list; the two
   stay in sync and a detail card shows time, depth, coordinates, felt reports,
   significance and a link to the USGS event page.
4. **Sort** — reorder the event list by recency, magnitude or depth.
5. **Time-range switch (1h / 24h / 7d / 30d)** — calls `search_earthquakes` again
   through `window.openai.callTool` and re-renders from fresh USGS data.

The impact widget adds its own: **tabbed official breakdowns** (shaking exposure, per-country,
loss probability, USGS notes), a **build-back-better multiplier slider**, and a
**recovery-pace selector** — the last two recompute the reconstruction need, annual funding
requirement and phased budget live.

UI state is persisted with `window.openai.setWidgetState`, so filters and the selected
event survive re-renders.

## Layout

```
api/mcp.js              Vercel serverless entry (stateless Streamable HTTP)
server.js               Local dev server -> http://localhost:8787/mcp
src/quake-server.js     MCP server: resource + tool definitions, USGS client
src/widget-html.js      GENERATED widget bundle (do not edit)
public/quake-widget.html  Map widget source (HTML + CSS + JS)
public/impact-widget.html Impact assessment widget source
src/impact.js           PAGER client + derived reconstruction model
public/land-path.txt    World coastline as an equirectangular SVG path
scripts/build-widget.cjs  Inlines the map into the widget -> src/widget-html.js
```

## Develop

```bash
npm install
npm run build:widget      # after editing public/quake-widget.html
npm start                 # http://localhost:8787/mcp
npx @modelcontextprotocol/inspector@latest --server-url http://localhost:8787/mcp --transport http
```

Expose it with `ngrok http 8787` and point the Athena agent at
`https://<subdomain>.ngrok.app/mcp`.

## Deploy

Any Node host works. On Vercel, `vercel.json` rewrites `/mcp` to the
`api/mcp.js` function; the transport is stateless
(`sessionIdGenerator: undefined`, `enableJsonResponse: true`) as serverless requires.

## Attribution

Earthquake data courtesy of the U.S. Geological Survey. Coastlines from
[world-atlas](https://github.com/topojson/world-atlas) (Natural Earth, public domain).