Last9
oficialIntegra sin problemas el contexto de producción en tiempo real (logs, métricas y trazas) a tu entorno local para corregir código más rápido de forma automática.
¿Qué puedes hacer con Last9 MCP?
- Investigar incidentes en producción — Pídele a tu asistente que recopile registros, trazas, métricas, excepciones y alertas activas de un servicio para que pueda diagnosticar qué falló.
- Correlacionar cambios con su impacto — Revisa las implementaciones y los cambios de configuración; luego compara un servicio y sus clientes antes y después para ver si algún cambio provocó una regresión.
- Consultar métricas de Prometheus — Ejecuta consultas PromQL de rango o instantáneas y enumera los valores de las etiquetas para explorar cualquier métrica de tu fuente de datos.
- Inspeccionar el rendimiento de la base de datos — Descubre bases de datos, encuentra ejecuciones de consultas lentas y revisa patrones de consulta y métricas del servidor a partir de spans de OTel.
- Analizar trazas y perfiles — Obtén el diagrama de cascada de una traza por ID, clasifica las desviaciones de atributos entre cohortes o recupera un gráfico de llamas y las funciones principales de un servicio.
- Gestionar alertas y paneles — Revisa la cobertura y el estado de las alertas, crea o modifica reglas de alerta y enumera, valida o edita paneles personalizados.
Documentación
Last9 MCP Server

