Skip to main content
Glama
earthquake-mcp-server

earthquake-mcp-server

by cyanheads

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://earthquake.caseyjhand.com/mcp


Overview

Seismic data from USGS ComCat and the EMSC SeismicPortal. Fetch real-time earthquake feeds, search and count seismic events by time, magnitude, depth, and location, and pull full analysis detail for a single event.

Tools

Tool

Description

earthquake_get_feed

Fetch a USGS pre-computed real-time earthquake feed by magnitude tier and time window

earthquake_search_events

Search earthquakes by time range, magnitude, depth, location radius, PAGER alert level, felt reports, or ShakeMap and DYFI intensity

earthquake_count_events

Count earthquakes matching filters without fetching full records

earthquake_get_event

Fetch one earthquake by USGS or EMSC event ID, with USGS analysis products and opt-in origins, magnitudes, phase arrivals, aftershock forecast, and PAGER loss estimates

Resources

Resource

Description

earthquake://feed/{magnitude_tier}/{time_window}

First 100-event page of a USGS real-time feed, with an exact continuation URI

earthquake://feed/{magnitude_tier}/{time_window}/page/{cursor}

Continue the retained feed generation using the returned next_uri

earthquake://event/{event_id}

One earthquake by USGS or EMSC event ID as injectable context, with the same detail product projection as earthquake_get_event

Skill

Skill

Description

earthquake-guide

Agent guide for the earthquake_* tools: identifier chains between USGS and EMSC, coverage and magnitude-scale gates, and when product and include groups answer a question. Loaded by plugin hosts from skills/ and served over MCP as skill://earthquake-guide/SKILL.md

Related MCP server: quake-mcp

Capability reference

earthquake_get_feed tool

  • Choose a magnitude tier (all, 1.0, 2.5, 4.5, significant) and a window (hour, day, week, month). USGS caches these feeds; use earthquake_search_events for historical or filtered queries.

  • Returns events, page count, generation time, and source feed URL. all includes microseisms; significant is USGS-curated using magnitude, felt reports, and PAGER impact.

  • Each page returns limit events (default 15), fewer only on the last page. A limit above 1,000 returns 1,000 with a notice naming the requested and applied values; with EARTHQUAKE_CLAMP_LIMITS=false it is rejected instead. limit applies to the first page; later pages keep the page size in the cursor. totalCount reports the whole feed; pass nextCursor back unchanged with the same tier and window.

  • Continuations read the first-read generation, unaffected by upstream edits. Retention lasts up to 15 minutes, subject to capacity eviction and restart. cursor_expired means restart without a cursor; legacy cursors must also be restarted.


earthquake_search_events tool

  • Search usgs or emsc by time, magnitude, depth, radius, or bounding box. A radius requires latitude, longitude, and radius_km together; box edges are independently optional, support antimeridian bounds up to ±360°, and intersect a supplied circle.

  • Returns normalized events with event_type, EMSC's event_certainty, and USGS's associated_ids (other network IDs merged into the event). ignoredFilters names unsupported filters; queryEcho reports the effective query.

  • Both search and count accept open magnitude_type values on either source; EMSC matches its own lowercase types (mb, mw) exactly and has no mww. product_type (required product, e.g. moment-tensor) and review_status (automatic or reviewed) apply only to USGS; EMSC names them in ignoredFilters. Blank values mean omitted.

  • event_type, magnitude_type, and product_type stay open strings; USGS matches them case-insensitively, and event_type takes a comma-separated list. USGS answers a value outside its published vocabulary with zero events, so when a USGS search's first page or a USGS count comes back empty, notice names each unlisted value and the listed values it equals once case and -, _, and spaces are ignored: product_type "moment_tensor" suggests "moment-tensor". The vocabulary read is one attempt within the call's deadline, cached for 24 hours; if it fails, the zero returns as it would without the check. EMSC publishes no vocabulary and is not checked.

  • min_mmi (ShakeMap intensity, Modified Mercalli) and min_cdi (DYFI community intensity), each 1–12, select events by how strongly they shook, which a magnitude floor or alert_level misses. Both apply only to USGS: an event without a ShakeMap or DYFI reports never matches, and EMSC names them in ignoredFilters.

  • Date-only bounds (including expanded year/year-month forms) mean midnight at the start of the named day on both sources: 2026-10-10 becomes 2026-10-10T00:00:00. Explicit time and timezone suffixes are preserved; unpadded date components are padded. Offsetless times use UTC for the 30-day default. queryEcho shows the exact effective bounds.

  • Sort by time or magnitude in either direction. Each call requests limit events upstream (default 15, set by DEFAULT_LIMIT) and returns them all, fewer only on the last page. A limit above 1,000 returns 1,000 with a notice naming the requested and applied values; with EARTHQUAKE_CLAMP_LIMITS=false it is rejected instead. queryEcho.limit reports the applied limit. Continue with the returned nextOffset (offset plus the events returned, counting from 1) and the same filters, limit, and order. Search pages read the current catalog, so upstream edits can affect later pages.

  • Strictly inverted magnitude, depth, or explicit time bounds fail locally as invalid_range, naming both values and recovery. Equal and single bounds remain valid; time comparisons use UTC instants, including explicit offsets. Calendar-invalid values remain for upstream rejection.

  • A nonempty short page establishes totalCount; an empty first page establishes zero, while an empty later page leaves the total unknown. Full pages use a count lookup, with countUnavailable on failure. Feed, search, count, and event calls each share one 50-second deadline across their requests and retries; for search that includes the count lookup. Cancellation remains cancellation.


