Skip to main content
Glama

gagelink

Datos de hidrología para agentes de IA. Nivel de río, caudal, pronósticos de inundación, calidad del agua, cuencas de drenaje y elevación de la superficie del agua por satélite, de USGS, NOAA y SWOT. Cada valor lleva su unidad, el datum desde el que se mide, su zona horaria y si el registro es provisional o aprobado.

mcp-name: io.github.Adeniyikayodee/gagelink

Pre-alfa. La API cambiará.

Ejecutarlo como servidor MCP

{
  "mcpServers": {
    "gagelink": {
      "command": "uvx",
      "args": ["--from", "gagelink", "gagelink-mcp"]
    }
  }
}

No se necesita cuenta para empezar. Una clave gratuita de api.waterdata.usgs.gov/signup aumenta el límite de 50 solicitudes por hora a 1.000; se configura como GAGELINK_API_KEY.

O como biblioteca:

pip install gagelink

Related MCP server: Environment Agency Flood Monitoring MCP Server

Preguntas que responde

  • ¿Qué altura tiene el río en una estación de aforo y cómo se compara con el nivel de inundación?

  • ¿Cuánto resguardo libre hay entre el agua y la cresta de un dique levantado?

  • ¿Cuál es el caudal actual y qué fracción del máximo histórico representa?

  • ¿Qué se pronostica para los próximos días y cruza alguna categoría de inundación?

  • ¿Qué hay aguas arriba o aguas abajo de este punto, a lo largo del río?

  • ¿Qué tan grande es la cuenca que drena hasta este punto?

  • ¿Qué registró esta estación en un rango de fechas y se ha revisado ese registro desde entonces?

  • ¿Cuál es la elevación de la superficie del agua de un río sin estación de aforo?

  • ¿Una lectura es provisional o aprobada y qué antigüedad tiene?

Lo que rechaza y por qué ese es el punto

Una altura de estación se mide desde el datum propio de la estación, no desde el nivel del mar. Restar una de una elevación levantada devuelve un número que parece un resguardo libre y se equivoca por decenas de pies, en la dirección de declarar seguro un dique. Ambas cifras son longitudes en pies, así que nada dimensional las separa y ninguna biblioteca de unidades lo detecta.

Este paquete rechaza esa resta en lugar de responderla, y describe_location devuelve el desfase que la hace estar bien definida. Lo mismo se aplica a las elevaciones por satélite, que están sobre un geoide, y a los caudales modelados, que pueden no tener una medición detrás.

Por qué

Los servicios de agua ya publican todo lo necesario para usar sus datos correctamente. Un caudal indica su unidad, un nivel indica el datum desde el que se mide, una lectura indica si es provisional o aprobada, y un sello de tiempo indica su desfase. Los clientes suelen analizar el número y descartar el resto, y los errores siguen a eso.

El fallo es medible. En una evaluación comparativa de 4.288 ejecuciones en once modelos, quantity-guard encontró que cada modelo que llegaba a la herramienta de cálculo enviaba un caudal publicado en pies cúbicos por segundo a un parámetro declarado en metros cúbicos por segundo sin convertirlo, en casi todas las ejecuciones, dando una respuesta 35,3 veces demasiado grande sin nada en la salida que lo indicara. Siete de once restaron un nivel sobre un datum local de estación contra una elevación en NAVD88 e informaron el resultado como resguardo libre.

gagelink recupera los datos con los metadatos conservados y usa quantity-guard para aplicarlos donde se llaman las herramientas del agente.

Superficie actual

from gagelink import Service

service = Service(api_key="...")           # free key, see below
page, retrieval = service.items(
    "latest-continuous",
    monitoring_location_id="USGS-07374000",
    parameter_code="00060",
)

retrieval.record()      # what a replay needs: url, params, time, status, sha256
retrieval.quota         # Quota(limit=1000, remaining=999)

items devuelve el contenido analizado y el registro de haberlo obtenido juntos, en lugar de solo el contenido, porque un número que llega a una respuesta sin la solicitud que lo produjo no puede reproducirse, y emparejarlos en el único punto de entrada es más barato que recordar registrarlo.

Las cargas útiles se convierten en cantidades que llevan sus propios marcos de referencia:

from gagelink import location_from, readings_from

page, _ = service.items("monitoring-locations", id="USGS-06730500")
station = location_from(page["features"][0])
station.register()                       # its datum, and the offset where one is published

