feat(monitoring): add Prometheus metrics endpoint and Grafana dashboards

Add observability stack with @willsoto/nestjs-prometheus for /metrics endpoint,
Prometheus scraping config, and 4 auto-provisioned Grafana dashboards
(API overview, database, search, business metrics).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 03:08:54 +07:00
parent b392bc3570
commit d99dfbafbc
13 changed files with 770 additions and 2 deletions

View File

@@ -23,6 +23,7 @@
"@nestjs/throttler": "^6.5.0",
"@paralleldrive/cuid2": "^3.3.0",
"@prisma/client": "^6.0.0",
"@willsoto/nestjs-prometheus": "^6.1.0",
"bcrypt": "^6.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.15.1",
@@ -36,6 +37,7 @@
"passport-local": "^1.0.0",
"pino": "^9.0.0",
"pino-pretty": "^13.0.0",
"prom-client": "^15.1.3",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.0",
"sanitize-html": "^2.17.2",

View File

@@ -6,6 +6,7 @@ import { NotificationsModule } from '@modules/notifications';
import { PaymentsModule } from '@modules/payments';
import { SubscriptionsModule } from '@modules/subscriptions';
import { AdminModule } from '@modules/admin';
import { MetricsModule } from '@modules/metrics';
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { CqrsModule } from '@nestjs/cqrs';
@@ -24,6 +25,7 @@ import { AppController } from './app.controller';
PaymentsModule,
SubscriptionsModule,
AdminModule,
MetricsModule,
// ── Rate Limiting ──
// Default: 60 requests per 60 seconds per IP

View File

@@ -0,0 +1 @@
export { MetricsModule } from './metrics.module';

View File

@@ -0,0 +1,69 @@
import { Module } from '@nestjs/common';
import {
PrometheusModule,
makeCounterProvider,
makeHistogramProvider,
makeGaugeProvider,
} from '@willsoto/nestjs-prometheus';
@Module({
imports: [
PrometheusModule.register({
path: '/metrics',
defaultMetrics: { enabled: true },
}),
],
providers: [
// ── HTTP Metrics ──
makeHistogramProvider({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
}),
makeCounterProvider({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code'],
}),
// ── Database Metrics ──
makeHistogramProvider({
name: 'db_query_duration_seconds',
help: 'Duration of database queries in seconds',
labelNames: ['operation', 'model'],
buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5],
}),
makeGaugeProvider({
name: 'db_pool_active_connections',
help: 'Number of active database connections',
}),
// ── Search Metrics ──
makeHistogramProvider({
name: 'search_query_duration_seconds',
help: 'Duration of search queries in seconds',
labelNames: ['collection', 'type'],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
}),
// ── Business Metrics ──
makeCounterProvider({
name: 'listings_created_total',
help: 'Total number of listings created',
labelNames: ['category'],
}),
makeCounterProvider({
name: 'payments_processed_total',
help: 'Total number of payments processed',
labelNames: ['status', 'method'],
}),
makeGaugeProvider({
name: 'active_subscriptions',
help: 'Number of active subscriptions',
labelNames: ['plan'],
}),
],
exports: [PrometheusModule],
})
export class MetricsModule {}

View File

@@ -0,0 +1,30 @@
{
"name": "@goodgo/mcp-servers",
"version": "0.1.0",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"lint": "eslint src/",
"test": "vitest run --passWithNoTests"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
"zod": "^3.24.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
},
"peerDependencies": {
"@nestjs/common": "^11.0.0",
"typesense": "^3.0.0"
},
"peerDependenciesMeta": {
"@nestjs/common": { "optional": true },
"typesense": { "optional": true }
}
}

View File