earthquake_count_events tool

  • Count matches using the same filters as earthquake_search_events, without fetching events. Omit start_time for the last 30 days; queryEcho reports the resolved window and applied filters.

  • exceeds_limit flags the 20,000-event single-request threshold, not a retrieval ceiling: search offset paging can retrieve larger match sets. max_allowed is 20,000 for USGS and null for EMSC; ignoredFilters names filters the source cannot apply.

  • A USGS count of zero carries notice when a supplied event_type, magnitude_type, or product_type value is not in the USGS vocabulary, as search's empty first page does.


earthquake_get_event tool

  • Pass the id of any feed or search result. An EMSC unid from a source=emsc search (eight digits, underscore, seven digits, e.g. 20190706_0000043) is looked up on EMSC SeismicPortal; every other ID goes to USGS (e.g. us6000sznj).

  • A USGS event returns the normalized event plus optional detail: PAGER, ShakeMap, DYFI, moment tensor, ground-failure alerts, origin quality (including depth_type, which separates an assigned depth from a located one), and finite-fault dimensions. Groups are omitted when none of their projected fields is available; absence does not imply zero impact. associated_ids lists the other network IDs USGS holds for the event; an alias such as us70004bn0 returns the preferred event (ci38457511) with the alias listed there.

  • An EMSC event returns the same normalized event its search result carries, with no detail and no associated_ids.

  • include opts into more for USGS events:

    Group

    Source

    Extra requests

    origins

    Every origin product in the event response, preferred first

    None

    magnitudes

    Event-level magnitudes from the preferred phase-data product's QuakeML, with a station count where published

    One, shared with arrivals

    arrivals

    Every phase arrival of that document's preferred origin, joined to its pick and sorted nearest first

    One, shared with magnitudes

    aftershock_forecast

    The USGS Operational Aftershock Forecast: each window's probability of an aftershock at or above each magnitude, its expected-count range, and the chance of one as large as the mainshock

    One

    pager

    The PAGER estimates behind the alert, from the preferred losspager product's JSON files: the fatality and economic-loss alert levels with every probability bracket (economic bounds in millions of USD), PAGER's impact statements, and the people exposed at each shaking intensity, in total and per country

    Two, or three when the product publishes impact statements

  • A requested group that is not returned is listed in unavailable_includes: not_published when the event has no such product (every group on an EMSC event) or its product lacks the files the group reads (PAGER products published before the JSON files existed), fetch_failed when the download failed, returned HTML, did not parse, pointed outside the configured USGS origin, or answered with a redirect. The event, detail, and other groups still return; cancellation propagates. The event request and its product downloads share one 50-second deadline.

  • Blank IDs fail locally; canonical IDs and aliases match case-insensitively. ShakeMap includes the producer's code_version when published.


