gagelink
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 gagelinkRelated 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") # DatumConversionUnavailableEsa ú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 valueLa 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 |
| buscar por estado, condado, unidad hidrológica, tipo de sitio o caja delimitadora |
| metadatos, datum, zona horaria y el desfase que un nivel necesita |
| valor más reciente por parámetro, con antigüedad y calidad |
| un rango de fechas, como identificador más un resumen |
| reducir una serie almacenada sin volver a obtenerla |
| registro anual de caudales máximos |
| nivel observado y pronosticado, con umbrales de inundación |
| ubicaciones de monitoreo aguas arriba o aguas abajo a lo largo del río |
| el área que drena hasta un punto |
| 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 |
| recalcular a partir de los cuerpos archivados | cambios de código y biblioteca |
| volver a obtener, exigir respuestas idénticas | cualquier desviación |
| 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_awareEl 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 |
| una herramienta de fetch que devuelve el JSON propio del servicio, que es lo que un desarrollador tiene hoy |
| las once herramientas con los resultados reducidos a magnitudes simples y las notas eliminadas |
| 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 4Cada 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 |
| 61/72 |
| 63/72 |
| 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 |
|
|
|
| 3/8 | 8/8 | 8/8 |
| 6/8 | 8/8 | 8/8 |
| 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/pytestEl 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
Maintenance
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
- FlicenseNot gradedqualityDmaintenanceProvides 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
- AlicenseBqualityCmaintenanceProvides 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.1111MIT
- FlicenseNot gradedqualityDmaintenanceProvides 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.
- AlicenseAqualityBmaintenanceEnables querying USGS water data including real-time and historical streamflow, gage height, and water temperature from USGS gauges across the United States.3MIT
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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