@@ -0,0 +1,149 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"links": [],
"panels": [
{
"title": "Request Rate (req/s)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (method)",
"legendFormat": "{{method}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "reqps",
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
},
{
"title": "Error Rate (5xx)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
"targets": [
{
"expr": "sum(rate(http_requests_total{status_code=~\"5..\"}[5m]))",
"legendFormat": "5xx errors/s",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "reqps",
"custom": { "drawStyle": "line", "fillOpacity": 10 },
"color": { "mode": "fixed", "fixedColor": "red" }
}
}
},
{
"title": "Request Latency (p50 / p95 / p99)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
"targets": [
{
"expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p50",
"refId": "A"
},
{
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p95",
"refId": "B"
},
{
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p99",
"refId": "C"
}
],
"fieldConfig": {
"defaults": {
"unit": "s",
"custom": { "drawStyle": "line", "fillOpacity": 5 }
}
}
},
{
"title": "Requests by Route",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (route)",
"legendFormat": "{{route}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "reqps",
"custom": { "drawStyle": "bars", "fillOpacity": 50 }
}
}
},
{
"title": "Requests by Status Code",
"type": "piechart",
"gridPos": { "h": 8, "w": 8, "x": 0, "y": 16 },
"targets": [
{
"expr": "sum(increase(http_requests_total[1h])) by (status_code)",
"legendFormat": "{{status_code}}",
"refId": "A"
}
]
},
{
"title": "Process Memory (RSS)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 8, "x": 8, "y": 16 },
"targets": [
{
"expr": "process_resident_memory_bytes{job=\"goodgo-api\"}",
"legendFormat": "RSS",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "bytes",
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
},
{
"title": "Node.js Event Loop Lag",
"type": "timeseries",
"gridPos": { "h": 8, "w": 8, "x": 16, "y": 16 },
"targets": [
{
"expr": "nodejs_eventloop_lag_seconds{job=\"goodgo-api\"}",
"legendFormat": "Event Loop Lag",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "s",
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
}
],
"schemaVersion": 39,
"tags": ["goodgo", "api"],
"templating": { "list": [] },
"time": { "from": "now-1h", "to": "now" },
"timepicker": {},
"timezone": "browser",
"title": "GoodGo API Overview",
"uid": "goodgo-api-overview",
"version": 1
}

View File

@@ -0,0 +1,117 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"links": [],
"panels": [
{
"title": "Listings Created (rate)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"expr": "sum(rate(listings_created_total[5m])) by (category)",
"legendFormat": "{{category}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops",
"custom": { "drawStyle": "bars", "fillOpacity": 50 }
}
}
},
{
"title": "Listings Created (total)",
"type": "stat",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
"targets": [
{
"expr": "sum(increase(listings_created_total[24h]))",
"legendFormat": "Last 24h",
"refId": "A"
}
],
"fieldConfig": {
"defaults": { "unit": "short" }
}
},
{
"title": "Payments Processed (rate)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
"targets": [
{
"expr": "sum(rate(payments_processed_total[5m])) by (status)",
"legendFormat": "{{status}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops",
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
},
{
"title": "Payment Success Rate",
"type": "gauge",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 },
"targets": [
{
"expr": "sum(rate(payments_processed_total{status=\"success\"}[1h])) / sum(rate(payments_processed_total[1h]))",
"legendFormat": "Success Rate",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "percentunit",
"min": 0,
"max": 1,
"thresholds": {
"steps": [
{ "color": "red", "value": null },
{ "color": "yellow", "value": 0.9 },
{ "color": "green", "value": 0.95 }
]
}
}
}
},
{
"title": "Active Subscriptions by Plan",
"type": "bargauge",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 16 },
"targets": [
{
"expr": "active_subscriptions",
"legendFormat": "{{plan}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "short",
"thresholds": {
"steps": [
{ "color": "blue", "value": null }
]
}
}
}
}
],
"schemaVersion": 39,
"tags": ["goodgo", "business"],
"templating": { "list": [] },
"time": { "from": "now-1h", "to": "now" },
"timepicker": {},
"timezone": "browser",
"title": "GoodGo Business Metrics",
"uid": "goodgo-business",
"version": 1
}

View File

