You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
npg_update_settingscould not set worker_shutdown_timeout, the nginx setting NPG v2.66.0 adds (seconds an old worker waits for its in-flight connections before exiting; 0 = wait for every connection, max 86400, omitted = 240). The field is part of upstream UpdateGlobalSettingsRequest, but it was missing from TOOL_KWARGS_WHITELIST in npg_mcp/upstream_models.py, so the client-side strict check rejected the call with ValueError: npg_update_settings: unknown field(s) ['worker_shutdown_timeout'] before any HTTP request — the only workaround was strict=false, i.e. exactly the silent-noop risk the whitelist exists to prevent. The key is now whitelisted (with a # v2.66.0 note) and npg_update_settings's description= names the field, its range and the 0 semantics in the same commit.
A regression test (tests/test_kwargs_whitelist.py::test_global_settings_v2_66_worker_shutdown_timeout_accepted) mirrors the existing v2.51 whitelist case and pins both 240 and the meaningful 0 value, including a mixed dict with keepalive_timeout.
Four new upstream GET /api/v1/traffic/* endpoints (NPG v2.66.0's per-host traffic analytics) had zero MCP coverage. Bridged as read-only tools in a new # ── Traffic ── section right after # ── Dashboard ──, all requiring dashboard:read: npg_get_traffic_summary (response bytes + requests per proxy host with each host's percentage, start/end, optional proxy_host_id via the timeseries/export siblings), npg_get_traffic_timeseries (start/end/proxy_host_id — host UUID, the literal unclassified, or omit — bucketed at 5min/hour/day), npg_get_traffic_realtime (fixed last 3 minutes in 10s buckets plus 30s average rates; deliberately no start/end), and npg_get_traffic_export (CSV).
npg_get_traffic_export calls c.get_text(...), not c.get(...): the route serves text/csv and client.get() unconditionally calls resp.json(), which would raise on a non-JSON body. It returns {"success": True, "data": "<csv text>"} following the npg_download_log_file / npg_get_metrics precedent, and its description says the payload is CSV, not JSON.
All four tools declare expand: bool = False (so the shared result cap applies and expand=true lifts it — enforced by tests/test_result_cap.py's read-only drift guard) and end their descriptions with the standard cap hint; every description stays under the 460-char DESC_LEN cap.
Description drifts left by v2.66.0 behaviour were corrected: npg_verify_challenge now documents 404 = proxy_host_id not found (the endpoint used to answer 500 for an unknown host), npg_create_sso_provider / npg_update_sso_provider document group_claim as max 64 chars (longer is now a 400 instead of a 500), and npg_get_dashboard_stats now states that without proxy_host_id only global rollup rows come back while an ID returns only that host's rows.
Generated artefacts were refreshed in the same commit: npg_mcp/permission_map.py regenerated from NPG's route registry (the four /traffic/* routes map to dashboard:read; 313 tools), tool-schemas.yaml + README.md + README.ko.md regenerated by regenerate_all_docs.py, and the new Traffic section registered with that generator's category map so the READMEs gain a Traffic | 4 tools row (29 categories).
Tier counts moved with the four read-only tools: read 137 → 141, standard 262 → 266, full 309 → 313 (destructive unchanged at 47), updated in .env.example, both READMEs and the tests/test_toolsets.py / tests/test_result_cap.py / tests/test_health.py pins.
Verified end to end against the local test stack (NPG v2.66.0 API) with a HEAD-pinned local image — local/npg-mcp:69933f957b9fd09379c42ab8b87214c30f8355be, not the stale published test image: 313 tools served, all four traffic tools returning live data (summary 288 bytes / 8 requests, timeseries hour point, realtime 10s buckets, CSV export containing the probe host) after driving requests through npg-test-proxy, worker_shutdown_timeout=120 accepted and reflected by npg_get_settings then restored to 0 (out-of-range 99999 rejected upstream with the documented 400), npg_verify_challenge surfacing the new 404 for an unknown host, and an SSO create/delete round-trip with a 64-char group_claim plus the 65-char 400 path. 23/23 live checks passed.
Release gates on this commit: 313 tools with no name changes, tier counts match tier_allowed() (read=141 / standard=266 / full=313, destructive 47), ruff check . clean, mypy npg_mcp/ clean, pytest 621 passed, docs --check exit 0 (460-char cap, longest 460), permission map in sync at 313 tools.
What's new
npg_get_traffic_summary — per-proxy-host traffic totals (response bytes, requests, percentage of the window total) for the last 24 h by default or an explicit RFC3339 window.
npg_get_traffic_timeseries — the bucketed traffic series (bytes + requests per point, resolution 5min/hour/day) for all hosts, one host UUID, or the literal unclassified bucket.
npg_get_traffic_realtime — live last-3-minutes traffic in 10s buckets with 30s average rates.
npg_get_traffic_export — the same per-host figures as CSV text for spreadsheets.
worker_shutdown_timeout is now settable through npg_update_settings (0 = wait for every connection, max 86400).
Breaking changes
(none) — all four tools are additive read-only GETs; no tool was renamed, retyped or removed, no parameter changed type, and no default changed. npg_update_settings accepts one previously-rejected kwargs key, so calls that used to fail client-side now reach the API. Deployments that pin NPG_TOOL_LEVEL see a larger surface (read 141, standard 266, full 313) because the new tools are read-tier.