observations, _ = service.items("latest-continuous", monitoring_location_id=station.id)
readings = {r.parameter_code: r for r in readings_from(observations, station)}

readings["00060"].value       # Q(1.35 ft³/s (provisional))
readings["00065"].value       # Q(9.11 ft (GAGE:06730500, provisional))
readings["00065"].value.to_datum("NGVD29")   # Q(4869.11 ft (NGVD29, provisional))
readings["00065"].value.to_datum("NAVD88")   # DatumConversionUnavailable

Esa última línea es el punto. Boulder Creek publica su altitud sobre NGVD29, así que un nivel allí se resuelve sobre NGVD29 y rechaza NAVD88, ya que el desfase entre ambos varía con la ubicación y no se publica aquí. Asumir el datum moderno porque es el datum moderno es un error de resguardo libre un paso antes del que cualquiera busca.

Lo que no se publica y qué se hace al respecto

altitude y drainage_area vuelven como números desnudos, y el esquema de la colección declara ninguna unidad para ninguno, así que las convenciones de USGS de pies y millas cuadradas se aplican en normalise.py donde son visibles en lugar de asumirse más adelante en la cadena.

Una unidad sin correspondencia se rechaza en lugar de adivinarse. Una unidad que pint puede analizar pero que este paquete no tiene registrada se deja pasar con una advertencia, porque que se pueda analizar no es lo mismo que entenderla: ppt se lee como partes por billón en pint y significa partes por mil en USGS, lo que es un factor de 10^9 entre dos lecturas dimensionalmente idénticas.

Un valor faltante es null aquí en lugar del -999999 que WaterServices publicaba, y permanece faltante. El calificador dice por qué, ["EQUIP"] para una interrupción de equipo.

La aprobación llega como Provisional o Approved en lugar de P o A, y los códigos de condición califican por debajo de su estado de revisión, así que un registro aprobado de una medición afectada por hielo se califica como no verificado en lugar de aprobado.

La zona horaria de la estación se resuelve desde la abreviatura junto con el indicador de horario de verano, ya que MST sin horario de verano es Arizona y MST con él es Colorado, y difieren por una hora durante ocho meses del año.

Herramientas

Una sesión guarda el estado de una pregunta y el registro de qué la respondió. Las herramientas devuelven un resultado en lugar de lanzar una excepción, porque un fallo que lleva una reparación mantiene al modelo en la conversación donde puede corregirse a sí mismo, y una excepción lanzada termina el turno.

from gagelink import Session, Toolkit

with Session(question="How high is the Potomac at Little Falls?") as work:
    kit = Toolkit(work)
    kit.describe_location("USGS-01646500")
    kit.get_latest("USGS-01646500", parameters=["00060", "00065"], max_age_hours=6)

    work.audit("The gage height is 3.02 ft and the discharge is 2960 ft3/s.")
    work.manifest()

Cada valor sale con su marco adjunto, y cada uno se registra en un libro de contabilidad, así que una respuesta puede verificarse contra lo que realmente se recuperó:

[ok]         3.02 ft        from get_latest.00065
[ok]         2960 ft3/s     from get_latest.00060
[UNSOURCED]  116000 ft3/s   no tool output produced this value

La tercera línea es la verificación que gana su lugar. La cifra es un caudal plausible para ese río, está equivocada, y nada en la frase que la contiene lo indica.

Una serie se devuelve como un identificador con un resumen y una muestra de veinte puntos en lugar de sus puntos completos, ya que un año de registros de 15 minutos son 35.000 valores. El identificador se deriva de la consulta que lo produjo, así que una reproducción de la misma sesión produce el mismo identificador. Los resultados tienen un presupuesto, y cualquier cosa que se recorte para mantenerse dentro del presupuesto se indica en el resultado, ya que un recorte silencioso se lee como cobertura.

herramienta

propósito

find_locations

buscar por estado, condado, unidad hidrológica, tipo de sitio o caja delimitadora

describe_location

metadatos, datum, zona horaria y el desfase que un nivel necesita

get_latest

valor más reciente por parámetro, con antigüedad y calidad

get_series

un rango de fechas, como identificador más un resumen

slice_series

reducir una serie almacenada sin volver a obtenerla

get_peaks

registro anual de caudales máximos

get_forecast

nivel observado y pronosticado, con umbrales de inundación

navigate_network