@@ -0,0 +1,108 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"links": [],
"panels": [
{
"title": "Query Latency (p50 / p95 / p99)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"expr": "histogram_quantile(0.50, sum(rate(db_query_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p50",
"refId": "A"
},
{
"expr": "histogram_quantile(0.95, sum(rate(db_query_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p95",
"refId": "B"
},
{
"expr": "histogram_quantile(0.99, sum(rate(db_query_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p99",
"refId": "C"
}
],
"fieldConfig": {
"defaults": {
"unit": "s",
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
},
{
"title": "Query Rate by Operation",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
"targets": [
{
"expr": "sum(rate(db_query_duration_seconds_count[5m])) by (operation)",
"legendFormat": "{{operation}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops",
"custom": { "drawStyle": "bars", "fillOpacity": 50 }
}
}
},
{
"title": "Active DB Connections",
"type": "gauge",
"gridPos": { "h": 8, "w": 8, "x": 0, "y": 8 },
"targets": [
{
"expr": "db_pool_active_connections",
"legendFormat": "Active",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"thresholds": {
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 15 },
{ "color": "red", "value": 25 }
]
},
"max": 30
}
}
},
{
"title": "Slow Queries (> 100ms)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 16, "x": 8, "y": 8 },
"targets": [
{
"expr": "sum(rate(db_query_duration_seconds_bucket{le=\"0.1\"}[5m])) / sum(rate(db_query_duration_seconds_count[5m]))",
"legendFormat": "% queries < 100ms",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "percentunit",
"min": 0,
"max": 1,
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
}
],
"schemaVersion": 39,
"tags": ["goodgo", "database"],
"templating": { "list": [] },
"time": { "from": "now-1h", "to": "now" },
"timepicker": {},
"timezone": "browser",
"title": "GoodGo Database",
"uid": "goodgo-database",
"version": 1
}

View File

@@ -0,0 +1,82 @@
{
"annotations": { "list": [] },
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"links": [],
"panels": [
{
"title": "Search Latency (p50 / p95 / p99)",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"expr": "histogram_quantile(0.50, sum(rate(search_query_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p50",
"refId": "A"
},
{
"expr": "histogram_quantile(0.95, sum(rate(search_query_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p95",
"refId": "B"
},
{
"expr": "histogram_quantile(0.99, sum(rate(search_query_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p99",
"refId": "C"
}
],
"fieldConfig": {
"defaults": {
"unit": "s",
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
},
{
"title": "Search Query Rate",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 },
"targets": [
{
"expr": "sum(rate(search_query_duration_seconds_count[5m])) by (collection)",
"legendFormat": "{{collection}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops",
"custom": { "drawStyle": "line", "fillOpacity": 10 }
}
}
},
{
"title": "Search Query Rate by Type",
"type": "timeseries",
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 8 },
"targets": [
{
"expr": "sum(rate(search_query_duration_seconds_count[5m])) by (type)",
"legendFormat": "{{type}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops",
"custom": { "drawStyle": "bars", "fillOpacity": 50 }
}
}
}
],
"schemaVersion": 39,
"tags": ["goodgo", "search"],
"templating": { "list": [] },
"time": { "from": "now-1h", "to": "now" },
"timepicker": {},
"timezone": "browser",
"title": "GoodGo Search (Typesense)",
"uid": "goodgo-search",
"version": 1
}

View File

@@ -0,0 +1,12 @@
apiVersion: 1
providers:
- name: 'GoodGo Dashboards'
orgId: 1
folder: 'GoodGo'
type: file
disableDeletion: false
editable: true
options:
path: /var/lib/grafana/dashboards
foldersFromFilesStructure: false

View File

@@ -0,0 +1,9 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: true

View File

@@ -0,0 +1,16 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'goodgo-api'
metrics_path: '/metrics'
static_configs:
- targets: ['host.docker.internal:3001']
labels:
service: 'goodgo-api'
environment: 'development'
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']

175
pnpm-lock.yaml generated
View File