earthquake://feed/{magnitude_tier}/{time_window} resource

  • Choose the same magnitude tiers and time windows as earthquake_get_feed; all 20 combinations are listed as browsable resources.

  • Returns 100 events per application/json page in the compact event projection, plus totalCount, truncated, nextCursor, and next_uri. Read next_uri verbatim until it is absent. The continuation URI is earthquake://feed/{magnitude_tier}/{time_window}/page/{cursor}.

  • Feed resources share retained generations with earthquake_get_feed. Cache hints are private with zero lifetime because continuation tokens belong to the requesting tenant. Upstream failures classified as ServiceUnavailable expose feed_unavailable; those classified as Timeout expose feed_timeout, each with recovery guidance and no captured upstream body. Availability failures preserve numeric HTTP status; HTTP 501 remains non-retryable and points to EMSC search. Other error classes propagate unchanged.


earthquake://event/{event_id} resource

  • Pass a USGS or EMSC event_id from a feed or search result. Returns the same event and optional detail products as earthquake_get_event; it takes no include.

  • Uses that tool's event_deleted, not_found, source_unavailable, and source_timeout error contract.

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

USGS/EMSC-specific:

  • Type-safe clients for the USGS FDSN/GeoJSON API and the EMSC FDSN-WS API, normalizing both into one shared earthquake domain schema

  • Automatic retry with backoff and per-request timeouts on every upstream call; detects USGS's rate-limited/CDN failure mode (HTML served instead of GeoJSON) and maps it to a typed service-unavailable error instead of parsing it as data. A body that is not the expected document fails the same way without a retry, and a connection dropped mid-body is retried; neither passes parser or runtime text to the caller

  • EMSC's two-character evtype code is decoded against the published event-type/certainty nomenclature into the same vocabulary USGS publishes, so event_type carries one meaning across both sources

  • USGS-only filters (alert_level, min_felt, min_significance, min_mmi, min_cdi, event_type, product_type, review_status) are named in ignoredFilters when source=emsc. On USGS, event_type="earthquake" excludes quarry blasts and other non-tectonic records.

  • No API key or rate-limit tier required — both USGS and EMSC are fully public, keyless APIs

Agent-friendly output:

  • Provenance — source: "usgs" | "emsc" on search and count responses, plus source_catalog/auth fields naming the catalog and authoritative agency, so agents can weigh two independent solutions against each other

  • Discriminated output contracts — event_type and EMSC's event_certainty preserve published classifications, with absent fields remaining unknown; exceeds_limit, countUnavailable, and truncated flags let callers branch on data instead of parsing prose

  • Response shaping — fields a source does not publish come back null, never a fabricated zero (tsunami, status, and mmi are always null on EMSC events); USGS-only filters dropped for an EMSC query are named in ignoredFilters rather than silently ignored

  • Graceful degradation — an upstream rejection surfaces the service's own explanation (offending parameter, accepted format) in the error message instead of a bare status code, a failed follow-up count degrades to countUnavailable rather than failing the whole search, and a failed product download lists its include groups as fetch_failed rather than failing the event lookup

Getting started

Public Hosted Instance

A public instance is available at https://earthquake.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:

{
  "mcpServers": {
    "earthquake-mcp-server": {
      "type": "streamable-http",
      "url": "https://earthquake.caseyjhand.com/mcp"
    }
  }
}

Self-Hosted / Local

Add the following to your MCP client configuration file.

{
  "mcpServers": {
    "earthquake-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/earthquake-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "earthquake-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/earthquake-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "earthquake-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/earthquake-mcp-server:latest"]
    }
  }
}

For Streamable HTTP, set the transport and start the server:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp

Prerequisites

  • Bun v1.4.0 or higher.

  • No API keys required — USGS and EMSC data is fully public.

Installation

  1. Clone the repository:

git clone https://github.com/cyanheads/earthquake-mcp-server.git
  1. Navigate into the directory:

cd earthquake-mcp-server
  1. Install dependencies:

bun install

Configuration

All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:

Variable

Description

Default

MCP_TRANSPORT_TYPE

Transport: stdio or http

stdio

MCP_HTTP_PORT

HTTP server port

3010

MCP_HTTP_ENDPOINT_PATH

HTTP endpoint path where the MCP server is mounted

/mcp

MCP_PUBLIC_URL

Public origin override for TLS-terminating reverse-proxy deployments

none

MCP_SESSION_MODE