ubicaciones de monitoreo aguas arriba o aguas abajo a lo largo del río

get_basin

el área que drena hasta un punto

lookup_parameter

resolver un código de parámetro, ya que las lecturas no llevan nombre

Como servidor MCP

export GAGELINK_API_KEY=...        # free, see below
gagelink-mcp
{"mcpServers": {"gagelink": {"command": "gagelink-mcp"}}}

Once herramientas, ni una más. Un modelo se degrada a medida que crece su lista de herramientas, así que la superficie se organiza por verbo y la elección de qué servicio responde la hace el servidor en lugar de dejársela al llamante.

Las descripciones de las herramientas son parte del producto en lugar de documentación del mismo. En la evaluación de quantity-guard, declarar metadatos físicos en el esquema sin aplicarlos aún así recuperó un tercio de las ejecuciones que fallaron en la línea base, así que lo que una descripción dice sobre datums, unidades y registro provisional sí funciona antes de que se ejecute ninguna validación.

Un fallo de herramienta vuelve como contenido marcado como error en lugar de como una falta de protocolo, lo que mantiene la reparación frente al modelo en lugar de terminar el turno. La sesión se reinicia en initialize, así que las cantidades de una conversación no pueden aparecer en el manifiesto de otra.

Resguardo libre, que es donde se encuentran los peligros

python demo/freeboard.py ejecuta todo el proceso sin conexión a partir de respuestas grabadas:

stage      3.02 ft (GAGE:01646500)
crest      41 ft (NAVD88)

The two are both lengths, so nothing dimensional separates them:
  refused: cannot difference an elevation on NAVD88 against one on GAGE:01646500

The gage's zero is at 37.04 ft NAVD88, so the stage is 40.06 ft (NAVD88).
  freeboard = 0.94 ft

Ignoring the datum gives 37.98 ft of margin where 0.94 ft is correct,
overstating it by a factor of 40.

Un nivel y una elevación levantada son ambas longitudes en pies, y restar una de la otra devuelve un número que parece un resguardo libre. El error es silencioso, actúa en la dirección de informar un dique como seguro, y ninguna biblioteca de unidades lo previene porque nada en las unidades está mal.

Pronósticos

Los umbrales de inundación provienen del Servicio Nacional de Predicción del Agua de NOAA, ya que un nivel no significa nada hasta que se compara con el nivel al que el río se desborda. Tres cosas en esa carga útil necesitan manejo y ninguna está señalizada:

El caudal aparece como cfs en las categorías de inundación y como kcfs en el bloque de estado de la misma respuesta, así que un llamante que lee ambos y los trata por igual se equivoca por un factor de mil.

Los umbrales que nunca se establecieron se publican como -9999 en lugar de omitirse. Eso es dimensionalmente válido, plausible solo en signo, y pasa todas las verificaciones posteriores, así que se lee como el centinela que es.

Los niveles están sobre el datum propio de la estación, no uno nacional. El nivel observado publicado allí coincide con el parámetro 00065 de USGS en la misma estación y hora exactamente, que es la evidencia de esa lectura, y es por eso que un nivel de inundación se compara contra una altura de estación pero no contra una elevación levantada.

La red fluvial

La navegación sigue el río en lugar de un radio, que es la distinción que hace útil la respuesta: una estación a dos millas en la cuenca vecina no está aguas arriba de nada aquí. Las direcciones son palabras en lugar de los códigos de dos letras del índice, así que upstream incluye afluentes y upstream_main sigue solo el cauce principal.

Una cuenca llega como un polígono de un par de miles de pares de coordenadas. Esa es la respuesta a una pregunta cartográfica y la respuesta equivocada a toda pregunta que un agente hace, así que el polígono se conserva y se informan su área, extensión y número de vértices. El área se calcula desde el polígono mediante la forma integral de línea del área esférica, que no necesita proyección y por tanto no tiene zona que elegir ni en la que equivocarse. Coincide con el área de drenaje que USGS publica para la única estación donde ambas cifras existen en un 0,06%, y el resultado dice que es calculada en lugar de publicada, así que no se cita contra una cifra levantada como si fueran lo mismo.

Reproducción

La hidrología se reproduce al 1,6% en la literatura probada. La explicación habitual es que los datos y el código no se publicaron, y oculta el fallo más interesante: una cadena de procesamiento publicada contra un servicio en vivo tampoco se reproduce, porque el servicio revisó el registro debajo de ella. El caudal provisional se convierte en caudal aprobado y el número se mueve.

