swmm-climate — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited swmm-climate (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Part of Agentic SWMM — install the project first for the executable toolchain (aiswmm CLI, SWMM solver, MCP servers).
[TIMESERIES] text lines[RAINGAGES] section.format_rainfall.py expects a header row and at minimum:
timestamp: date-time string, default format %Y-%m-%d %H:%Mrainfall_mm_per_hr: rainfall intensity in mm/hrOptional extensions:
station_id (or another column via --station-column) to carry multiple stations in one file.--input and/or using --input-glob.--window-start and --window-end (inclusive).Accepted rainfall units (--value-units):
mm_per_hr (aliases: mm/hr, mm/h)in_per_hr (aliases: in/hr, in/h)Unit policy (--unit-policy):
strict: only mm_per_hr accepted.convert_to_mm_per_hr: supported units are converted to mm_per_hr..dat input contractFor SWMM-native rainfall .dat files (e.g. <series> YYYY M D HH MM value), use --input-dat <path> and declare row units via --dat-value-units:
mm_per_hr, in_per_hr (intensities)mm_per_day, in_per_day (24h volumes; divided by 24 to mm/hr)In .dat mode the --window-start / --window-end filters expect %Y-%m-%d. Use --default-station-id to override the series token taken from the .dat row. --input-dat may be repeated to batch multiple .dat files but cannot be mixed with --input / --input-glob.
Via the MCP tool, pass inputDatPaths: [<path>] and datValueUnits: "mm_per_day" (or another supported unit) instead of inputCsvPath.
Temporal validation:
--timestamp-policy strict default; optional sort).scripts/format_rainfall.pytimeseries text block for SWMMscripts/build_raingage_section.py[RAINGAGES] snippet referencing a timeseries name.--station-id to choose one station’s series.[TIMESERIES])MCP wrapper location:
mcp/swmm-climate/server.jsExposed tools:
format_rainfallbuild_raingage_sectionpython3 skills/swmm-climate/scripts/format_rainfall.py \
--input skills/swmm-climate/examples/rainfall_event.csv \
--out-json runs/swmm-climate/example_rainfall.json \
--out-timeseries runs/swmm-climate/example_timeseries.txt \
--series-name TS_EVENTpython3 skills/swmm-climate/scripts/format_rainfall.py \
--input skills/swmm-climate/examples/rainfall_multi_station.csv \
--station-column station_id \
--series-name-template 'TS_EVENT_{station_safe}' \
--out-json runs/swmm-climate/example_multi_station.json \
--out-timeseries runs/swmm-climate/example_multi_station.txtpython3 skills/swmm-climate/scripts/format_rainfall.py \
--input skills/swmm-climate/examples/rainfall_batch_rg1.csv \
--input skills/swmm-climate/examples/rainfall_batch_rg2.csv \
--window-start '2025-06-01 00:05' \
--window-end '2025-06-01 00:15' \
--series-name TS_BATCH \
--out-json runs/swmm-climate/example_batch_windowed.json \
--out-timeseries runs/swmm-climate/example_batch_windowed.txtpython3 skills/swmm-climate/scripts/build_raingage_section.py \
--gage-id RG1 \
--rainfall-json runs/swmm-climate/example_multi_station.json \
--station-id RG1 \
--interval-min 5 \
--out-text runs/swmm-climate/example_raingage.txt \
--out-json runs/swmm-climate/example_raingage.jsonUse design_storm.py to synthesise a hyetograph from a return period and IDF coefficients when no measured rainfall data exists. The output format matches format_rainfall.py so build_inp --rainfall-json consumes it unchanged.
| Method | When to use | Required inputs |
|---|---|---|
chicago (Keifer-Chu) | IDF formula coefficients available | --form, coefficient flags, --return-period, --duration |
alternating_block | Explicit IDF table (duration → intensity) | --idf-csv or --idf-json, --duration |
CN form (--form CN): q = 167·A1·(1+C·lgP)/(t+b)^n [L/s/ha → converted to mm/hr] Flags: --a1, --C, --b, --n
Generic form (--form generic): i = a/(t+b)^c [mm/hr] Flags: --a-coeff, --b, --c-exp
python3 skills/swmm-climate/scripts/design_storm.py \
--method chicago \
--form CN \
--a1 10.0 \
--C 0.811 \
--b 11.0 \
--n 0.711 \
--return-period 2 \
--duration 120 \
--dt 5 \
--out-json runs/swmm-climate/storm_p2y.json \
--out-timeseries runs/swmm-climate/storm_p2y.txtExecuted output:
{
"ok": true,
"out_json": "/tmp/design_storm_test/storm_p2y.json",
"out_timeseries": "/tmp/design_storm_test/storm_p2y.txt",
"series_name": "TS_DESIGN_P2Y_120MIN",
"series_names": [
"TS_DESIGN_P2Y_120MIN"
],
"rows": 24,
"stations": 1,
"interval_minutes": 5
}python3 skills/swmm-climate/scripts/design_storm.py \
--method alternating_block \
--idf-json '[{"duration_min":5,"intensity_mm_per_hr":60},{"duration_min":10,"intensity_mm_per_hr":45},{"duration_min":30,"intensity_mm_per_hr":28},{"duration_min":60,"intensity_mm_per_hr":18},{"duration_min":120,"intensity_mm_per_hr":11}]' \
--duration 120 \
--dt 5 \
--return-period 2 \
--out-json runs/swmm-climate/storm_ab_p2y.json \
--out-timeseries runs/swmm-climate/storm_ab_p2y.txtgenerate_design_storm on the swmm-climate MCP server (third tool after format_rainfall and build_raingage_section). Pass camelCase equivalents: method, duration, outJson, outTimeseries, form, returnPeriod, dt, r, a1, cCoeff, b, n, aCoeff, cExp, idfCsv, idfJson, seriesName.
swmm-builder path in this repo still assembles a single raingage reference per build step.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.