HTTP sessions: stateful, stateless, or auto (resolves to stateful). Overrides the createApp() declaration; Docker and .env.example also pin stateless.

stateless (declared in src/index.ts)

MCP_AUTH_MODE

Authentication: none, jwt, or oauth

none

MCP_LOG_LEVEL

Log level (debug, info, warning, error, etc.)

info

MCP_GC_PRESSURE_INTERVAL_MS

Opt-in Bun-only forced-GC pressure loop (ms). Try 60000 if heap growth is observed under sustained HTTP load.

0 (disabled)

LOGS_DIR

Directory for log files on Node.js and Bun

<project-root>/logs

LOG_TOOL_FAILURE_PAYLOADS

Log failed tool arguments and results, redacted by key name. Secrets inside free-form values remain.

false

LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES

UTF-8 byte cap per logged failure payload

16384

STORAGE_PROVIDER_TYPE

Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1

in-memory

USGS_BASE_URL

USGS API base URL. Override for testing or mirroring. Product files (phase-data QuakeML, aftershock forecast, PAGER JSON) are fetched only on this origin, so a mirror must also serve them, with content URLs rewritten to its origin; otherwise those include groups report fetch_failed.

https://earthquake.usgs.gov

EMSC_BASE_URL

EMSC API base URL. Override for testing or mirroring.

https://www.seismicportal.eu

DEFAULT_LIMIT

Events earthquake_search_events returns when a call omits limit (1–1,000)

15

EARTHQUAKE_CLAMP_LIMITS

Clamp a search or feed limit above 1,000 to 1,000 with a notice. false advertises and enforces a schema maximum of 1,000 instead.

true

REQUEST_TIMEOUT_MS

Per-attempt HTTP timeout in milliseconds (1,000–120,000). Every feed, search, count, and event call also runs under one 50-second total deadline across its requests and retries.

10000

OTEL_ENABLED

Enable OpenTelemetry

false

OTEL_EXPORTER_OTLP_ENDPOINT

Base URL for traces (/v1/traces) and metrics (/v1/metrics); signal-specific endpoint variables override it

none

OTEL_EXPORTER_OTLP_LOGS_ENDPOINT

Opt-in OTLP log endpoint, used as-is; the base URL never enables logs

none

Empty values and unsubstituted whole-value ${…} placeholders use the defaults. See .env.example for optional overrides.

Running the server

Local development

  • Build and run the production version:

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:http
    # or
    bun run start:stdio
  • Run checks and tests:

    bun run devcheck  # Lints, formats, type-checks, and more
    bun run test      # Runs the test suite

Docker

docker build -t earthquake-mcp-server .
docker run --rm -p 3010:3010 earthquake-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/earthquake-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

Directory

Purpose

src/mcp-server/tools

Tool definitions (*.tool.ts). Four tools across USGS and EMSC.

src/mcp-server/resources

Feed, feed continuation, and event resources.

src/services/feed-snapshots.ts

Tenant-scoped serialized feed retention with bounded payload and metadata accounting.

src/services/usgs

USGS ComCat service — GeoJSON feed fetcher, FDSN query API client, and the event include projections (origin products, phase-data QuakeML, aftershock forecast, PAGER estimates).

src/services/emsc

EMSC SeismicPortal service — FDSN event search, count, and by-ID lookup.

src/config

Server-specific environment variable parsing and validation with Zod.

tests/

Unit and integration tests, mirroring the src/ structure.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic

  • Use ctx.log for logging, ctx.state for storage

  • Register new tools and resources in the createApp() arrays

  • Validate upstream data, normalize to domain types, and preserve missing values rather than inventing facts

Contributing

Issues are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    An MCP server for querying the FDSN Web Service Event APIs of multiple seismological datacenters and retrieving earthquake information as JSON.
    6
    2
    AGPL 3.0
  • A
    license
    A
    quality
    C
    maintenance
    MCP server wrapping the USGS Earthquake Hazards API, enabling AI assistants to search the global earthquake catalog, look up event details, count quakes, find 'Did You Feel It' reports, and read realtime feeds.
    5
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Exposes live earth-science data (e.g., earthquakes from USGS) as tools for MCP-compatible AI agents, enabling them to fetch and reason about real-time geophysical events.
    1
    -