@@ -90,6 +90,9 @@ importers:
'@prisma/client':
specifier: ^6.0.0
version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)
'@willsoto/nestjs-prometheus':
specifier: ^6.1.0
version: 6.1.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(prom-client@15.1.3)
bcrypt:
specifier: ^6.0.0
version: 6.0.0
@@ -129,6 +132,9 @@ importers:
pino-pretty:
specifier: ^13.0.0
version: 13.1.3
prom-client:
specifier: ^15.1.3
version: 15.1.3
reflect-metadata:
specifier: ^0.2.0
version: 0.2.2
@@ -249,6 +255,31 @@ importers:
specifier: ^5.7.0
version: 5.9.3
libs/mcp-servers:
dependencies:
'@modelcontextprotocol/sdk':
specifier: ^1.12.1
version: 1.29.0(zod@3.25.76)
'@nestjs/common':
specifier: ^11.0.0
version: 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
typesense:
specifier: ^3.0.0
version: 3.0.5(@babel/runtime@7.29.2)
zod:
specifier: ^3.24.0
version: 3.25.76
devDependencies:
'@types/node':
specifier: ^22.0.0
version: 22.19.17
typescript:
specifier: ^5.7.0
version: 5.9.3
vitest:
specifier: ^3.0.0
version: 3.2.4(@types/node@22.19.17)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
packages:
'@alloc/quick-lru@5.2.0':
@@ -564,6 +595,12 @@ packages:
engines: {node: '>=6'}
hasBin: true
'@hono/node-server@1.19.13':
resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==}
engines: {node: '>=18.14.1'}
peerDependencies:
hono: ^4
'@hookform/resolvers@5.2.2':
resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==}
peerDependencies:
@@ -754,6 +791,16 @@ packages:
resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
engines: {node: '>=8'}
'@modelcontextprotocol/sdk@1.29.0':
resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==}
engines: {node: '>=18'}
peerDependencies:
'@cfworker/json-schema': ^4.1.1
zod: ^3.25 || ^4.0
peerDependenciesMeta:
'@cfworker/json-schema':
optional: true
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
@@ -1543,6 +1590,12 @@ packages:
'@webassemblyjs/wast-printer@1.14.1':
resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==}
'@willsoto/nestjs-prometheus@6.1.0':
resolution: {integrity: sha512-lrCEnJBBSzUIYWGR+PsZw1YXs1B9jzxFEuNAa3RzTxuFAFdI+sW7Fp52il/U/dX2MWoHc32x06OS0nm56QwyzQ==}
peerDependencies:
'@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
prom-client: ^15.0.0
'@xtuc/ieee754@1.2.0':
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
@@ -1728,6 +1781,9 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
bintrees@1.0.2:
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@@ -2278,10 +2334,24 @@ packages:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
eventsource-parser@3.0.6:
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
engines: {node: '>=18.0.0'}
eventsource@3.0.7:
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
engines: {node: '>=18.0.0'}
expect-type@1.3.0:
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
engines: {node: '>=12.0.0'}
express-rate-limit@8.3.2:
resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==}
engines: {node: '>= 16'}
peerDependencies:
express: '>= 4.11'
express@5.2.1:
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
engines: {node: '>= 18'}
@@ -2571,6 +2641,10 @@ packages:
help-me@5.0.0:
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
hono@4.12.12:
resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==}
engines: {node: '>=16.9.0'}
html-entities@2.6.0:
resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
@@ -2639,6 +2713,10 @@ packages:
resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==}
engines: {node: '>=12.22.0'}
ip-address@10.1.0:
resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
engines: {node: '>= 12'}
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@@ -2726,6 +2804,9 @@ packages:
jose@4.15.9:
resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==}
jose@6.2.2:
resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
joycon@3.1.1:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
@@ -2755,6 +2836,9 @@ packages:
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
json-schema-typed@8.0.2:
resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==}
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -3256,6 +3340,10 @@ packages:
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
engines: {node: '>= 6'}
pkce-challenge@5.0.1:
resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
engines: {node: '>=16.20.0'}
pkg-types@2.3.0:
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
@@ -3346,6 +3434,10 @@ packages:
process-warning@5.0.0:
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
prom-client@15.1.3:
resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==}
engines: {node: ^16 || ^18 || >=20}
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
@@ -3761,6 +3853,9 @@ packages:
resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==}
engines: {node: '>=6'}
tdigest@0.1.2:
resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==}
teeny-request@9.0.0:
resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==}
engines: {node: '>=14'}
@@ -4143,6 +4238,14 @@ packages:
resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==}
engines: {node: '>=18'}
zod-to-json-schema@3.25.2:
resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==}
peerDependencies:
zod: ^3.25.28 || ^4
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
@@ -4469,6 +4572,10 @@ snapshots:
yargs: 17.7.2
optional: true
'@hono/node-server@1.19.13(hono@4.12.12)':
dependencies:
hono: 4.12.12
'@hookform/resolvers@5.2.2(react-hook-form@7.72.1(react@18.3.1))':
dependencies:
'@standard-schema/utils': 0.3.0
@@ -4651,6 +4758,28 @@ snapshots:
'@lukeed/csprng@1.1.0': {}
'@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)':
dependencies:
'@hono/node-server': 1.19.13(hono@4.12.12)
ajv: 8.18.0
ajv-formats: 3.0.1(ajv@8.18.0)
content-type: 1.0.5
cors: 2.8.6
cross-spawn: 7.0.6
eventsource: 3.0.7
eventsource-parser: 3.0.6
express: 5.2.1
express-rate-limit: 8.3.2(express@5.2.1)
hono: 4.12.12
jose: 6.2.2
json-schema-typed: 8.0.2
pkce-challenge: 5.0.1
raw-body: 3.0.2
zod: 3.25.76
zod-to-json-schema: 3.25.2(zod@3.25.76)
transitivePeerDependencies:
- supports-color
'@napi-rs/wasm-runtime@0.2.12':
dependencies:
'@emnapi/core': 1.9.2
@@ -4823,8 +4952,7 @@ snapshots:
dependencies:
consola: 3.4.2
'@opentelemetry/api@1.9.1':
optional: true
'@opentelemetry/api@1.9.1': {}
'@package-json/types@0.0.12': {}
@@ -5448,6 +5576,11 @@ snapshots:
'@webassemblyjs/ast': 1.14.1
'@xtuc/long': 4.2.2
'@willsoto/nestjs-prometheus@6.1.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(prom-client@15.1.3)':
dependencies:
'@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
prom-client: 15.1.3
'@xtuc/ieee754@1.2.0': {}
'@xtuc/long@4.2.2': {}
@@ -5605,6 +5738,8 @@ snapshots:
binary-extensions@2.3.0: {}
bintrees@1.0.2: {}
bl@4.1.0:
dependencies:
buffer: 5.7.1
@@ -6187,8 +6322,19 @@ snapshots:
events@3.3.0: {}
eventsource-parser@3.0.6: {}
eventsource@3.0.7:
dependencies:
eventsource-parser: 3.0.6
expect-type@1.3.0: {}
express-rate-limit@8.3.2(express@5.2.1):
dependencies:
express: 5.2.1
ip-address: 10.1.0
express@5.2.1:
dependencies:
accepts: 2.0.0
@@ -6596,6 +6742,8 @@ snapshots:
help-me@5.0.0: {}
hono@4.12.12: {}
html-entities@2.6.0:
optional: true
@@ -6679,6 +6827,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
ip-address@10.1.0: {}
ipaddr.js@1.9.1: {}
is-arrayish@0.2.1: {}
@@ -6743,6 +6893,8 @@ snapshots:
jose@4.15.9: {}
jose@6.2.2: {}
joycon@3.1.1: {}
js-tokens@4.0.0: {}
@@ -6765,6 +6917,8 @@ snapshots:
json-schema-traverse@1.0.0: {}
json-schema-typed@8.0.2: {}
json-stable-stringify-without-jsonify@1.0.1: {}
json5@2.2.3: {}
@@ -7242,6 +7396,8 @@ snapshots:
pirates@4.0.7: {}
pkce-challenge@5.0.1: {}
pkg-types@2.3.0:
dependencies:
confbox: 0.2.4
@@ -7318,6 +7474,11 @@ snapshots:
process-warning@5.0.0: {}
prom-client@15.1.3:
dependencies:
'@opentelemetry/api': 1.9.1
tdigest: 0.1.2
prompts@2.4.2:
dependencies:
kleur: 3.0.3
@@ -7815,6 +7976,10 @@ snapshots:
tapable@2.3.2: {}
tdigest@0.1.2:
dependencies:
bintrees: 1.0.2
teeny-request@9.0.0:
dependencies:
http-proxy-agent: 5.0.0
@@ -8229,6 +8394,12 @@ snapshots:
yoctocolors-cjs@2.1.3: {}
zod-to-json-schema@3.25.2(zod@3.25.76):
dependencies:
zod: 3.25.76
zod@3.25.76: {}
zod@4.3.6: {}
zustand@5.0.12(@types/react@18.3.28)(react@18.3.1):