Tu agente de IA no sabe qué está fallando en producción. Esto lo soluciona.
Last9 MCP Server conecta Claude, Cursor, Windsurf y cualquier otro asistente de IA compatible con MCP directamente con tus datos de observabilidad de producción: registros, métricas, trazas, excepciones, consultas a bases de datos, alertas y despliegues. El agente deja de hacer conjeturas y empieza a leer las señales reales.
Empieza en 30 segundos (alojado)
No hace falta instalar ningún binario. No hay tokens que gestionar. Una URL, OAuth en el navegador y listo.
Busca el slug de tu organización en tu URL de Last9: app.last9.io/<org_slug>/...
Claude Code
claude mcp add --transport http last9 https://app.last9.io/api/v4/organizations/<org_slug>/mcp
Escribe /mcp, selecciona last9 y autentícate. Eso es todo.
Cursor
Settings > MCP > Add New MCP Server:
{
"mcpServers": {
"last9": {
"type": "http",
"url": "https://app.last9.io/api/v4/organizations/<org_slug>/mcp"
}
}
}
Haz clic en Connect y completa la autenticación OAuth.
VS Code
Requiere la versión 1.99 o posterior. Abre la paleta de comandos → MCP: Add Server, pega la URL y autentícate.
O directamente en settings.json:
{
"mcp": {
"servers": {
"last9": {
"type": "http",
"url": "https://app.last9.io/api/v4/organizations/<org_slug>/mcp"
}
}
}
}
Windsurf
Settings > Cascade > Open MCP Marketplace > gear icon (mcp_config.json):
{
"mcpServers": {
"last9": {
"serverUrl": "https://app.last9.io/api/v4/organizations/<org_slug>/mcp"
}
}
}
Claude Web/Desktop
Settings > Connectors > Add custom connector. Asígnale el nombre last9, pega la URL y autentícate.
Se requiere acceso de administrador a tu organización de Claude.
Autohospedado (STDIO)
Úsalo cuando tu cliente MCP no admita el transporte HTTP o cuando necesites ejecutar el servidor localmente.
Instalación
Homebrew:
brew install last9/tap/last9-mcp
NPM:
Requiere Node.js 22.23.4+, 24.22.1+ o 26.12.1+: las versiones anteriores aceptan certificados TLS de los intermediarios eMudhra comprometidos cuando el instalador descarga el binario.
npm install -g @last9/mcp-server@latest
# or directly:
npx -y @last9/mcp-server@latest
Versiones binarias (Windows / instalación manual):
Descárgalas desde GitHub Releases:
| Plataforma | Archivo |
|---|---|
| Windows (x64) | last9-mcp-server_Windows_x86_64.zip |
| Windows (ARM64) | last9-mcp-server_Windows_arm64.zip |
| Linux (x64) | last9-mcp-server_Linux_x86_64.tar.gz |
| Linux (ARM64) | last9-mcp-server_Linux_arm64.tar.gz |
| macOS (x64) | last9-mcp-server_Darwin_x86_64.tar.gz |
| macOS (ARM64) | last9-mcp-server_Darwin_arm64.tar.gz |
Obtener un token de actualización
Solo los administradores pueden crear tokens.
- Ve a API Access
- Haz clic en Generate Token con permisos de escritura
- Cópialo
Configuración del cliente
Homebrew:
{
"mcpServers": {
"last9": {
"command": "/opt/homebrew/bin/last9-mcp",
"env": {
"LAST9_REFRESH_TOKEN": "<your_refresh_token>"
}
}
}
}
NPM:
{
"mcpServers": {
"last9": {
"command": "npx",
"args": ["-y", "@last9/mcp-server@latest"],
"env": {
"LAST9_REFRESH_TOKEN": "<your_refresh_token>"
}
}
}
}
Dónde pegarlo:
| Cliente | Ubicación |
|---|---|
| Claude Web/Desktop | Settings > Developer > Edit Config (claude_desktop_config.json) |
| Cursor | Settings > Cursor Settings > MCP > Add New Global MCP Server |
| Windsurf | Settings > Cascade > MCP Marketplace > gear icon (mcp_config.json) |
| VS Code | Envuélvelo en { "mcp": { "servers": { ... } } } en settings.json — detalles |
Configuración STDIO de VS Code
{
"mcp": {
"servers": {
"last9": {
"type": "stdio",
"command": "/opt/homebrew/bin/last9-mcp",
"env": {
"LAST9_REFRESH_TOKEN": "<your_refresh_token>"
}
}
}
}
}
Para NPM: usa "command": "npx" y añade "args": ["-y", "@last9/mcp-server@latest"].
Windows
Después de descargarlo desde GitHub Releases, extráelo e indica la ruta completa:
{
"mcpServers": {
"last9": {
"command": "C:\\Users\\<user>\\AppData\\Local\\Programs\\last9-mcp-server.exe",
"env": {
"LAST9_REFRESH_TOKEN": "<your_refresh_token>"
}
}
}
}
La opción de NPM es más sencilla en Windows: no hay que gestionar rutas.
Variables de entorno
| Variable | Valor predeterminado | Descripción |
|---|---|---|
LAST9_REFRESH_TOKEN | (obligatorio) | Token de actualización de API Access |
LAST9_DATASOURCE | organización predeterminada | Nombre de la fuente de datos o del clúster; útil si tienes varios clústeres de Levitate |
LAST9_FAKE_NOW_ISO | (sin definir) | Solo para evaluaciones/reproducciones; nunca lo establezcas en producción. Instante RFC3339 (p. ej., 2026-01-02T15:04:05Z) que sustituye la hora actual al resolver intervalos relativos como lookback_minutes, para que las ejecuciones sean reproducibles. Un valor no válido provoca un error de inicio. Los tiempos de espera, las cachés y la caducidad de los tokens siguen usando la hora real |
LAST9_API_HOST | app.last9.io | Anula el host de la API |
LAST9_TOOLSETS | todas las herramientas | Conjuntos de herramientas separados por comas que se expondrán (logs, traces, metrics, alerts, dashboards, profiles, grafana, investigate, all). Alias: LAST9_MCP_TOOLSETS |
LAST9_MCP_READ_ONLY | false | Establece true para servir solo herramientas de lectura; consulta Modo de solo lectura. Un valor que no sea booleano provoca un error de inicio. Alias: LAST9_READ_ONLY |
LAST9_MCP_ALLOW_WRITE_TOOLS | — | Herramientas de escritura separadas por comas que se siguen sirviendo en modo de solo lectura. Alias: LAST9_ALLOW_WRITE_TOOLS |
LAST9_MAX_GET_LOGS_ENTRIES | 5000 | Número máximo de entradas para solicitudes get_logs fragmentadas |
LAST9_USE_LOG_SEARCH_API | false | Establece true para responder a get_logs y get_service_logs con una sola llamada de búsqueda en el servidor en lugar de fragmentar en el cliente. Cada llamada dispone de 90 segundos y, después, devuelve recomendaciones para acotar el intervalo; los errores 502 o 503 se reintentan una vez dentro de ese plazo |
LAST9_DEBUG_CHUNKING | false | Establece true para registrar detalles de planificación de fragmentos para get_logs, get_service_logs, get_traces |
LAST9_DISABLE_TELEMETRY | true | Establece false para habilitar el trazado interno de OTel |
OTEL_SDK_DISABLED | — | Variable de entorno estándar de OTel. Anula LAST9_DISABLE_TELEMETRY |
OTEL_EXPORTER_OTLP_ENDPOINT | — | Punto de conexión del colector OTLP (solo cuando la telemetría está habilitada) |
OTEL_EXPORTER_OTLP_HEADERS | — | Encabezados de autenticación OTLP (solo cuando la telemetría está habilitada) |
Tiempos por fase de las herramientas
Con la telemetría interna habilitada, los spans de las herramientas incluyen last9.phase.<phase>.duration_ms
(milisegundos de tiempo real transcurrido) y last9.phase.<phase>.failed (si esa fase
devolvió un error). Los mismos campos se emiten como registros de depuración
tool phase completed, junto con tool y phase, cuando el registrador configurado acepta registros de depuración.
Estos tiempos no añaden entradas, texto de consultas, credenciales ni cuerpos de errores.
| Herramienta | Fases |
|---|---|
get_log_patterns | sample: obtener y decodificar la muestra; mine: extracción de patrones; deep_link: resolver el índice opcional y crear el enlace |
get_logs | fetch: obtener, decodificar y combinar resultados, incluidos los reintentos y las comprobaciones opcionales de coherencia de recuentos; count_sanity: la comprobación opcional anidada; post_process: recomendaciones, resumen y formato JSON; deep_link: resolución del índice y creación del enlace |
get_service_profile | request: preparar y enviar la solicitud mediante el cliente de API compartido y leer la respuesta completa, antes del formato breve |
Solo se registran las fases ejecutadas, incluidas las obtenciones fallidas. Los ayudantes opcionales que suprimen errores mantienen ese comportamiento; failed: false no garantiza que haya estado disponible un enlace opcional o una comprobación de recuentos. La duración anidada de count_sanity ya está incluida en fetch; no la sumes dos veces. El tiempo de obtención incluye el trabajo del cliente, por lo que no mide únicamente la ejecución del backend. Compara estas fases en intervalos y rutas de búsqueda equivalentes antes de ajustar el muestreo o el almacenamiento en caché.
Para investigar la latencia, reproduce la misma combinación de servicio, fuente de datos, índice, intervalo y límites en las compilaciones de referencia y candidata. Registra el SHA de la compilación, el total de la herramienta, las duraciones de las fases, last9.logs.search_path, los atributos de reintento cuando estén presentes y los indicadores de cobertura. Compara tanto las llamadas lentas como las habituales. Una obtención lenta o una solicitud de perfil lenta identifica una fase de red/API que conviene investigar; se necesitan pruebas correlacionadas propias para atribuir la cola de latencia al backend. Estos diagnósticos por sí solos no demuestran una mejora de latencia.
Modo de solo lectura
--read-only (o LAST9_MCP_READ_ONLY=true) sirve únicamente herramientas anotadas con readOnlyHint: true, por lo que ninguna herramienta puede crear, modificar o eliminar reglas de alerta, canales de notificación, paneles o reglas de descarte, confirmar alertas, reasignarlas ni silenciar grupos. Una herramienta sin anotaciones se considera una herramienta de escritura y queda oculta.
Para conservar algunas herramientas de escritura, enuméralas con --allow-write-tools (o LAST9_MCP_ALLOW_WRITE_TOOLS):
last9-mcp --read-only --allow-write-tools=acknowledge_alert,mute_alert_group
- El servidor se niega a iniciarse si un nombre permitido no corresponde a una herramienta de escritura (por un error tipográfico o porque sea el nombre de una herramienta de lectura); el error enumera las herramientas de escritura válidas.
--allow-write-toolssin--read-onlyprovoca un error de inicio, porque ya se sirven todas las herramientas de escritura.- El modo de solo lectura se combina con
--toolsets: una herramienta solo se sirve si cumple ambas condiciones.--toolsets=alerts --read-only --allow-write-tools=mute_alert_groupsirve las herramientas de lectura de alertas ymute_alert_group; una herramienta de escritura permitida que no pertenezca a los conjuntos de herramientas seleccionados permanece oculta. - El modo
--httpydump-toolsrespetan los mismos indicadores y variables de entorno:go run . dump-tools --read-onlymuestra la superficie de solo lectura, junto con las instrucciones del servidor que sirve eninstructions. - Las descripciones, los esquemas y los resultados de las herramientas de lectura, así como las instrucciones del servidor, nunca mencionan una herramienta de escritura oculta. Una herramienta de escritura permitida aún puede mencionar a sus herramientas relacionadas ocultas (
create_alertcrea una regla deshabilitada quepatch_alerthabilita), así que permite ambas herramientas a la vez.
Qué puede hacer
Orientación
get_workspace_overview— Primera llamada recomendada cuando una solicitud no especifica ningún servicio ni herramienta (si especifica uno, se dirige directamente a esa herramienta): entornos, señales detectadas durante la última hora (registros, trazas, métricas), recuentos de servicios y grupos de alertas, alertas activas y hora actual, todo en una sola llamada económica. Una señal que no se haya detectado durante esa hora puede tener datos anteriores, por lo que sigue siendo consultable. Si falla una subllamada, su campo queda comonully se añade una entradaerrors[]. Disponible en todos los conjuntos de herramientasget_current_time— Hora actual y límites absolutos paralast 2h,yesterdayosince yesterday 9am IST, en cualquier zona IANA o abreviatura común. Disponible en todos los conjuntos de herramientas
Estado de los servicios
get_service_summary— Filas de flota(service, env)ordenadas: request_count del intervalo, throughput_rpm, recuentos de errores HTTP 4xx/5xx y recuentos de errores gRPCget_service_environments— Entornos disponibles para tus servicios. Ejecútala primero: otras herramientas de APM necesitanenvde aquíget_service_performance_details— Desglose completo: rendimiento, tasa de errores, p50/p90/p95/promedio/máximo, apdex y disponibilidad (resumido de forma predeterminada; serie completa coninclude_series)get_service_operations_summary— Operaciones agrupadas por endpoints HTTP, llamadas a bases de datos, mensajería y clientes HTTPget_service_dependency_graph— Mapa de dependencias con rendimiento, latencia y tasas de error para upstream/downstream/infraestructuraget_apm_service_deviations— Compara un intervalo actual con una línea base de igual duración: regresiones/mejoras, conciliación de Apdex y un resultado final (flota o servicio individual)get_exceptions— Excepciones del servidor con filtros de servicio y span
Observabilidad de bases de datos
Cuatro herramientas que analizan directamente el rendimiento de tu base de datos, a partir de spans de trazas de OpenTelemetry y, cuando no hay trazas, de métricas de infraestructura como CloudWatch. No hace falta instrumentación adicional si ya usas OTel.
get_databases— Descubre todas las bases de datos de tu infraestructura: tipo de DB, host, rendimiento (consultas/min), latencia p95, tasa de errores y número de servicios dependientes. También descubre bases de datos a partir de métricas de infraestructura, como CloudWatch, sin necesidad de instrumentar trazas; esas filas incluyen un valor de actividad en lugar de métricas de trazasget_database_slow_queries— Las ejecuciones de consultas realmente más lentas, ordenadas por duración, con ID de traza para consultar las trazas completasget_database_queries— Patrones y agregados de consultas: frecuencia de ejecución, duración media/p95 y tasa de erroresget_database_server_metrics— Métricas del lado del servidor obtenidas directamente del host de la DB (CPU, conexiones, tasas de aciertos de búfer; depende del sistema de DB)
Admite PostgreSQL, MySQL, MongoDB, Redis, Aerospike y cualquier otro sistema cuyas trazas de OTel tengan un atributo db_system, además de bases de datos descubiertas a partir de métricas de infraestructura, como CloudWatch, cuyas filas incluyen un valor de actividad en lugar de métricas de trazas.
Prometheus / PromQL
prometheus_range_query— Consultas de rango PromQL para cualquier métricaprometheus_instant_query— Consultas instantáneas; usa funciones de agregación temporal comoavg_over_time,sum_over_timeprometheus_label_values— Valores de etiquetas para una serie determinadaprometheus_labels— Todas las etiquetas disponibles para una serie
Dirige estas consultas a una fuente de datos o clúster distinto del predeterminado configurando LAST9_DATASOURCE, o por llamada con datasource. Si se omite datasource, se usa el valor predeterminado; una coincidencia aproximada única que el comparador considere segura (como una variante de mayúsculas y minúsculas) usa la coincidencia más cercana. No se garantiza que una diferencia de una letra, una letra adicional o una letra transpuesta se resuelva automáticamente: incluso un único candidato puede requerir confirmación si el cambio podría afectar a un código de región o entorno. Las coincidencias más imprecisas o ambiguas devuelven candidatos y no ejecutan ninguna consulta (no es un error); los nombres desconocidos devuelven un error. Si no indicaste el nombre exacto de la fuente de datos, un bloque de contenido adicional indica cuál se usó. get_metric_status, get_service_profile y get_apm_service_deviations resuelven datasource de la misma manera.
Registros
get_logs— Consultas de registros con canalización JSON completa (agregaciones, filtros, extracción de campos)get_service_logs— Líneas de registro de un servicio, filtrables por gravedad y contenido del cuerpo; se resumen en grupos de 20 líneas, a menos queresponse_modesearawget_log_patterns— Líneas de registro de un servicio en un intervalo, agrupadas en plantillas de mensajes, con cantidad, primera/última aparición, distribución de gravedad y una muestraget_emerging_log_patterns— Plantillas de registros nuevas, aumentadas, reducidas o desaparecidas en comparación con un intervalo de referencia, con cantidades de ambos intervalosget_log_attributes— Catálogo global de atributos del esquema de registros para un intervalo de tiempoget_log_attributes_for_pipeline— Campos de registro presentes realmente en una canalización en curso (descubrimiento con alcance limitado), cada uno con sufilter_fieldexactoget_drop_rules— Reglas para descartar registros de Last9 Control Planeadd_drop_rule— Crea una regla de descarte para reducir el volumen de registros en el origenget_remapping_rules— Reglas de reasignación (logs_extract,logs_map,traces_map) de Last9 Control Planeadd_remapping_rule— Crea una regla de reasignación que extraiga o asigne atributos de registros y trazas durante la ingesta
Trazas
get_traces— Consultas de trazas con canalización JSON para búsquedas y agregaciones ampliasget_service_traces— Trazas por ID de traza exacto o nombre de servicio. Úsalo cuando tengas un ID de traza: es más rápidoget_trace_attributes— Catálogo global de atributos del esquema de trazasget_trace_attributes_for_pipeline— Atributos presentes realmente en una canalización en curso (descubrimiento con alcance limitado), cada uno con sufilter_fieldexactoget_trace_attribute_values— Valores distintos de un atributo de traza, opcionalmente limitados a una canalizaciónget_trace_attribute_deviations— Clasifica los valores de atributos que difieren entre dos cohortes de spans delimitadas (lentas frente a rápidas, con error frente a sin error, o dos intervalos de tiempo). Correlación, no causalidadget_trace_waterfall— Una traza exacta como cascada de relaciones padre/hijo, con tiempo propio de unión de intervalos, spans más lentos y advertencias del grafo
Eventos de cambio y alertas
get_change_events— Despliegues, cambios de configuración y reversiones. Correlaciona incidentes con los cambios realizadosanalyze_change_impact— ¿Un cambio causó algún problema? Compara un servicio y sus llamadores directos antes y después de un cambio: señales de oro, desviaciones de APM y atributos de trazas, nuevas alertas activadas y un veredicto (regression likely/no significant change/insufficient data)get_alert_groups— Grupos de alertas de Compass configurados, con etiquetas de metadatos, equipo, nivel y cantidad de reglas; incluye grupos sin reglas y grupos que no están activándoseget_alert_operations_summary— Estado de guardia durante un intervalo: alertas generadas frente a reconocidas, acumulación pendiente, entregas y, coninclude_rules, las reglas más ruidosas, inestables y sin enrutamiento. Cada cantidad incluye sustatus(available/partial/unavailable)get_alert_coverage— Aquello sobre lo que no estás generando alertas: señales de oro sin cobertura, grupos de alertas totalmente desactivados o sin enrutamiento y reglas silenciadas durante demasiado tiempo, cada uno con una herramienta de corrección sugerida. Solo lectura.get_alert_config— Configuraciones de reglas de alerta, con búsqueda por nombre, gravedad, tipo y etiquetasget_entity_kpis— Todos los KPI (id, nombre, PromQL, unidad) de un grupo de alertas, incluidos los KPI que aún no usa ninguna regla; los idcreate_alertyvalidate_alert_expressionnecesitancreate_kpi— Crea un KPI (PromQL + unidad) en un grupo de alertas cuando no haya ningún KPI existente adecuado. Créalo una sola vez y conserva el id devueltoget_alerts— Alertas que se están activando actualmente dentro de un intervalo de tiempoget_alert_rule_state— Estado histórico de activación (1/0) por regla de alerta durante un intervalo de tiempo, agrupado porrule_id. Se puede filtrar por grupo de alertas, nombre de regla, filtros de etiquetas y estado.get_alert_hygiene— Reglas de alerta ruidosas, inestables, obsoletas y sin enrutamiento durante 7 o 30 días, clasificadas y con las cifras que respaldan el veredicto y una corrección sugerida. Solo lectura.get_notification_channels— Canales de notificación configurados (Slack, PagerDuty, correo electrónico, etc.).scope=channelsmuestra los canales (id para adjuntarlos); el valor predeterminadoattachedmuestra las asociaciones por grupo de alertasrecommend_alert_config— Orientación de diseño de solo lectura (ciclo de vida, etapas de validación, contexto para quienes responden) antes de crear una regla de alertavalidate_alert_expression— Ejecuta una expresión de alerta de prueba con los KPI de un grupo de alertas sin guardar nadadescribe_alert_chart— Solo lectura: las señales que pueden generar alertas (clave, unidad) en un gráfico de Discover (discover-service,discover-exceptions)create_alert— Crea una regla de alerta (estática o adaptativa medianteadaptive_std_cmp) en un grupo de alertas existente; siempre se crea desactivada como borrador. Créala una sola vez y ajústala conupdate_alert/patch_alertcreate_alert_from_chart— Crea una regla de alerta de umbral estático a partir de un gráfico de Discover en una sola llamada. Al igual quecreate_alert, la regla se crea desactivada como borradorupdate_alert— Reemplazo completo de la configuración de evaluación de una regla (puede devolver un id de regla de reemplazo)patch_alert— Cambio parcial de una regla: activar/pausar,mute_until, responsableproperties,recurring_mute_schedulesemanal (oclear_recurring_mute_schedule)delete_alert— Elimina permanentemente una regla de alertamute_alert_group— Silencia (o reactiva) todas las reglas de un grupo de alertas hasta una fecha límiteacknowledge_alert— Asume la responsabilidad de una alerta activa o libéralareassign_alert— Asigna una alerta ya reconocida a otro usuariocreate_notification_channel— Crea un canal de Slack, PagerDuty, Opsgenie, correo electrónico o webhook genéricoattach_notification_channel— Enruta un canal existente a un grupo de alertas parabreach,threato ambosdetach_notification_channel— Elimina una asociación entre un canal y un grupo de alertasdelete_notification_channel— Elimina permanentemente un canal que no tenga asociaciones
Las herramientas de escritura de alertas (create_kpi, create_alert, update_alert, patch_alert, delete_alert, create_alert_from_chart, mute_alert_group, acknowledge_alert, reassign_alert, create_notification_channel, attach_notification_channel, detach_notification_channel, delete_notification_channel) necesitan un token con permisos de escritura; delete_alert, detach_notification_channel y delete_notification_channel necesitan permisos de eliminación. Solo están disponibles en el conjunto de herramientas alerts.
Las reglas, los canales y las asociaciones creados por estas herramientas se marcan como created_via: last9-mcp, junto con el nombre y la versión del cliente MCP (en las reglas, las anotaciones _l9_created_via_ y _l9_created_via_client_, que patch_alert y update_alert conservan y que los llamadores no pueden establecer). get_entity_alert_rules y get_notification_channels muestran la marca, y delete_alert, detach_notification_channel y delete_notification_channel indican a los agentes que limpien solo los recursos marcados, a menos que el usuario nombre uno.
Paneles personalizados
list_dashboards— Paneles personalizados filtrados por nombre y paginados con un límite de 32 KB: ID, nombres, fechas de última actualización, autores y estado de bloqueoget_dashboard— Definición completa de un panel por ID, incluidos los paneles y las consultaspreview_dashboard— Vista previa de solo lectura de un panel o parche sin guardar: consultas resueltas, resultados acotados por panel y diferencias netas entre paneles; nunca guardavalidate_dashboard— Lint + ejecución + clasificación de solo lectura para un id de panel guardado o undashboard_definitionintegrado, en un intervalo de ≤24h. Nunca crea ni actualiza panelescreate_dashboard— Crea una sola vez un panel personalizado completamente nuevo (paneles, consultas, metadatos). Una vez devuelto el id, edita los paneles conpatch_dashboard.update_dashboard— Reestructura la mayor parte de un panel existente por ID (reemplazo completo; los paneles del sistema de solo lectura devuelven un error). Para editar paneles individuales, usapatch_dashboardpatch_dashboard— Ediciones específicas de un panel existente por ID (añadir/actualizar/eliminar/mover un panel, establecer el título o una variable); devuelve una diferencia compacta, no el panel completodelete_dashboard— Elimina un panel personalizado por IDset_dashboard_lock— Bloquea o desbloquea un panel personalizado por ID (solo el autor o un administrador de la organización); un panel bloqueado rechaza las operaciones de actualización, parche y eliminaciónlist_dashboard_snapshots— Instantáneas congeladas de un panel en un momento determinado (solo metadatos)get_dashboard_snapshot— Instantánea congelada completa, incluidos los datos de los paneles, para análisis de causa raíz (RCA) o vistas compartiblesdelete_dashboard_snapshot— Elimina una instantánea congelada por ID
La creación de paneles está disponible mediante MCP con prompts/list y prompts/get, sin instalar una skill. Los tres prompts no aceptan argumentos:
create_dashboard_guide— Descubrimiento, diseño y selección de paneles; valida el candidato completo y ejecuta la consulta de cada panel antes de la aprobación, crea una sola vez, aplica parches para ediciones específicas y, después, vuelve a leerlo.dashboard_examples— Ejemplo completo que abarca RED de servicios, latencia de endpoints, clases de estado, estado de hosts, recuentos de registros y paneles de trazas, con variables dependientes.query_guide— Semántica de contadores, indicadores, histogramas y métricas de trazas derivadas; consultas instantáneas frente a consultas de rango; ventanas de tasa; límites de porcentajes y reglas de logjson/tracejson.
El mismo contenido se puede consultar en last9://reference/create_dashboard_guide, last9://reference/dashboard_examples y last9://reference/query_guide. Los prompts y recursos siguen siendo detectables en todos los conjuntos de herramientas; para ejecutar el flujo de trabajo se necesitan las herramientas pertinentes (para crear paneles con señales mixtas, usa --toolsets=dashboards,investigate, que también sirve para list_datasources). Las sesiones de solo lectura pueden preparar y validar, pero no guardar. Comprueba la disponibilidad de las herramientas antes de seguir un paso.
Las comprobaciones de paneles usan actualmente prometheus_range_query, prometheus_instant_query, get_logs y get_traces, junto con validate_dashboard. Los tipos de consulta no compatibles y los valores predeterminados simplificados del validador no garantizan que la representación sea idéntica. El paso independiente de comprobación de consultas cambiará a query_panel cuando se integre #475.
Perfilado continuo
Requiere que el perfilado continuo esté habilitado para la organización. Primero descubre los servicios con get_profile_services y, después, obtén un gráfico de llamas o una clasificación de funciones.
get_profile_services— Servicios que tienen datos de perfilado en el intervalo (índice antes de consultar)get_flamegraph— Árbol de flamegraph anidado para un servicio (cpupredeterminado; tambiénalloc,wall)get_top_functions— Clasificación de muestras propias de las funciones más activas de un servicioget_profile_summary— Análisis breve en lenguaje natural del perfil de un servicio
Paneles de Grafana
Herramientas de solo lectura para la instancia de Grafana de la organización (a través del proxy de Grafana de Last9). Los campos de credenciales nunca se devuelven al modelo. Actívalas con LAST9_TOOLSETS=grafana (o deja los conjuntos de herramientas sin definir para habilitar todas las herramientas).
grafana_search_dashboards— Buscar paneles por subcadena del título (paginado;truncated: truecuando se alcanza el límite)grafana_get_dashboard— Resumen del panel por uid (paneles, variables, destinos PromQL);full_json=truepara el JSON sin procesar de Grafanagrafana_list_folders— Árbol de carpetasgrafana_list_folder_dashboards— Paneles de una carpeta (paginado)grafana_list_datasources— Inventario de fuentes de datos sin credenciales
Resolución aproximada de nombres
did_you_mean— Cuando el agente no está seguro del nombre de una entidad, devuelve las coincidencias más cercanas del catálogo (servicios, entornos, hosts, bases de datos, implementaciones/espacios de nombres de K8s, trabajos). Hasta 3 sugerencias con puntuaciones de similitud. El servidor la llama automáticamente antes de la mayoría de las herramientas cuando una búsqueda de nombre no devuelve resultados.
Perfil del servicio
get_service_profile— Cómo es realmente la telemetría de un servicio antes de consultarla: qué señales existen, el lenguaje y el entorno de ejecución, los entornos de implementación, la estructura de sus registros y una solución de ingesta recomendada cuando corresponda. Permite que el agente omita las herramientas de trazas cuando un servicio no tiene trazas y que analice la gravedad a partir del cuerpo del registro cuandoSeverityTextestá vacío, en lugar de filtrarlo y no encontrar nada.
Cómo funciona
Enlaces profundos en cada respuesta. Cada herramienta devuelve un campo deep_link: una URL directa al panel de Last9 para esa consulta y ese intervalo de tiempo exactos. El agente puede proporcionarte el enlace; haces clic y llegas directamente.
Conjuntos de herramientas. De forma predeterminada, el servidor expone todas las herramientas. Los hosts de automatización que solo necesitan investigar (registros/trazas/métricas/perfiles) pueden establecer LAST9_TOOLSETS=investigate (o pasar --toolsets=investigate) para mantener reducido tools/list sin deshabilitar herramientas en masa desde el cliente. Paquetes con nombre: logs, traces, metrics, alerts, dashboards, profiles, grafana, investigate, all. Los nombres desconocidos generan un error de inmediato. get_current_time y get_workspace_overview están disponibles en todos los conjuntos de herramientas. En modo HTTP, una solicitud puede restringir aún más el conjunto con el encabezado X-Last9-Toolsets (consulta Conjuntos de herramientas por solicitud). El paquete alerts también ofrece las herramientas de solo lectura prometheus_range_query y get_dashboard, que invoca la instrucción author-alert. El paquete metrics por sí solo no incluye list_datasources ni did_you_mean: usa investigate (o combina conjuntos de herramientas) cuando necesites esas herramientas de descubrimiento.
Recursos de referencia de herramientas. Los manuales extensos de logjson/tracejson/service-logs/metrics son recursos MCP (last9://reference/logjson, last9://reference/tracejson, last9://reference/service_logs, last9://reference/metrics, last9://reference/investigation, last9://reference/alerting), no texto descriptivo de herramientas que siempre esté disponible. El manual de alertas abarca la gramática de expresiones de alertas, los campos estáticos y adaptativos, las unidades, qué comprueba validate_alert_expression y plantillas de reglas de latencia/tasa de errores/saturación. Las reglas de consulta críticas permanecen en la descripción de la herramienta para que los agentes que nunca llaman a resources/read sigan recibiendo instrucciones correctas para construir consultas. Descubre los campos específicos de la organización con get_log_attributes / get_log_attributes_for_pipeline (y sus equivalentes para trazas); no se insertan en las descripciones.
Resultados grandes por fragmentos. get_logs y get_traces gestionan conjuntos de resultados grandes mediante fragmentación, en lugar de truncarlos. El límite predeterminado es de 5000 entradas para los registros; se puede configurar mediante LAST9_MAX_GET_LOGS_ENTRIES.
Desarrollo
Modo HTTP, pruebas con curl y compilación desde el código fuente
Ejecutar en modo HTTP
export LAST9_REFRESH_TOKEN="your_refresh_token"
export LAST9_HTTP=true
export LAST9_PORT=8080
./last9-mcp-server
El servidor se inicia en http://localhost:8080/mcp.
Probar con curl
El controlador HTTP Streamable se ejecuta en modo sin estado, por lo que cada solicitud se atiende de forma independiente. El protocolo de enlace initialize y el encabezado Mcp-Session-Id son opcionales: los clientes que los envían siguen funcionando (el encabezado se acepta y se ignora), y también pueden pasar directamente a tools/list / tools/call. Cada herramienta es una consulta independiente de solicitud/respuesta; el servidor no envía notificaciones del servidor al cliente, por lo que GET /mcp (el flujo SSE) devuelve 405.
# List tools — a session handshake is optional in stateless mode
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'
# Call a tool
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_service_logs",
"arguments": {
"service_name": "your-service-name",
"lookback_minutes": 30,
"limit": 10
}
}
}'
Conjuntos de herramientas por solicitud
En modo HTTP, una solicitud puede restringir las herramientas disponibles mediante el encabezado X-Last9-Toolsets o el parámetro de consulta toolsets para los clientes que no pueden establecer encabezados (/mcp?toolsets=logs,traces). Si se envían ambos, prevalece un encabezado que no esté en blanco. El valor acepta los mismos nombres separados por comas que --toolsets. La solicitud ve la intersección con el conjunto de inicio --toolsets: puede restringirlo, pero nunca ampliarlo; all o un valor vacío mantienen el conjunto de inicio. Un nombre desconocido devuelve 400 e indica cuál es. tools/list, tools/call y las instrucciones de initialize se ajustan al conjunto efectivo; get_current_time y get_workspace_overview permanecen en todos los conjuntos.
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-Last9-Toolsets: logs,alerts" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'
Cada conjunto efectivo distinto se sirve mediante su propio servidor, que se crea con la primera solicitud (en decenas de milisegundos) y se mantiene hasta el cierre; una solicitud que vuelva a especificar el conjunto de inicio reutiliza el servidor de inicio. No hay expulsión: solo se aceptan conjuntos de herramientas con nombre, por lo que la caché contiene como máximo un servidor por combinación de estos (135 actualmente, unos 75 MB de memoria dinámica activa en total; en las pruebas, una caché completa se estabilizó cerca de los 230 MB de memoria residente).
Compilar desde el código fuente
git clone https://github.com/last9/last9-mcp-server.git
cd last9-mcp-server
go build -o last9-mcp-server
LAST9_HTTP=true ./last9-mcp-server
LAST9_HTTP=true es para desarrollo local. Para el uso real, el punto de conexión HTTP alojado es más sencillo.
Referencia de herramientas
Todos los parámetros, estándares de entrada de tiempo y detalles
Entrada de tiempo
- Los tiempos absolutos (
start_time_iso/end_time_isootime_iso) tienen prioridad sobrelookback_minutes. - Para intervalos relativos: usa
lookback_minutes. - Para intervalos absolutos: usa RFC3339/ISO8601 —
2026-02-09T15:04:05Z. - Se acepta el formato heredado
YYYY-MM-DD HH:MM:SSsolo por compatibilidad. - Convierte expresiones como
since yesterday 9am ISTen límites RFC3339 conget_current_time.
get_workspace_overview
Sin parámetros. Sondea los últimos 60 minutos de forma concurrente, con un tiempo de espera breve para cada subllamada, y devuelve current_time, lookback_minutes, environments, telemetry (valores booleanos logs/traces/metrics), service_count (rangos de servicios distintos get_service_summary, todos los entornos), alert_group_count, firing_alert_count y errors ([{field, error}]). Un campo cuya subllamada haya fallado es null (desconocido), nunca 0 ni false. Los sondeos no se restringen mediante --toolsets: los conjuntos de herramientas filtran qué herramientas se ofrecen y el token de API determina qué puede leer el servidor.
get_current_time
range(cadena, opcional):last 2h,past 30 minutes,last 7d(s, m, h, d = 24 h, w = 7 d; máximo 366 d);today;yesterday;since <point>;<point> to <point>. Un punto es RFC3339 otoday/yesterday/ un día de la semana (monday,mon: su ocurrencia más reciente, incluido hoy) /YYYY-MM-DDcon un9am,9:30pmo14:30opcional. Una zona horaria al final reemplazatimezonepara ambos límites; una zona en cualquier otro lugar es un error. Omítelo para obtener solonow.timezone(cadena, opcional): nombre o abreviatura IANA (IST = Asia/Kolkata, PST/PDT, EST/EDT, UTC, GMT). El valor predeterminado es UTC. Las zonas desconocidas generan un error, al igual que el ambiguoCST(pasaAmerica/ChicagooAsia/Shanghai).
Devuelve now (y start/end con range) como {iso, epoch_seconds, local}; local solo aparece para una zona que no sea UTC.
get_exceptions
limit(entero, opcional): máximo de excepciones. Valor predeterminado: 20.lookback_minutes(entero, opcional): valor predeterminado: 60.start_time_iso/end_time_iso(cadena, opcional): intervalo de tiempo absoluto.service_name(cadena, opcional): filtrar por servicio.span_name(cadena, opcional): filtrar por nombre de span.env(cadena, opcional): filtrar por entorno.
get_service_summary
start_time_iso/end_time_iso(cadena, opcional)env(cadena, opcional): expresión regular PromQL. El valor predeterminado es.*. Para una coincidencia exacta se necesitan anclas (p. ej.,^prod$).sort_by(cadena, opcional):request_count(predeterminado),throughput_rpm,http_4xx_count,http_5xx_countogrpc_error_count.limit(entero, opcional): máximo de filas clasificadas. Omitirlo o usar 0 significa 10; los valores superiores a 100 se limitan a 100.
get_service_environments
start_time_iso/end_time_iso(cadena, opcional)
Todas las demás herramientas de APM requieren un valor
env. Usa""si esto no devuelve resultados.
get_service_performance_details
Devuelve de forma predeterminada un resumen por serie: points, missing_points (intervalos de disponibilidad sin tráfico, excluidos de las estadísticas), min, p50, p90, p99 (rango más cercano), max, mean, last, baseline_mean (media de la primera mitad de los puntos válidos, no del intervalo), delta_pct (segunda mitad de los puntos válidos frente a la línea de base; ausente si la línea de base es 0) y hasta 3 change_points (timestamp, before_mean, after_mean), para un máximo de 4 series por métrica y cada cuantil de tiempo de respuesta (primero la media más alta; series_omitted cuenta el resto). Una serie con points: 0 no contiene estadísticas, y baseline_mean/delta_pct requieren al menos 6 puntos (3 por mitad). full_series indica que los puntos sin procesar están disponibles mediante include_series.
service_name(cadena, obligatorio)lookback_minutes(entero, opcional): valor predeterminado: 60.start_time_iso/end_time_iso(cadena, opcional)env(cadena, opcional): el valor predeterminado esprod.top_n(entero, opcional): máximo de entradas por lista de operaciones principales/errores principales. Valor predeterminado: 5, o 10 coninclude_series=true. Máximo: 100.include_series(booleano, opcional): devuelve todos los puntos de todas las series en lugar del resumen. Valor predeterminado: false.
get_service_operations_summary
service_name(cadena, obligatorio)lookback_minutes(entero, opcional): valor predeterminado: 60.start_time_iso/end_time_iso(cadena, opcional)env(cadena, opcional): el valor predeterminado esprod.
get_service_dependency_graph
service_name(cadena, opcional)lookback_minutes(entero, opcional): valor predeterminado: 60.start_time_iso/end_time_iso(cadena, opcional)env(cadena, opcional): el valor predeterminado esprod.
get_apm_service_deviations
service_name(string, opcional): Omitir para el ámbito de toda la flota; proporcionar para un servicio y sus correlaciones de operaciones.lookback_minutes(integer, opcional): Ventana actual. Valor predeterminado: 60.start_time_iso/end_time_iso(string, opcional): Ventana actual explícita.baseline_start_time_iso/baseline_end_time_iso(string, opcional): Línea base explícita. De forma predeterminada, se usa la ventana de igual duración inmediatamente anterior.datasource(string, opcional): Omitir para usar el valor predeterminado; los nombres aproximados se resuelven igual que en las herramientas de Prometheus. La respuesta indica el valor predeterminado o la coincidencia más cercana utilizada; los nombres ambiguos devuelven candidatos sin realizar consultas.env(string, opcional): De forma predeterminada, se usaprod.max_services/max_operations(integer, opcional): Valor predeterminado: 10; máximo: 10 cada uno.
get_databases
env(string, opcional): Filtrar por entorno. Acepta una expresión regular. Valor predeterminado: todos.lookback_minutes(integer, opcional): Valor predeterminado: 60. La ventana no puede superar los 7 días.start_time_iso/end_time_iso(string, opcional)
get_database_slow_queries
db_system(string, opcional): p. ej.,postgresql,mysql,mongodb,redis.host(string, opcional): Host de la base de datos (net_peer_name).service_name(string, opcional): Nombre del servicio que realiza la llamada.env(string, opcional)min_duration_ms(float, opcional): Duración mínima de la consulta en ms.lookback_minutes(integer, opcional): Valor predeterminado: 60.start_time_iso/end_time_iso(string, opcional)limit(integer, opcional): Valor predeterminado: 20.
get_database_queries
db_system(string, opcional)host(string, opcional)service_name(string, opcional)env(string, opcional)lookback_minutes(integer, opcional): Valor predeterminado: 60.start_time_iso/end_time_iso(string, opcional)limit(integer, opcional): Valor predeterminado: 20.
get_database_server_metrics
db_system(string, obligatorio): p. ej.,postgresql,mysql,mongodb,redis,aerospike.host(string, opcional)lookback_minutes(integer, opcional): Valor predeterminado: 60.start_time_iso/end_time_iso(string, opcional)
prometheus_range_query
query(string, obligatorio): La consulta PromQL.start_time_iso/end_time_iso(string, opcional): De forma predeterminada, se usan los últimos 60 min.lookback_minutes(float, opcional): Valor predeterminado: 60.
prometheus_instant_query
query(string, obligatorio)time_iso(string, opcional): De forma predeterminada, se usa la hora actual.lookback_minutes(float, opcional)
prometheus_label_values
match_query(string, opcional): Filtro PromQL.label(string, obligatorio): Nombre de la etiqueta.lookback_minutes(float, opcional): Valor predeterminado: 60.start_time_iso/end_time_iso(string, opcional)
Si no se especifica una ventana y match_query nombra una métrica (metric{...} o {__name__="metric"}), se vuelve a intentar una vez con un período de 4 días si el resultado de 60 minutos está vacío; un segundo bloque de texto indica el período que abarca el resultado.
prometheus_labels
match_query(string, opcional): Filtro PromQL.lookback_minutes(float, opcional): Valor predeterminado: 60.start_time_iso/end_time_iso(string, opcional)
Vuelve a intentar durante 4 días una búsqueda vacía de una sola métrica, como prometheus_label_values.
get_logs
logjson_query(array, obligatorio): Consulta de canalización JSON.lookback_minutes(integer, opcional): Valor predeterminado: 5.start_time_iso/end_time_iso(string, opcional)limit(integer, opcional): Valor predeterminado del servidor: 5000.index(string, opcional):physical_index:<name>orehydration_index:<block_name>.response_mode(string, opcional):summary(valor predeterminado; también paranull) oraw. Si el resultado de líneas sin procesar supera las 20 líneas, se devuelve comosummary:lines_summarized,severity_counts(las seis etiquetas principales; el resto enother_severities),first_timestamp/last_timestamp, unhistogramde 10 intervalos sobre la ventana de consulta (sobre las líneas devueltas, contruncated: true, cuando el límite haya truncado el resultado) y los principalespatterns(template,count, hasta dossamples), con el resto totalizado enother, con un máximo de 4000 caracteres; los metadatos de nivel superior que aún no quepan se descartan y se indican endropped_fields.rawdevuelve las líneas. Los resultados agregados nunca se resumen.
Para el inventario de servicios basado en registros, consulta primero physical_index_service_count:
sum by (name, service_name, env) (physical_index_service_count{destination="logs"})
Usa service_name como ServiceName, env como entorno cuando esté presente y name como nombre del índice físico. Si name="default", omite index; para un índice físico no predeterminado seleccionado por el usuario, pasa index: "physical_index:<name>". Si el backend rechaza el filtrado explícito por índice físico, vuelve a intentarlo sin index e indica que ese backend no admite el filtrado explícito por índice físico.
get_service_logs
service_name(string, obligatorio)lookback_minutes(integer, opcional): Valor predeterminado: 60.limit(integer, opcional): Valor predeterminado: 100, o 20 conresponse_mode: "raw".env(string, opcional)severity_filters(array, opcional): p. ej.,["error", "warn"]. Lógica OR.body_filters(array, opcional): p. ej.,["timeout", "failed"]. Lógica OR.start_time_iso/end_time_iso(string, opcional)index(string, opcional)response_mode(string, opcional):summary(valor predeterminado; también paranull) oraw. Si hay más de 20 líneas,logsse sustituye por el mismosummaryqueget_logs, y se conservan los demás campos;rawdevuelve las entradas.
Los distintos tipos de filtros se combinan con AND. Cada matriz usa OR internamente.
Usa get_logs primero para obtener recuentos agregados generales; usa get_service_logs solo después de acotar la búsqueda a un servicio/entorno/índice y a un conjunto pequeño de muestras.
get_log_patterns
service_name(string, obligatorio)env(string, opcional)start_time_iso/end_time_iso(string, opcional)lookback_minutes(integer, opcional): Valor predeterminado: 60.severity_filters/body_filters(array, opcional): Lógica OR, como enget_service_logs. Estableceseverity_filterssolo cuandoget_service_profileindiqueseverity_setall; connoneopartial, descarta las líneas cuya gravedad aparezca en el cuerpo.index(string, opcional)sample_size(integer, opcional): Líneas muestreadas de la ventana, de la más reciente a la más antigua. Valor predeterminado: 1000; máximo: 5000.top_n(integer, opcional): Plantillas devueltas por recuento. Valor predeterminado: 20; máximo: 100. El resto se totaliza enother.
La agrupación en clústeres se ejecuta en el servidor MCP sobre las líneas muestreadas, por lo que los recuentos, first_seen y last_seen corresponden a la muestra; window.sample_limit_reached indica cuándo la muestra podría no incluir líneas coincidentes (contiene sample_size líneas y la API no informa un total_matching_lines exacto igual a ese valor, o la búsqueda fue logs_truncated), en cuyo caso solo contiene las líneas más recientes de la ventana. Cada template y sample se trunca a 500 caracteres y termina en …, por lo que un cuerpo que no se agrupe en clústeres (como JSON sin un campo message) no puede llenar la respuesta. Una búsqueda con fragmentos fallidos genera un error de herramienta; una búsqueda truncada o limitada sigue devolviendo patrones, señalados por sample_limit_reached. Una lista patterns vacía significa que nada coincidió solo cuando sample_limit_reached es false; si es true, la búsqueda se detuvo antes de tiempo y podría haber omitido líneas coincidentes.
get_emerging_log_patterns
Acepta todos los parámetros de get_log_patterns (el intervalo de tiempo es la ventana actual), además de:
baseline_start_time_iso/baseline_end_time_iso(string, opcional): Establece ambos o ninguno. De forma predeterminada, se usa la ventana de igual duración inmediatamente anterior a la actual.min_count(integer, opcional): Número de líneas que necesita un patrón modificado en la ventana en la que es más frecuente: la actual paranew/increased, la línea base paradecreased/disappeared. Valor predeterminado: 3 (osample_sizesi es menor); máximo:sample_size.change_factor(number, opcional): Relación de frecuencia, superior a 1, que debe alcanzar (increased) o a cuyo inverso debe descender (decreased) un patrón presente en ambas ventanas. Valor predeterminado: 2.
Deben cumplirse ambos umbrales, por lo que nunca se informa de una plantilla de gran volumen cuya frecuencia no haya cambiado en change_factor. Devuelve solo las plantillas modificadas (new, increased, decreased, disappeared) con recuentos de current y baseline; unchanged_patterns cuenta las plantillas presentes en ambas ventanas cuyo cambio fue inferior a change_factor, y below_min_count_patterns cuenta los cambios descartados por tener menos de min_count líneas. Las frecuencias se comparan por minuto cuando ambas muestras contienen todas las líneas coincidentes; por minuto, estimadas a partir del total_matching_lines exacto de una muestra limitada; y, en los demás casos, como proporciones de la muestra, donde increased/decreased describen la composición, no la tasa (comparison_basis). Una lista patterns vacía significa que no hubo cambios que superaran ambos umbrales, no que los registros no hayan cambiado: los cambios menos frecuentes se cuentan en below_min_count_patterns, y una ventana con sample_limit_reached compara muestras, no ventanas completas.
get_log_attributes
lookback_minutes(integer, opcional): Valor predeterminado: 15.start_time_iso/end_time_iso(string, opcional)region(string, opcional)index(string, opcional)
get_log_attributes_for_pipeline
pipeline(array, obligatorio): Etapas de filtro anteriores para delimitar el descubrimiento, p. ej.,[{"type":"filter","query":{"$eq":["ServiceName","<service>"]}}].lookback_minutes(integer, opcional): Valor predeterminado: 15.start_time_iso/end_time_iso(string, opcional)region(string, opcional)index(string, opcional)
get_drop_rules
Sin parámetros. Enumera las reglas de descarte mediante GET /otel_settings/drop?region=....
add_drop_rule
name(string, obligatorio)filters(array, obligatorio): Cada filtro:key,value,operator(equals/not_equals),conjunction(and).- Las claves de filtro deben usar
attributes["key_name"]oresource.attributes["key_name"](requisito de la API de Last9). - Crea la regla mediante
POST /otel_settings/drop?region=...&cluster_id=....
get_remapping_rules
rule_type(string, obligatorio):logs_extract,logs_mapotraces_map.region(string, opcional): Valor predeterminado: la región configurada del origen de datos.- Enumera las reglas mediante
GET /otel_settings/remapping/{rule_type}?region=....
add_remapping_rule
Crea una regla por llamada; las reglas de asignación requieren una llamada por cada target_attribute.
rule_type(cadena, obligatorio):logs_extract,logs_mapotraces_map.name(cadena, obligatorio)remap_keys(matriz, obligatorio): Campos de origen.logs_extractconjson: nombres de campos del cuerpo; conpattern: exactamente una expresión regular con un grupo de captura con nombre. Reglas de asignación: rutas de atributos, gana el primer valor no vacío.target_attribute(cadena, obligatorio):logs_extract:log_attributesoresource_attributes.logs_map:service,severityoresource_deployment.environment.traces_map:service.extract_type(cadena, solologs_extract, obligatorio allí):jsonopattern.action(cadena, solologs_extract):upsert(predeterminado) oinsert.prefix(cadena, solologs_extract)preconditions(matriz, solologs_extract): Como máximo un{key, value, operator};operatoresequals(predeterminado),not_equalsolike(expresión regular).region(cadena, opcional): Predeterminado: la región de la fuente de datos configurada.- Crea la regla mediante
POST /otel_settings/remapping/{rule_type}?region=.... Nunca se reintenta; tras un tiempo de espera agotado, comprueba conget_remapping_rules.
get_traces
Úsalo para búsquedas amplias y agregaciones. Para buscar un ID de traza exacto, usa get_service_traces.
tracejson_query(matriz, obligatorio)start_time_iso/end_time_iso(cadena, opcional)lookback_minutes(entero, opcional): Predeterminado: 60.limit(entero, opcional): Predeterminado: 5000.
get_service_traces
Se requiere exactamente uno de trace_id o service_name.
trace_id(cadena, opcional): Período retrospectivo predeterminado: 72 horas.service_name(cadena, opcional): Período retrospectivo predeterminado: 60 min.lookback_minutes(entero, opcional)start_time_iso/end_time_iso(cadena, opcional)limit(entero, opcional): Predeterminado: 10.env(cadena, opcional)
get_trace_attributes
lookback_minutes(entero, opcional): Predeterminado: 15.start_time_iso/end_time_iso(cadena, opcional)region(cadena, opcional)
get_trace_attributes_for_pipeline
pipeline(matriz, obligatorio): Etapas de filtro anteriores para limitar el descubrimiento, p. ej.,[{"type":"filter","query":{"$eq":["ServiceName","<service>"]}}].lookback_minutes(entero, opcional): Predeterminado: 15.start_time_iso/end_time_iso(cadena, opcional)region(cadena, opcional)
get_trace_attribute_values
tag_name(cadena, obligatorio): Nombre del atributo deget_trace_attributes(p. ej.,resource_departmentoattributes['http.method']).pipeline(matriz, opcional): Etapas de filtro anteriores para limitar los valores; omítelo para obtener valores globales.lookback_minutes(entero, opcional): Predeterminado: 15.start_time_iso/end_time_iso(cadena, opcional): Límites históricos RFC3339; tienen prioridad sobrelookback_minutes.region(cadena, opcional)
get_trace_attribute_deviations
comparison_mode(cadena, obligatorio):latency,errorsotime.service_name(cadena, obligatorio)environment(cadena, obligatorio): Valor exacto dedeployment.environment.operation(cadena, opcional)filters(matriz, opcional): Condiciones de filtro JSON de trazas.candidate_attributes(matriz, opcional): Máximo 8; omítelo para un descubrimiento acotado.latency_threshold_ms(número, opcional): Obligatorio para el modolatency; se rechaza en los demás modos.start_time_iso/end_time_iso(cadena, opcional)lookback_minutes(entero, opcional): Predeterminado: 15. Máximo: 15.baseline_start_time_iso/baseline_end_time_iso(cadena, opcional): Obligatorio para el modotime; no se superponen y tienen la misma duración que la ventana objetivo.minimum_cohort_size(entero, opcional): Predeterminado: 100. Mínimo: 20.minimum_value_support(entero, opcional): Predeterminado: 20. Mínimo: 10.limit(entero, opcional): Predeterminado: 10. Máximo: 10.
Requiere que esté habilitada la capacidad complementaria del backend.
get_trace_waterfall
trace_id(cadena, obligatorio)environment(cadena, opcional)start_time_iso/end_time_iso(cadena, opcional)lookback_minutes(entero, opcional): Predeterminado: 4320 (72 horas).selected_span_id(cadena, opcional): Devuelve atributos, eventos y enlaces solo para ese span.max_spans(entero, opcional): Predeterminado: 500. Máximo: 1000; la política de redacción de la organización puede limitarlo a un valor menor.
Devuelve sin cambios el sobre investigation-evidence/v1 del servicio de trazas; el diagrama de cascada está en data. El servicio genera el diagrama de cascada y redacta los valores según una política por organización (credenciales, literales de consulta, URL, correos electrónicos, IP, identificadores de usuario) antes de que salgan del backend: sanitizer indica la política aplicada y evidence.sanitization cuenta lo que se redactó o truncó. Si se redacta algo necesario, pide a un administrador de la organización que ajuste la política en lugar de buscar una forma de eludirla. La herramienta rechaza una respuesta sin un bloque sanitizer, con un sanitizer.version distinto de trace-evidence-sanitizer/v1, con un cuerpo que repita una clave JSON o con un cuerpo de más de 512 KiB (límite inferior max_spans). Requiere que esté habilitada la capacidad complementaria del backend.
get_change_events
start_time_iso/end_time_iso(cadena, opcional)lookback_minutes(entero, opcional): Predeterminado: 60.service_name(cadena, opcional)env(cadena, opcional)event_name(cadena, opcional): Llama primero sin este parámetro para obteneravailable_event_names.
analyze_change_impact
Pasa exactamente uno de change_event o service + timestamp.
change_event(objeto, opcional): Un elemento deget_change_eventschange_events[], sin cambios. Usametric.service,metric.deployment_environmenty elvalues[].timestampmás temprano.service(cadena, opcional): Obligatorio junto contimestampcuando se omitechange_event.timestamp(cadena, opcional): RFC3339 o segundos Unix, redondeados hacia abajo al minuto.env(cadena, opcional): Entorno exacto. Predeterminado: el del evento; si no, todos.baseline_minutes(entero, opcional): Predeterminado: 60. Limitado al intervalo 30..120.after_minutes(entero, opcional): Predeterminado:baseline_minutes. Limitado al intervalo 30..120 y como máximo hasta ahora.
regression likely cuando se cumpla cualquiera de estas condiciones: la tasa de errores aumenta al menos 1 punto porcentual, el p95 aumenta al menos un 40 % o el rendimiento (rpm) disminuye al menos un 50 % para el servicio o uno de sus 10 principales llamadores, clasificados por rendimiento combinado en ambas ventanas (solo con al menos 100 solicitudes antes del cambio y, para el rendimiento, también después; el p95 cuenta solo las solicitudes de latency_env); una alerta para ellos empieza a activarse después del cambio y sigue activa al final de la ventana posterior (coincidencia sin distinguir mayúsculas y minúsculas por nombre del grupo de alertas o por una etiqueta service, service_name o service.name; en su defecto, por anotación, nunca por texto libre como un resumen; con env, se descartan las alertas etiquetadas con otro entorno, sin distinguir mayúsculas y minúsculas, y las que no tienen etiqueta se marcan como env_match: unlabelled); una regresión de get_apm_service_deviations. insufficient data cuando la ventana posterior dura menos de 5 minutos (incluido un cambio futuro), el servicio tiene muy pocas solicitudes o fallaron las señales doradas, la búsqueda de llamadores directos, las alertas o get_apm_service_deviations, total o parcialmente en cualquiera de las consultas enumeradas en deviations.apm.unavailable: la falta de evidencia nunca produce no significant change, y una regresión detectada indica qué falló. no significant change es una respuesta válida, no un fallo. Las desviaciones de atributos de traza solo se ejecutan cuando se establece env, durante un máximo de 15 minutos a cada lado; explican un veredicto, pero nunca lo determinan, por lo que omitirlas o no poder obtenerlas no lo modifica. Cada lista conserva 10 elementos e informa cuántos descartó; una llamada ascendente fallida deja un error solo en su sección. La latencia de las señales doradas proviene de un único entorno en ambas ventanas, indicado en latency_env: si no se especifica env, se usa el de mayor actividad. La tasa de errores de las señales doradas cuenta los errores 5xx y gRPC; deviations.apm también cuenta los spans con estado de error y los 429. Las señales de un llamador corresponden a sus propios spans de servidor, por lo que los llamadores que solo tienen spans de cliente muestran 0 rpm.
regressions enumera una entrada por cada desencadenante detrás de regression likely, en el orden de reason, y es [] para los demás veredictos. Cada entrada tiene service, role (changed o dependent), env, metric (exactamente uno de error_rate_pct, latency_p95_ms, throughput_rpm, alert, apm_deviation) y baseline/after en la unidad de la métrica (%, ms, rpm o el valor propio de la señal APM; nulo para alert). Un elemento alert añade rule_id y state, el estado de la instancia en el segmento más reciente del monitor de alertas de la ventana posterior que la incluye, siempre firing: una alerta que empezó después del cambio, pero se resolvió antes del final de la ventana posterior, no es una regresión, por lo que no genera un ticket; permanece en alerts.started con su state, y reason la cuenta. env es el entorno en el que se midió la métrica: el env solicitado; si no se especifica, la tasa de errores y el rendimiento suman todos los entornos (env: ""), mientras que el p95 usa el de mayor actividad, latency_env; una alerta usa su propia etiqueta de entorno y una desviación APM, su propio entorno. Todas las regresiones afectan a todo el servicio, por lo que ninguna identifica un endpoint.
get_alert_groups
Inventario de grupos de alertas de Compass configurados para auditorías de changeboard / cobertura de etiquetas. Incluye grupos sin reglas y grupos que no están activos. No devuelve PromQL.
alert_group_name/alert_group_type/data_source_name(cadena, opcional): Coincidencia parcial sin distinguir mayúsculas y minúsculas.team/tier(cadena, opcional): Coincidencia exacta sin distinguir mayúsculas y minúsculas en los metadatos configurados.label_key+label_value(cadena, opcional): Deben establecerse juntos. Coincidencia exacta sin distinguir mayúsculas y minúsculas con un parmetadata.labels: tanto la clave como el valor.
Devuelve {"count":N,"groups":[...]} JSON compacto con id, name, type, entity_class, team, tier, metadata.labels y los recuentos de reglas. Un team / labels vacío significa que no está establecido.
get_alert_operations_summary
start_time_iso/end_time_iso(cadena, obligatorio): Ventana RFC3339 de hasta 31 días.timezone(cadena, opcional): Nombre IANA para los límites del día. Predeterminado:UTC.severities/services(matriz, opcional): Limita el resumen.include_rules(booleano, opcional): Devuelve también el estado de higiene por regla (las 20 principales porfire_count, reglas con oscilaciones y sin enrutamiento, contruncatedcuando la API haya limitado la lista de reglas). Predeterminado:false.
Devuelve JSON con alerts_raised, acknowledged, unacknowledged, current_backlog, delivered, notification_outcomes, top_rules, needs_attention y coverage. Cada métrica es {status, count?, reason?}; un recuento partial cubre solo parte de la ventana y no es un límite inferior.
get_alert_coverage
Responde a «¿sobre qué no tengo alertas?» para un servicio, un entorno, un grupo de alertas o toda la organización si no se especifica ningún ámbito. Solo lectura: nunca crea, modifica ni reactiva nada. También está disponible en el conjunto de herramientas investigate.
service(cadena, opcional): Nombre del servicio.environment(cadena, opcional): Entorno de implementación (por ejemplo,prod).entity_id(cadena, opcional): UUID del grupo de alertas (entidad), tal como lo devuelveget_alert_groups.muted_days(entero, opcional): Días completos, de 1 a 3650. Predeterminado: 7; 0 significa el valor predeterminado. Una regla silenciada durante más tiempo que este período constituye una brecha demuted_too_long. Los valores se recortan; se ignoran las cadenas vacías. Tipos de brecha: uncovered_signal (no hay ninguna regla habilitada para latency, error_rate, throughput o saturation; indica una regla deshabilitada que la cubriría), all_rules_disabled (un grupo de alertas cuyas reglas están todas deshabilitadas), no_notification_channel (una regla cuya gravedad no tiene ningún canal al que enrute el grupo de alertas), muted_too_long (silenciada indefinidamente o durante más demuted_days;muted_sincees la última actualización de la regla ymute_untilse omite si el silenciamiento es indefinido).
Devuelve JSON compacto {scope, generated_at, complete, summary, counts, groups}. groups contiene una entrada {kind, count, gaps, omitted} por cada tipo presente y enumera como máximo 10 brechas por tipo (omitted cuenta el resto; acota el ámbito para verlas); cada brecha incluye suggested_tool (create_alert, create_alert_from_chart, attach_notification_channel o patch_alert) y suggested_args, que contiene solo entradas aceptadas por esa herramienta (patch_alert: entity_id, id y is_disabled=false o mute_until=0; attach_notification_channel: alert_group_id y un breach/threat severity; create_alert: entity_id; create_alert_from_chart: service_name); junto a ellas se enumeran los valores propios de la brecha entity_id, rule_id y signal. Llama a la herramienta sugerida solo si está disponible en la sesión y el usuario lo confirma. complete es true solo cuando la API no enumera ninguna brecha, todos los recuentos son 0 y al menos un grupo de alertas coincide con el ámbito (etiquetas exactas service_name / env, comprobadas con la lista de get_alert_groups); si el ámbito no coincide con ninguno, falla una consulta o hay recuentos sin brechas enumeradas, no se considera completo, y summary explica el motivo.
get_alert_config
search_term(string, opcional): Búsqueda de texto libre en nombre, grupo, fuente de datos y etiquetas.rule_name(string, opcional)severity(string, opcional)rule_type(string, opcional):staticoanomaly.alert_group_name/alert_group_type/data_source_name(string, opcional)tags(array, opcional): Todos deben coincidir (lógica AND).
Cada regla enumera Recent Evaluations: 5 muestras de un minuto, de la más reciente a la más antigua, cada una etiquetada con el minuto UTC en que termina; la más reciente termina entre 1 y 2 minutos antes de la llamada, ya que el monitor completa el último minuto con retraso. firing; ok = el monitor informó que la regla no se está activando (estado normal, resuelto, pendiente o vacío, según is_firing=0 en get_alert_rule_state); unknown = no hay ninguna fila del monitor para la regla (aún no se ha evaluado o falló la consulta de ese minuto, como se indica al principio de la respuesta), o hay una fila cuyo estado no es ninguno de los siguientes: firing, normal, resolved, pending o empty (p. ej., no_data, silenced). Las reglas deshabilitadas muestran none. Si coinciden más de 25 reglas habilitadas, se omite el historial de todas y la respuesta lo indica.
get_entity_kpis
Solo lectura; en los conjuntos de herramientas alerts y investigate. Enumera todos los KPI de un grupo de alertas, incluidos los KPI que aún no usa ninguna regla de alerta. Usa los ID devueltos en expression_args / indicators_used; nunca inventes uno.
entity_id(string, obligatorio): UUID del grupo de alertas/entidad, obtenido deget_alert_groupsoget_alert_config.
Devuelve JSON {"entity_id","count","kpis":[{"id","name","description","query","unit"}]}, ordenado por nombre. unit es una etiqueta de visualización: los umbrales se comparan con el valor sin procesar de la consulta. Si no hay ningún KPI adecuado, crea uno con create_kpi. El resultado de un grupo vacío solo menciona create_kpi si la sesión lo ofrece; con --read-only (salvo --allow-write-tools=create_kpi) o el conjunto de herramientas investigate, indica que la creación de KPI requiere una sesión alerts con permisos de escritura.
create_kpi
Crea un KPI en un grupo de alertas existente; solo en el conjunto de herramientas alerts. Úsala únicamente si get_entity_kpis no tiene ningún KPI con la consulta que necesitas. Comprueba primero con prometheus_range_query que la consulta devuelve datos: la API solo rechaza las consultas que no se pueden analizar y guarda las consultas válidas aunque la métrica no exista.
entity_id(string, obligatorio): UUID del grupo de alertas/entidad.name(string, obligatorio): Debe ser único dentro del grupo de alertas; se rechaza un segundo intento de creación con el mismo nombre por conflicto.query(string, obligatorio): PromQL. Para crear una alerta a partir de un panel del dashboard, toma el PromQL del panel deget_dashboard.unit(string, obligatorio): Unidad del valor sin procesar de la consulta, p. ej.,seconds,ms,percent,ratio,count,req/s. Es solo una etiqueta y no convierte nada, por lo que los umbrales se comparan con el valor sin procesar.description(string, opcional)data_source_id(string, opcional): De forma predeterminada, usa la fuente de datos del grupo de alertas.
Ninguna herramienta MCP elimina ni edita un KPI (usa la interfaz de Last9), así que confirma el nombre, la consulta y la unidad antes de crearlo. Créalo una sola vez y conserva el ID devuelto para expression_args / indicators_used; si hay un conflicto de nombre, reutiliza el ID del KPI existente de get_entity_kpis. Si se produce un tiempo de espera o un error de conexión, es posible que la creación se haya aplicado o no: comprueba el nombre con get_entity_kpis antes de volver a intentarlo. Devuelve {"id","name","query","unit"}.
get_alerts
time_iso(string, opcional): Hora de evaluación en RFC3339.window(integer, opcional): Intervalo retrospectivo en segundos. Predeterminado: 900. Rango: 60–86400.lookback_minutes(integer, opcional): Rango: 1–1440.
get_alert_rule_state
start_time(integer o string, obligatorio): Inicio del intervalo (inclusive): segundos de época Unix (1718000000) o RFC3339 (2024-06-10T06:13:20Z). Los milisegundos, microsegundos y nanosegundos de época se convierten a segundos; se rechazan los valores anteriores a 2000-01-01.end_time(integer o string, obligatorio): Fin del intervalo (inclusive), en los mismos formatos.step(integer, obligatorio): Resolución en segundos entre muestras. El número de muestras((end_time - start_time) / step + 1)está limitado a 100. Cada muestra es una llamada al sistema ascendente que cubre losmin(step, 3600)segundos que terminan en su marca de tiempo; las muestras se obtienen de 5 en 5 y la herramienta devuelve un error con instrucciones para reintentar si tardan más de 60 s.alert_group_id(string, opcional): Filtrar por ID del grupo de alertas.rule_name(string, opcional): Filtro regex por nombre de regla.alert_group_name(string, opcional): Filtro regex por nombre del grupo de alertas.label_filters(string, opcional): Filtros de etiquetaskey=valueseparados por comas.state(string, opcional): Filtrar por estado (p. ej.,firing).
Devuelve un mapa JSON de rule_id -> [{timestamp, is_firing}]. Si una regla no aparece en la respuesta del sistema ascendente en una marca de tiempo, se informa como is_firing=0; esto significa «no se observó que se activara», no que se haya confirmado un estado normal.
get_alert_hygiene
Auditoría de solo lectura de las reglas de alerta habilitadas. Nunca modifica nada: cada corrección sugerida es una propuesta que solo se aplica cuando el usuario la confirma explícitamente, mediante update_alert (ajuste), patch_alert (is_disabled), delete_alert o attach_notification_channel cuando estén disponibles.
window(string, opcional):7do30d. Predeterminado:7d.limit(integer, opcional): Número máximo de reglas marcadas que se devuelven, empezando por las peores. Predeterminado: 20. Rango: 1–100.
El intervalo termina ahora. Categorías (una regla puede pertenecer a varias): noisy (≥ 5 activaciones/día con < 20% acked; with an unavailable ack_rate it is judged on fire rate alone and the reason says so; mute time is not reported), flapping (> 4 oscilaciones/día), stale (la última activación fue antes del intervalo y no hubo ninguna durante este, o una regla configurada y habilitada no tuvo ningún episodio durante el intervalo mientras el ciclo de vida coverage está completo y la lista de la API no está truncada), unrouted (ninguna activación durante el intervalo llegó a un canal y el grupo de alertas no tiene ningún canal asociado actualmente; si no se pueden leer las asociaciones, la regla se contabiliza como unavailable.routed). Cada estadística es {status, count|value, reason} o null: un valor no disponible nunca se interpreta como cero y solo omite la comprobación que lo necesita; unavailable cuenta las reglas habilitadas por cada valor ausente (fire_count, flap_count, ack_rate, routed, rule_config para las reglas ausentes de la lista de reglas de alerta, cuyas correcciones no incluyen un valor actual, y activity para las reglas configuradas sin episodios cuyo silencio no puede demostrarse con el historial y que, por tanto, no se evalúan). coverage indica qué parte del intervalo cubrió el historial de alertas; las tasas usan el período cubierto, de al menos un día. upstream_truncated es true cuando la API devolvió solo sus 200 reglas más ruidosas, por lo que no se evaluaron las reglas menos ruidosas. Las correcciones se basan en la configuración de cada regla de la lista de reglas de alerta: aumentar bad_minutes (la N de count_true(result) >= N) o alargar eval_window (hasta 60 minutos); para los umbrales estáticos ruidosos, mover el umbral en condition (expr > 100) o cambiar la expresión a adaptive_std_cmp(metric, sensitivity, window); asociar un canal de notificación; deshabilitar o eliminar las reglas obsoletas.
Devuelve JSON compacto {window, from, to, coverage, upstream_truncated, rules_evaluated, flagged_rules, truncated, omitted_for_size, unavailable, rules}, con un objetivo de tamaño de 32 KB: truncated es true cuando las reglas marcadas superan limit o ese objetivo, y omitted_for_size cuenta las reglas dentro de limit que se omitieron por tamaño (siempre se conserva la peor regla, así que una sola regla de más de 32 KB se devuelve por sí sola y supera el objetivo); cada regla incluye findings (category, reason, suggested_fix, opcionalmente alternative_fix) y el valor sin procesar stats.
get_notification_channels
scope(string, opcional):attached(predeterminado) devuelve una fila por asociación con un grupo de alertas;ides un ID de asociación, como el que aceptadetach_notification_channel.channelsdevuelve los canales de la organización (id,name,type,send_resolved,created_via);ides un ID de canal, como el que aceptaattach_notification_channel. Nunca se devuelven destinos, encabezados ni certificados.
Devuelve los canales de notificación configurados (Slack, PagerDuty, correo electrónico, webhooks, etc.) en una tabla TSV. Las filas de asociación (service_fqid establecido) muestran global false y services -; services enumera solo las vinculaciones de servicios heredadas. En las filas de asociación, send_resolved es el valor predeterminado almacenado en la fila, no el que se usa para la entrega; las notificaciones de resolución siguen el send_resolved propio del canal (scope=channels). Ambos ámbitos incluyen created_via (last9-mcp para las filas creadas por un agente MCP; de lo contrario, -).
recommend_alert_config
Solo lectura. Devuelve orientación sobre el ciclo de vida, criterios de validación y requisitos de contexto para quienes responden; no constituye evidencia por sí sola, así que fundamenta los umbrales con herramientas de telemetría.
signal(string, obligatorio): Métrica o señal de impacto para el usuario, incluidas la unidad y las dimensiones.objective(string, obligatorio): Síntoma o impacto para el usuario que se debe detectar.rule_type(string, opcional):static(predeterminado) oanomaly.severity(string, opcional):breach(predeterminado) othreat.environment(string, opcional): Entorno y perfil de tráfico.
validate_alert_expression
Ejecuta una expresión de prueba sin guardarla. Úsala antes de create_alert / update_alert.
entity_id(string, obligatorio): UUID del grupo de alertas/entidad.expression(string, obligatorio): Operaciones aritméticas sobre indicadores identificados por el nombre exacto de su KPI, p. ej.,errors / requests. Se permiten como máximo 3 indicadores. Escribe los nombres con espacios, puntos o guiones tal cual (Test Alert / errors), exactamente como los guardacreate_alert.indicators_used(object, obligatorio, no vacío): Nombre del indicador asociado al UUID del KPI propiedad deentity_id; enumera los ID y nombres de los KPI del grupo conget_entity_kpis. Usa el nombre exacto de cada KPI: esta ejecución de prueba acepta cualquier nombre, perocreate_alertyupdate_alertrechazan las clavesexpression_argsque no sean el nombre del KPI asociado. Solo se comprueba la expresión aritmética (pasa la expresión de métrica interna, no un contenedoradaptive_std_cmp(...)); no se compruebancondition/alert_condition. La comprobación de al menos una serie temporal solo se ejecuta cuando se usa más de un indicador.
Devuelve {"valid":true} o {"valid":false,"reason":"..."}; una expresión no válida es un resultado normal, no un error de la herramienta.
describe_alert_chart
Solo lectura. Enumera las señales que pueden generar alertas (key, unit, eval_query, query_kind) en un gráfico de Discover compatible. Se rechazan los tokens con rol de visor.
surface(string, obligatorio):discover-serviceodiscover-exceptions; se rechaza cualquier otra superficie, incluidos los paneles personalizados, antes de llamar a la API (usacreate_alertcon un KPI en su lugar).chart_key(string, obligatorio): p. ej.,apdex,error_rate,response_time,exception_count.service_name(string, obligatorio)env(string, opcional)attributes(object, opcional): Dimensiones de identidad adicionales, usando los nombres de clave que informa esta herramienta.
create_alert
Crea una regla en un grupo de alertas existente, siempre deshabilitada (un borrador, incluso si se pasa is_disabled=false). Llama primero a recommend_alert_config y luego promuévela con patch_alert is_disabled=false solo después de que el usuario lo confirme. Créala una sola vez y conserva el ID de regla devuelto. Se rechaza un rule_name que el grupo de alertas ya use (sin distinguir mayúsculas y minúsculas), sin crear nada y se indica el id de la regla existente.
entity_id(string, obligatorio): UUID del grupo de alertas/entidad.alert_rule(object, obligatorio): Cuerpo completo y tipado de la regla, incluidosprimary_indicatoryexpression_argsvinculados a KPI reales de la entidad. Cada claveexpression_argsdebe ser el nombre exacto del KPI al que está vinculada (no un marcador de posición comox), yprimary_indicatoryexpressionusan esos nombres. Las reglas estáticas requierencondition,alert_conditionyeval_window(0–60 minutos). Las reglas adaptativas usanexpression=adaptive_std_cmp(<primary_indicator>, sensitivity, window); en ese caso, los tres campos son opcionales (valores predeterminadosexpr > 0,count_true(result) >= 1,1). No se puede estableceralgorithm. El contexto del responsable va enproperties(descripción, runbook, anotaciones,query_mode).
El enrutamiento de notificaciones se configura en el grupo de alertas, no en la regla.
create_alert_from_chart
Crea una regla de umbral estático a partir de un gráfico descrito por describe_alert_chart. Al igual que create_alert, la regla se crea deshabilitada como borrador y no se activa; promuévela con patch_alert is_disabled=false solo después de que el usuario lo confirme. La API del gráfico crea las reglas habilitadas, por lo que la herramienta deshabilita la regla justo después de crearla; si eso falla, el resultado es un error que indica el id de la regla, que podría estar activa.
surface/chart_key/service_name/signal_key/name(string, obligatorio)env(string, opcional)attributes(object, opcional)threshold(number, opcional): En la unidad de la señal; debe ser 0 o más. Las señales en unidades de puntuación, como apdex, requieren un valor explícito.threshold_operator(string, opcional):>(predeterminado),<,>=,<=,==,!=.eval_window(integer, opcional): Minutos, 1–60. Predeterminado: 5.bad_minutes(integer, opcional): 1–eval_window. Predeterminado: 3.severity(string, opcional):breachothreat.description(string, opcional)group_id(string, opcional): Normalmente se omite.
update_alert
Reemplazo completo de la configuración de evaluación de una regla. Recupera primero la regla con get_alert_config o get_entity_alert_rules. Se rechaza sin cambiar nada si la regla no existe o pertenece a otro grupo de alertas. Puede eliminar la regla de forma lógica y volver a crearla, así que usa después el ID de reemplazo devuelto.
entity_id(string, obligatorio)id(string, obligatorio): UUID de la regla de alerta.alert_rule(object, obligatorio): Configuración completa; misma estructura quecreate_alert.
patch_alert
Cambio parcial que conserva el ID de la regla. Se requiere al menos un cambio. Se rechaza sin cambiar nada si la regla no existe o pertenece a otro grupo de alertas.
entity_id(string, obligatorio)id(string, obligatorio): UUID de la regla de alerta.is_disabled(boolean, opcional):falsepromueve/habilita;truepausa.mute_until(integer, opcional): Fecha límite en tiempo Unix epoch;0quita el silencio.properties(object, opcional):description,runbook,annotations,query_mode. Cada campo enviado reemplaza el valor almacenado; los campos omitidos o en blanco conservan su valor almacenado.annotationsreemplaza todo el mapa, así que envía todas las claves para conservarlas;{}lo borra.recurring_mute_schedule(object, opcional): Reemplaza la programación semanal:enabled(predeterminado false),timezone(nombre IANA, obligatorio) ywindows(de 1 a 20 deweekdayscomosun..sat,start_time/end_timecomoHH:MMde 24 horas). Si la hora de finalización es igual o anterior a la de inicio, la ventana termina al día siguiente. Envía todas las ventanas para conservarlas.clear_recurring_mute_schedule(boolean, opcional): Elimina por completo la programación; no se puede combinar conrecurring_mute_schedule. Se diferencia deenabled=false, que conserva las ventanas almacenadas.
Los usuarios con rol de visor solo pueden cambiar mute_until. Se rechazan los cambios de silencio a nivel de regla mientras el grupo o la entidad estén silenciados. Consulta los cambios con get_entity_alert_rules, que muestra mute_until, recurring_mute_schedule y properties.
delete_alert
Elimina una regla de forma permanente. Verifica con get_alert_config y obtén primero la confirmación explícita del usuario. Se rechaza sin eliminar nada si la regla no existe o pertenece a otro grupo de alertas.
entity_id(string, obligatorio)id(string, obligatorio): UUID de la regla de alerta.
mute_alert_group
Silencia todas las reglas actuales y futuras de un grupo; para silenciar una sola regla, usa patch_alert con mute_until. Requiere acceso de editor.
entity_id(string, obligatorio): UUID del grupo de alertas/entidad.until(integer, obligatorio): Segundos de Unix epoch en que termina el silencio.0quita el silencio; un valor negativo como-1silencia indefinidamente.
acknowledge_alert
Se hace cargo de una alerta activa o libera su propiedad. No resuelve ni silencia la alerta. Reconocer una alerta de la que ya eres responsable no produce ningún cambio. ack se rechaza sin registrar nada, a menos que get_alerts incluya esa alerta exacta (los tres ids) durante la última hora.
alert_group_id/rule_id/rule_type(string, obligatorio): Copia los valores de la alerta enget_alerts.action(string, opcional):ack(predeterminado) ounack. Solo el responsable o un administrador de la organización puede quitar el reconocimiento.mute_until(integer, opcional): Soloack. Registra un silencio que ya aplicaste conmute_alert_groupopatch_alert; no silencia nada.
reassign_alert
Transfiere una alerta que ya tiene responsable a otro usuario; si no tiene ninguno, reconócela primero. Si no se reasigna nada porque la alerta no tiene responsable, indica si get_alerts incluye esa alerta durante la última hora. Last9 no notifica al nuevo responsable.
alert_group_id/rule_id/rule_type(string, obligatorio): Copia los valores de la alerta enget_alerts.owner_email(string, opcional): Preferido; se compara sin distinguir mayúsculas y minúsculas con los usuarios activos de la organización.owner_user_id(string, opcional): Proporciona exactamente uno deowner_emailoowner_user_id.
create_notification_channel
Crea un canal; no enruta nada hasta que se asocie con attach_notification_channel. Créalo una sola vez y conserva el id; no hay ninguna herramienta para actualizarlo. Devuelve solo id, name, type y send_resolved.
name(string, obligatorio): Único por tipo; sin dos puntos.type(string, obligatorio):slack,pagerduty,opsgenie,emailogeneric_webhook.destination(string, obligatorio): URL de Slack Incoming Webhook (o ID de canal conslack_app_mode), clave de enrutamiento de PagerDuty, clave de API de Opsgenie, direcciones de correo electrónico de trabajo separadas por comas de cualquier dominio, no solo del de la organización (la API rechaza direcciones de proveedores personales como Gmail, y una organización con una cuenta personal solo acepta la dirección de su administrador), o una URL de webhook HTTPS.slack_app_mode(boolean, opcional): Solo Slack. Predeterminado: false.mention_handles(string, opcional): Separados por comas, p. ej.,user|U123ABC|name,special|here,special|channel.webhook_headers(object, opcional): Sologeneric_webhook.webhook_ca_certificate(string, opcional): Sologeneric_webhook; un certificado de CA PEM de hasta 64 KiB.send_resolved(boolean, opcional): Predeterminado: true.
attach_notification_channel
Enruta un canal existente a un grupo de alertas. Es idempotente. Las asociaciones copian la configuración de entrega del canal (encabezados, certificado, menciones) en el momento de asociarlo; si la configuración del canal cambia después, vuelve a asociarlo.
channel_id(integer, obligatorio): Un id de canal (get_notification_channelsconscope=channels), no un id de asociación.alert_group_id(string, obligatorio): Deget_alert_groups.severity(string, obligatorio):breach,threatoboth.bothcrea dos asociaciones; si falla la segunda, la primera permanece asociada y el error indica la gravedad que se debe reintentar.
detach_notification_channel
Elimina una asociación. Requiere un token con ámbito de eliminación; un token con ámbito de escritura no puede eliminar asociaciones.
attachment_id(integer, obligatorio): Eliddeget_notification_channelsconscope=attached. Se rechazan los ids de canal.alert_group_id(string, opcional): Se rechaza si la asociación pertenece a otro grupo.
delete_notification_channel
Elimina de forma permanente un canal que no tenga asociaciones; primero elimina sus asociaciones. Requiere un token con ámbito de eliminación. Confirma la eliminación volviendo a enumerar los canales.
channel_id(integer, obligatorio): Eliddeget_notification_channelsconscope=channels. Se rechazan los ids de asociación.
did_you_mean
query(string, obligatorio): El nombre que se va a buscar: parcial, mal escrito o abreviado.type(string, opcional): Restringe por tipo de entidad:service,environment,host,database,k8s_deployment,k8s_namespace,job.
Devuelve hasta 3 coincidencias más cercanas con puntuaciones de similitud. Úsalo antes de cualquier llamada a una herramienta cuando el nombre de la entidad no sea seguro. Si una llamada anterior devolvió resultados vacíos, úsalo antes de volver a intentarlo.
get_service_profile
service_name(string, obligatorio): Servicio para el que se derivará un perfil de telemetría.datasource(string, opcional): Nombre del origen de datos. Omítelo para usar el predeterminado; un nombre aproximado se resuelve igual que en las herramientas de Prometheus. Devuelve un breve informe de investigación seguido del perfil completo como JSON sin procesar: presencia de señales (logs/traces/metricscomopresent,absentounknown), lenguaje y entorno de ejecución, entornos de despliegue,signal_shapede registros (log_format,severity_set,level_field,trace_context_in_logs) y una solución recomendada para la ingesta cuando corresponda. Cuando el backend informafreshness, el informe añade una línealast seenconfreshness.logs.last_seenyfreshness.traces.last_seen(RFC3339 ononesi es nulo), y una advertenciatraces stalecuandofreshness.traces_staleestrue: los spans dejaron de llegar mientras los registros continuaron, así que investiga una regresión en torno afreshness.traces.last_seen, no una configuración ausente. Se deriva del upstream y se almacena en caché con un TTL de ~15 minutos.
Llámalo antes de cualquier investigación con alcance de servicio para que la selección de herramientas coincida con la telemetría real del servicio: omite las herramientas de trazas cuando traces sea absent, y cuando severity_set sea none o partial, analiza la gravedad de level_field en el cuerpo del registro en lugar de usar severity_filters. metrics siempre es unknown y dependencies no se completa en v1. Cuando logs y traces sean ambos absent, confirma el nombre con did_you_mean antes de concluir que el servicio no está monitorizado.
list_dashboards
name(string, opcional): Subcadena del nombre del dashboard, sin distinción entre mayúsculas y minúsculas. Reduce la lista ytotal.limit(integer, opcional): Dashboards por página, 1–200. Valor predeterminado: 100.cursor(string, opcional):next_cursorde la página anterior; pásalo junto con el mismoname.
Devuelve {"dashboards": [{"id", "name", "updated_at", "created_by", "locked"}], "total", "next_cursor"}, ordenados del más recientemente actualizado al más antiguo. created_by se omite si está vacío; locked se omite cuando la API no informa readonly. updated_at son segundos desde la época Unix como cadena. Una página termina antes para mantenerse por debajo de 32 KB; next_cursor no aparece en la última página.
get_dashboard
id(string, obligatorio): UUID del dashboard.region(string, opcional): Región para completar las consultas de los paneles. De forma predeterminada, se usa la región de la fuente de datos configurada.
El resultado añade locked en el nivel superior (false cuando la API lo omite) y created_by (el ID de usuario del autor).
Para crear una alerta en un panel, consulta last9://reference/alerting.
validate_dashboard
Solo lectura. Nunca crea ni actualiza dashboards. Acepta exactamente uno de dashboard_id o dashboard_definition.
dashboard_id(string, opcional): UUID del dashboard guardado que se va a validar.dashboard_definition(object, opcional): Cuerpo de un dashboard en línea sin guardar (simulación real). Un sobre de creación{dashboard, metadata}se desenvuelve para obtener su objetodashboard.start_time_iso/end_time_iso(string, opcional): Ventana de validación (RFC3339). Debe ser ≤ 24h.region(string, opcional): Región para ejecutar las consultas de los paneles.
Devuelve dashboard_validation/v1: clasificación de lint y ejecución por panel (data / no_data / invalid / error). Los resultados vacíos del primer día se clasifican como valid_no_data sin sondeos de diagnóstico. Un dashboard sin paneles devuelve success: false con error: "no_panels". Los campos de panel de otros formatos (Grafana gridPos, title, type y targets en paneles que necesitan consultas), así como los campos con tipos incorrectos, se enumeran por panel en shape_problems, se contabilizan en summary.panels_invalid_shape y hacen que overall sea fail.
preview_dashboard
Vista previa del lado del cliente, de solo lectura. Pasa dashboard (el objeto de dashboard sin guardar de una solicitud de creación) o id junto con ops (las mismas operaciones ordenadas que patch_dashboard). Los valores obligatorios start_time_iso y end_time_iso son marcas de tiempo RFC3339 que abarcan como máximo 24 horas. variables es opcional y contiene anulaciones escalares; de lo contrario, se aplican los valores del dashboard y los valores predeterminados integrados.
{
"id": "dashboard-id",
"ops": [{"op": "update_panel", "panel_id": "panel-id", "fields": {"name": "Request latency"}}],
"start_time_iso": "2026-10-01T08:00:00Z",
"end_time_iso": "2026-10-01T09:00:00Z"
}
Devuelve saved: false, un resumen de definición/consulta validation (incluido definition_error si hay una discrepancia con el esquema de creación) y panels con el estado ok / empty / error, las consultas resueltas y los diagnósticos de destino. Se ejecutan PromQL y LogJSON; los tipos de consulta no compatibles y las variables sin resolver hacen que el panel dé error. Los paneles válidos que no son de consulta son ok; las definiciones de panel no válidas son error. El parche diff incluye panels_added neto, panels_changed (incluidos los movimientos) y panels_removed, además de los cambios ordenados de las operaciones. Los ID y los índices de base cero identifican los paneles; los índices eliminados hacen referencia al dashboard original. Los paneles guardados deben tener ID distintos y no vacíos.
Límites: 50 paneles, 100 consultas, 100 operaciones, 65536 bytes JSON para cada uno de los dashboards de entrada y candidato, y un plazo de ejecución de 60 segundos. Las consultas resueltas que superen los 65536 bytes JSON se rechazan antes de ejecutarse. La evidencia conserva como máximo tres muestras y 1024 bytes JSON por destino, con truncated: true cuando se omiten muestras. Si un informe JSON supera los 262144 bytes, se produce un error con instrucciones para reducir la entrada.
No se crea ni se actualiza ningún dashboard. Esto comprueba el candidato con el esquema de creación y ejecuta las consultas mediante el validador existente; no garantiza la aceptación ni la representación del backend. Los ID asignados por el backend, los valores predeterminados, los campos descartados o las ediciones simultáneas pueden cambiar el resultado guardado. La vista previa usa las mismas operaciones de parche locales y los mismos valores predeterminados de versión de panel que una escritura.
create_dashboard
Solo para crear uno nuevo. Cuando esta llamada devuelva dashboard.id, edita los paneles con patch_dashboard (o reestructura con update_dashboard); no vuelvas a crear el dashboard para añadir, recortar o corregir paneles.
dashboard(object, obligatorio): Definición del dashboard connameypanels[]. Cada panel requierename,version,layout(x,y,w,h),visualization.typeyqueries[].metadata(object, opcional): Metadatos del dashboard: campos_categoryy_type(p. ej.,{"_category":"custom","_type":"metrics"}).
update_dashboard
Úsalo solo para reestructurar la mayor parte de un dashboard; para añadir, editar, eliminar o mover paneles individuales, usa patch_dashboard. Reemplazo completo por ID (mismo cuerpo que en la creación): se deben volver a enviar todos los paneles exactamente.
id(string, obligatorio): UUID del dashboard que se va a actualizar.dashboard(object, obligatorio): Cuerpo completo del dashboard de reemplazo (misma estructura que en la creación).metadata(object, opcional): Metadatos de reemplazo. Los dashboards del sistema de solo lectura devuelven un error recuperable de la herramienta.
patch_dashboard
Prefiérelo a update_dashboard para pequeños ajustes: los paneles sin cambios nunca se vuelven a enviar. Lee el dashboard, aplica ops en orden y lo vuelve a escribir, por lo que se sobrescribe cualquier edición realizada en otro lugar entre la lectura y la escritura. Devuelve {dashboard_id, changes[]}; se omiten las operaciones que el dashboard ya cumple y, si ninguna cambia nada, no se escribe nada. La diferencia se comprueba con una nueva lectura después de la escritura: fields de los paneles muestra los valores almacenados, not_stored enumera las claves que la API descartó y not_removed las claves que conservó pese a un null. Los valores de la diferencia que superen los 200 caracteres se acortan en el mismo lugar, conservando su tipo JSON.
id(string, obligatorio): UUID del dashboard que se va a modificar.ops(array, obligatorio): Operaciones aplicadas en orden; si alguna no es válida, no se escribe nada y el error la identifica comoops[i].add_panel:panel(objeto de panel completo;versiontiene el valor predeterminado 1),indexopcional (posición de inserción de base cero, al final de forma predeterminada).update_panel:panel_id,fields(claves de nivel superior del panel que se van a reemplazar;nullelimina una clave;idno se puede cambiar).remove_panel:panel_id.move_panel:panel_id,index(posición de base cero después del movimiento).set_title:title(el nombre del dashboard).set_variable:variable; reemplaza la variable con el mismotarget; de lo contrario, la añade.
delete_dashboard
id(string, obligatorio): UUID del dashboard que se va a eliminar. Los dashboards del sistema de solo lectura no se pueden eliminar.
set_dashboard_lock
id(string, obligatorio): UUID del dashboard.locked(boolean, obligatorio):truebloquea el dashboard;falselo desbloquea.
Herramienta de escritura; llámala solo cuando el usuario lo solicite explícitamente. Solo el autor del dashboard o un administrador de la organización puede bloquearlo o desbloquearlo, y los dashboards integrados siempre son de solo lectura. Devuelve {"id", "locked"} con el estado resultante. Mientras esté bloqueado, update_dashboard, patch_dashboard y delete_dashboard no realizan ninguna escritura y devuelven un error recuperable de la herramienta que comienza con dashboard is locked, identifica al autor y sugiere desbloquear el dashboard o copiarlo con create_dashboard.
list_dashboard_snapshots
dashboard_id(string, obligatorio): UUID del dashboard cuyas instantáneas se van a enumerar.
Devuelve solo metadatos (id, name, expires_at, etc.). Usa get_dashboard_snapshot para los datos congelados de los paneles.
get_dashboard_snapshot
id(string, obligatorio): UUID de la instantánea.
Devuelve la instantánea congelada completa, incluidos dashboard_definition, panel_data, time_range y variables.
delete_dashboard_snapshot
id(string, obligatorio): UUID de la instantánea que se va a eliminar.
get_profile_services
lookback_minutes/start_time_iso/end_time_iso(opcional): Ventana; prefiere el período retrospectivo o los límites ISO explícitos (60m de forma predeterminada).region(string, opcional): Anulación de región.
Devuelve los servicios que tienen datos de perfilado en la ventana. Llámalo antes de get_flamegraph / get_top_functions / get_profile_summary.
get_flamegraph
service(string, obligatorio): Nombre del servicio deget_profile_services.profile_type(string, opcional):cpu(predeterminado),allocowall. Fija un tipo al comparar ventanas.env/cluster/namespace/runtime(string, opcional): Filtros de alcance.limit(number, opcional): Máximo de filas de pila agregadas (1000 de forma predeterminada, máximo 10000).lookback_minutes/start_time_iso/end_time_iso/region(opcional).
Devuelve un árbol de gráfico de llamas anidado (name / value / self / children). truncated: true significa que se alcanzó el límite de filas de la API.
get_top_functions
Mismos filtros que get_flamegraph. Devuelve una clasificación de las funciones más activas según las muestras propias. Puede estar truncada; comprueba truncated.
get_profile_summary
Mismos filtros que get_flamegraph. Devuelve un breve análisis de triaje en lenguaje natural del perfil del servicio.
grafana_search_dashboards
query(string, opcional): Subcadena del título. Si está vacío, muestra una lista amplia (sujeta al límite de 5,000 filas).
Devuelve {"dashboards":[…], "truncated":bool} con uid, title, uri, url, type, tags. Usa uid con grafana_get_dashboard.
grafana_get_dashboard
uid(string, obligatorio): UID del dashboard de Grafana.full_json(boolean, opcional): Si es true, devuelve el JSON sin procesar de Grafana en lugar del resumen filtrado.
Resumen predeterminado: versión, etiquetas, variables de plantillas y tipo/fuente de datos/gridPos/objetivos promQL de cada panel. Los tipos de panel de complementos desconocidos aparecen en unsupportedPanelTypes.
grafana_list_folders
Sin parámetros. Devuelve el árbol de carpetas.
grafana_list_folder_dashboards
folder_uid(string, obligatorio): uid de la carpeta de Grafana.
Devuelve {"dashboards":[…], "truncated":bool} para los paneles de esa carpeta (paginados hasta 5,000).
grafana_list_datasources
Sin parámetros. Devuelve una proyección segura de las fuentes de datos (sin campos de credenciales).
Pruebas
Ejecuta el analizador de goroutines de pruebas sin ejecutar las pruebas de la aplicación:
make lint-test
Esto ejecuta el analizador testinggoroutine de Go en todos los paquetes, incluidos sus
archivos *_test.go, y lo comprueba con fixtures seguros e inseguros. Ambos flujos de trabajo de pruebas de CI ejecutan go vet -testinggoroutine ./... como un paso independiente, por lo que los resultados de pruebas de paquetes almacenados en caché no pueden eludir la aplicación del lint.
El paquete raíz también ejecuta el analizador como una prueba. Usa go test -count=1 ./...
para ejecutar toda la suite sin almacenar en caché esa comprobación: la caché de pruebas de Go no registra los archivos fuente de paquetes internos que lee el subproceso del analizador. Un go test ./... normal puede reutilizar el resultado satisfactorio de la prueba raíz después de que esos archivos cambien.
Fatal, Fatalf y FailNow en testing.T o testing.B deben ejecutarse en la goroutine de prueba principal. Los workers deben enviar los errores para que la goroutine principal los compruebe:
errs := make(chan error, 1)
go func() { errs <- doWork() }()
if err := <-errs; err != nil {
t.Fatal(err)
}
El analizador detecta llamadas directas en cierres go func; no es una comprobación interprocedimental general de aserciones ocultas tras llamadas a helpers arbitrarios.
Las pruebas de integración se omiten a menos que se establezca TEST_REFRESH_TOKEN.