Una sesión guarda un paquete de su manifiesto y los cuerpos de respuesta que vio. Reproducirla se ejecuta en tres modos, y la distinción entre ellos es el punto.

modo

procedimiento

aísla

offline

recalcular a partir de los cuerpos archivados

cambios de código y biblioteca

strict

volver a obtener, exigir respuestas idénticas

cualquier desviación

revision_aware

volver a obtener, comparar, pedir al servicio que explique cada diferencia

revisión de datos

with Session(question="what was the discharge in mid May 2021?") as work:
    Toolkit(work).get_series("USGS-02344872", "00060", "2021-05-16", "2021-05-20")
    work.save("bundle.json")
gagelink-replay bundle.json --mode strict
gagelink-replay bundle.json --mode revision_aware

El mismo paquete, la misma re-obtención y dos veredictos diferentes:

strict replay: changed
  changed      daily
      [changed] USGS-02344872 00060 at 2021-05-16: 702.1 -> 826.0 ft^3/s

revision_aware replay: reproduced
  changed      daily
      [revised] USGS-02344872 00060 at 2021-05-16: 702.1 -> 826.0 ft^3/s,
                Revisions: Discharge for the period May 16, 2021 to Oct. 27, 2021,
                was revised on Aug. 16, 2024, based on changes to the estimated discharge.

Un resultado que cambió porque la agencia revisó 400 valores provisionales es un hecho distinto sobre la ciencia que uno que cambió porque el código cambió, y ambos son indistinguibles por lo demás. El registro de revisiones proviene de la colección time-series-revisions del propio servicio, unida por el identificador de serie temporal que la lectura ya lleva, por lo que la atribución es una consulta en lugar de una suposición. Una diferencia sin una revisión publicada detrás se mantiene reportada como no explicada, lo que evita que la comprobación sea vacua.

Los cuerpos se verifican contra sus hashes antes de comparar nada. Un paquete cuyo archivo no coincide se rechaza en lugar de reproducirse, ya que cada veredicto se basa en que el archivo sea lo que la sesión realmente vio.

waterbench

bench/ es un punto de referencia que mide cuánto vale el kit de herramientas para un modelo, en nueve tareas en una estación que cubren nueve peligros, todos ellos observados en una carga útil de servicio en vivo mientras se construía el paquete.

Se comparan tres condiciones con datos idénticos, que difieren solo en la interfaz entre el modelo y los bytes:

condición

lo que recibe el modelo

http_only

una herramienta de fetch que devuelve el JSON propio del servicio, que es lo que un desarrollador tiene hoy

toolkit_plain

las once herramientas con los resultados reducidos a magnitudes simples y las notas eliminadas

toolkit

las herramientas tal como están, con unidades, datums, calidad, obsolescencia y las notas

La condición intermedia es lo que hace que la medición valga la pena. Sin ella, una diferencia entre la primera y la última solo mostraría que la recuperación estructurada supera al JSON bruto, lo que nadie duda. La diferencia entre las dos últimas es lo que valen los metadatos por sí solos.

python -m bench --dry-run                                   # no provider, no spend
python -m bench --model anthropic/claude-opus-5 --replicates 4

Cada respuesta esperada se deriva de las mismas respuestas registradas que sirven las herramientas, y tests/test_bench.py resuelve cada tarea a partir de esas respuestas y comprueba el resultado contra la respuesta declarada. Una tarea cuya respuesta no se puede alcanzar de esa manera es una tarea rota, y ahí es donde se manifiesta. Cada tarea también registra una basis que indica de dónde proviene su cifra, para que un lector pueda comprobarlo sin tomar la palabra de este proyecto.

La tarea de francobordo se puede comprobar con la aritmética propia de la agencia: USGS publica la elevación de la superficie del agua como parámetro 63160, a 40.07 ft NAVD88, que es la altura del medidor de 3.03 ft más el desfase del datum de la estación de 37.04 ft.

La puntuación se escribe antes de que se ejecute cualquier barrido y está en el control de versiones, por lo que una regla no se puede ajustar después de ver un resultado que le desfavorece.

Primeros resultados

gpt-oss-120b, nueve tareas, tres condiciones, ocho réplicas, 216 ejecuciones, $0.09.

condición

correcto

http_only

61/72

toolkit_plain

63/72

toolkit

70/72

La corrección cuenta cada ejecución, incluidas las ocho que terminaron sin respuesta alguna. Siete de ellas pertenecen a http_only en las dos tareas cuyo registro bruto alcanza 42,000 y 50,000 tokens de prompt, donde el modelo degenera en repetir un número en lugar de responder. Esos fallos son causados por la condición, por lo que excluirlos daría crédito al JSON crudo por las ejecuciones que su propio tamaño de carga destruyó.

La suite se queda en el techo en seis de nueve tareas, lo que es un hallazgo sobre la suite. Donde se separa:

tarea

http_only

toolkit_plain

toolkit

peak_fraction, un registro largo

3/8

8/8

8/8

record_peak, un registro largo

6/8

8/8

8/8

forecast_flow, una unidad opaca

8/8

1/8

7/8

En las dos tareas de registro largo, la mediana del prompt fue de 49,864 y 42,006 tokens a través del JSON crudo frente a 5,462 y 2,384 a través del kit de herramientas. En la tarea de unidad opaca, eliminar los marcos de referencia envió siete de ocho ejecuciones a la trampa registrada, respondiendo con el caudal USGS de 3010 ft³/s en lugar de los 2.95 kcfs del servicio de pronóstico.

El kit de herramientas no supera al JSON crudo en precisión en general. La brecha está impulsada casi por completo por las dos tareas donde la carga cruda no cabe. Ocho de 27 celdas se dividen entre sus réplicas, por lo que las diferencias por debajo de aproximadamente dos ejecuciones de ocho no se separan con este diseño, y un modelo es un modelo.

Claves de API y límites de tasa

El servicio permite 50 solicitudes por IP por hora sin autenticación y 1,000 por hora con una clave, que es gratuita desde api.waterdata.usgs.gov/signup. Una sola pregunta de agente que compara condiciones en cinco sitios cuesta aproximadamente de 15 a 25 solicitudes, por lo que el almacenamiento en caché es más de carga que una optimización, y las respuestas se guardan en caché durante la vida del proceso por defecto.

El saldo restante se lee de X-RateLimit-Remaining en cada respuesta y se usa en la recuperación, para que se pueda informar a un agente de lo que le queda en lugar de descubrir el límite al fallar.

La clave viaja en el encabezado X-Api-Key y nunca aparece en una URL registrada, ya que un manifiesto está diseñado para ser publicado.

El servicio al que se dirige

USGS está retirando la familia de API WaterServices, con la retirada programada para el primer trimestre de 2027 y una posible degradación a partir de la segunda mitad de 2026. gagelink apunta únicamente al reemplazo en api.waterdata.usgs.gov/ogcapi/v0. Una consecuencia que vale la pena mencionar es la colección time-series-revisions, que publica cambios y eliminaciones en el registro aprobado, y que es lo que permitirá a una reproducción separar una respuesta que cambió porque la agencia revisó una medición de una que cambió porque el código cambió.

Desarrollo

python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest

El acceso a la red pasa por un fetch reemplazable, por lo que la suite se ejecuta contra respuestas registradas y ninguna prueba necesita la red.

Licencia

MIT

Install Server
A
license - permissive license
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides access to real-time water data from the USGS Water Services API, allowing users to fetch instantaneous measurements like stream flow, gage height, temperature, and water quality parameters from thousands of monitoring stations across the US.
    3
  • A
    license
    B
    quality
    C
    maintenance
    Provides access to UK Environment Agency's real-time flood monitoring data, enabling users to check flood warnings, monitor water levels and flow rates, and access historical measurements from monitoring stations across the UK.
    11
    11
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides real-time hydrological data from Korea's Flood Control Office via MCP protocol, optimized for AI assistants with features to prevent infinite loop calls and standardize data structures.
  • A
    license
    A
    quality
    B
    maintenance
    Enables querying USGS water data including real-time and historical streamflow, gage height, and water temperature from USGS gauges across the United States.
    3
    MIT

View all related MCP servers

Related MCP Connectors

  • US weather, alerts, earthquakes and elevation for AI agents, from NWS/NOAA and USGS. No API keys.

  • US weather & geo for AI agents: forecasts, alerts, earthquakes, elevation, geocoding. No keys.

  • Query real-time and historical USGS water data from ~8,000 stream gages and groundwater wells.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Adeniyikayodee/gagelink'

If you have feedback or need assistance with the MCP directory API, please join our Discord server