Files
ag-bids-mcp/CHANGELOG.md
T
justin 03c6c540ef
CI / test (push) Successful in 17s
CI / build-push (push) Successful in 30s
Add fertilizer to input-cost tools (regional $/ton, USDA AgTransport)
input_cost_trend/input_cost_series now accept six fertilizers (urea, uan,
anhydrous, dap, map, potash) alongside diesel, with an optional `geo` region
(default Cornbelt). Real $/ton + MoM/YoY change + seasonal context.

- client: pass geo through; add input_cost_geographies
- server: expand VALID_INPUTS; geo param + docstrings
- format already unit-aware ($/ton) and geo-aware
- README tools table now lists the reference/trend + input-cost tools
- CHANGELOG: regional fertilizer input-cost release notes
- tests: fertilizer $/ton + region formatting

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:59:33 -04:00

219 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
Notes for clients/agents that consume the ag-bids MCP tools and the underlying
`ag-monitor` `/api/data/*` HTTP API. Newest first.
## 2026-05-30 — Regional fertilizer input costs (real $/ton + change)
Real U.S. **regional retail fertilizer prices** now feed the input-cost tools,
so the advisor can reason about fertilizer the same way it does diesel and grain
— actual dollars and the move, not an index. Source: **USDA AgTransport**
(monthly, `$/ton`, history back to 2023).
### Changed MCP tools (fertilizer added to the existing input-cost tools)
- **`input_cost_trend(item, geo?, years=10)`** — `item` now accepts six
fertilizers in addition to `diesel`: **`urea`, `uan`, `anhydrous`** (anhydrous
ammonia), **`dap`, `map`, `potash`**. Returns the latest real `$/ton`,
month-over-month and year-over-year change ($ and %), and seasonal context.
- New optional **`geo`** = AgTransport region; **defaults to `Cornbelt`**.
Other regions: `U.S. Gulf NOLA`, `Northern Plains`, `Southern Plains`,
`Southeast`, `Northeast`, `California`, `Pacific Northwest`, `South Central`,
`Central Florida`, `Tampa` (not every product is published for every region).
- `diesel` is unchanged — national U.S. `$/gal`, weekly; `geo` is ignored for it.
- **`input_cost_series(item, geo?)`** — raw monthly `$/ton` series for any
fertilizer (or the diesel `$/gal` weekly series), region-selectable.
Seasonal percentile for fertilizer is computed over a short history (2023+), so
it deepens over time; the price and YoY/MoM change are solid now.
### API
- `GET /api/data/input-cost-trend?item=&geo=&years=``geo` added.
- `GET /api/data/input-cost-series?item=&geo=``geo` added.
- `GET /api/data/input-cost-geographies?item=`**new**: lists the regions a
given input has data for (plus its `default_geo`).
### Example questions → tool calls
| Ask | Call |
|---|---|
| Cornbelt urea price and how it's moved | `input_cost_trend(item="urea")` |
| Anhydrous ammonia in the Southern Plains | `input_cost_trend(item="anhydrous", geo="Southern Plains")` |
| Potash $/ton history | `input_cost_series(item="potash")` |
| Which regions have DAP prices | (API) `GET /api/data/input-cost-geographies?item=dap` |
## 2026-05-30 — Grain price-received trends (real $ + change + seasonal)
New national/seasonal reference layer (USDA NASS, corn back to 1908) — the
macro benchmark to compare local cash bids against. Real prices and the change,
not an index.
### New MCP tools
- **`price_trend(commodity, geo="US", years=10)`** — monthly price *received by
farmers* ($/bu) with the move: latest real price, **month-over-month and
year-over-year change ($ and %)**, and seasonal context (percentile vs the
same month over the last N years, normal, and range). `geo` is `US` or any
2-letter state. Conclusions, not rows.
- **`price_series(commodity, geo="US", start_year?, end_year?)`** — raw monthly
series ($/bu) for charting/drill-down (defaults to the recent window).
### API
- `GET /api/data/price-trend?commodity=&geo=&years=` — computed trend (cents +
`change_cents`/`change_pct`/`yoy_*` + `seasonal`).
- `GET /api/data/price-series?commodity=&geo=&start_year=&end_year=` — raw series.
- `GET /api/data/price-geographies?commodity=` — which geos (US + states) exist.
All grain: corn/soy/wheat, all 50 states + US.
### Input-cost tools (real $ + change)
- **`input_cost_trend(item, years=10)`** — real input price with the move.
Currently `item="diesel"` (EIA U.S. retail $/gal, weekly, back to 1994):
latest price + week-over-week and year-over-year change + seasonal
percentile/range. (For current fertilizer $/ton, `current_input_price` (DTN)
still applies; more inputs extend this same tool.)
- **`input_cost_series(item)`** — raw historical series for an input.
- API: `GET /api/data/input-cost-trend?item=&years=`,
`GET /api/data/input-cost-series?item=`.
USDA stopped publishing dollar input prices in 2014, so these use real-dollar
sources (EIA) rather than an index.
## 2026-05-30 — Source geo + many more locations
### Per-source geo (API + MCP)
- **`GET /api/data/sources`** now returns location geo on every source:
`city, state, zip, county, latitude, longitude` (any may be `null`). The
AgriCharts-fed elevators carry exact coordinates; smaller sites carry
city/state/zip.
- **MCP `list_sources`** gained a **Location** column (`City, ST ZIP`).
`source_health` is unchanged in shape.
- Sources that are national or non-physical (CBOT futures, USDA AMS, DTN
fertilizer, hedge-to-arrive / "Direct" bid buckets) legitimately have `null`
geo. ~43 of ~51 sources are geo-tagged.
### Many more elevator locations
These flow through every data tool/endpoint automatically (`latest`, `history`,
`best`, `basis_movement`/`basis_detail`, `sources`, `deliveries`). No tool
signatures changed — just far more sources, all named `"<Co-op> — <Location>"`:
- **Heritage Cooperative** — new co-op, **23 central/eastern-Ohio locations**
(corn/soy/wheat).
- **Mercer Landmark** — expanded from 2 to **all ~16 locations**. Note: the two
old source names were renamed for continuity — `"Mercer Landmark — St Henry"`
`"Mercer Landmark — MPS St Henry"`, `"Mercer Landmark — Minster"`
`"Mercer Landmark — Heartland Minster"`.
- **Bambauer** — now both locations incl. **Pemberton**
(`"Bambauer — Jackson Center / New Knoxville"`, `"Bambauer — Pemberton"`);
the old `"Bambauer Jackson Center / New Knoxville, OH"` source was renamed to
the first of those.
If you cache source names, refresh them — several changed and many were added.
## 2026-05-29 — Futures price + change tool
### New MCP tool
- **`futures_quote(commodity, delivery?)`** — CBOT futures price and change for a
grain. Reports the latest price, today's **session open**, the prior day's
close, and **both** moves: **change since open** and **change on the day**
(vs the previous settle). With `delivery` (e.g. `"Jul 2026"`) it resolves the
listed contract that month prices against (e.g. `ZCN26`); without it, the
continuous nearby.
### API changes (`ag-monitor`)
- **`GET /api/data/futures?commodity=<grain>&delivery=<label?>`** — new endpoint.
Returns `{ commodity, delivery, contract, symbol, quote }` where `quote` is
`{ settle_date, open_cents, last_cents, prev_close_cents,
change_since_open_cents, change_on_day_cents, fetched_at }` (or `null` if no
data yet for that contract). `commodity` must be `corn`/`soy`/`wheat`.
- **`futures_quotes` table** gained an `open_cents` column; the futures scraper
now stores the session **Open** alongside the close. Rows captured before this
change have `open_cents = NULL`, so `change_since_open_cents` is `null` for
those until the next session is scraped.
- Futures now also pull at **:30 during the CBOT day session** (on top of the
hourly :00), so `last` and the changes track the session every ~30 min.
### Example questions → tool calls
| Ask | Call |
|---|---|
| Corn Jul futures and how far it's moved today | `futures_quote(commodity="corn", delivery="Jul 2026")` |
| Soy nearby futures, change on the day | `futures_quote(commodity="soy")` |
## 2026-05-29 — Flexible history + basis movement
### New MCP tools
- **`basis_movement(commodity?, source?, delivery?, days=30)`** — aggregated
basis trend, **one headline line per crop**. Averages every matching
(elevator × delivery) series to `avg basis first → last` over the window and
reports how far it moved. This is the cheap "how is basis moving overall"
view. All filters optional; `commodity` (if given) must be `corn`/`soy`/`wheat`.
- Positive move = **cash strengthened** vs futures (basis up); negative = weaker.
- Aggregation is intentional: divergent elevators can net to "flat". When the
aggregate looks flat or surprising, call `basis_detail` to see per-elevator
splits.
- **`basis_detail(commodity?, source?, delivery?, days=30)`** — the drill-down
for `basis_movement`. **One row per (elevator, crop, delivery)** series with
basis `first → last` and the move. Same optional filters.
Both tools do the aggregation **server-side (in the MCP)** and return compact
markdown — they do not dump every raw sample — to keep token usage low. Pattern:
call `basis_movement` first, then `basis_detail` (optionally filtered) only when
you need the breakdown.
### Changed MCP tools
- **`price_history`** — `commodity` is now **optional** (was required). Omit it
to span every crop. Output now groups by **(elevator, crop, delivery)**,
surfaces **basis first → last** in each series summary, and adds a **Futures**
column to the raw points table. Every filter (`commodity`, `source`,
`delivery`, `days`) is optional and AND'd — pivot per elevator, per crop, per
delivery, or any combination.
- Raw per-row points are still only included when the window has ≤ ~60 samples;
wider queries return the per-series summaries only.
- **`latest_prices`** — added the **`kind`** filter (`grain` | `fertilizer`) for
parity with the API. `commodity`, `source`, `delivery`, `kind` all optional.
### API changes (`ag-monitor`)
- **`GET /api/data/history`** — `commodity` query param is now **optional**.
When omitted, returns history across all crops. `source_id`, `delivery`, and
`days` (1730, default 30) remain optional. Response shape is unchanged:
`{ "commodity": <str|null>, "days": <int>, "rows": [...] }`. Each row carries
`fetched_at, source_id, source_name, commodity, delivery, bid_cents,
basis_cents, futures_cents` so callers can pivot client-side.
- `GET /api/data/latest` already supported optional `commodity`, `source`,
`delivery`, `kind` — unchanged.
### Conventions / gotchas
- All money fields are **integer cents**. `bid_cents`/`futures_cents` render as
`$/bu` (4 dp) for grain and `$/ton` (2 dp) for fertilizer; `basis_cents`
renders as a signed dollar value (2 dp), e.g. `-0.14`.
- Basis is only meaningful for grain — the basis tools skip non-grain rows and
any series with no basis on file.
- `source` filtering is by **exact** elevator display name (e.g.
`"Mercer Landmark — Minster"`). Use `list_sources` to get exact names.
### Example questions → tool calls
| Ask | Call |
|---|---|
| Basis movement overall, last 7 days | `basis_movement(days=7)` |
| Corn basis movement across all elevators | `basis_movement(commodity="corn")` |
| Basis movement at one elevator | `basis_movement(source="Mercer Landmark — Minster")` |
| Per-elevator basis breakdown for corn | `basis_detail(commodity="corn")` |
| Last 7 days of history from elevator X for corn | `price_history(commodity="corn", source="…", days=7)` |
| All-crop price history at one elevator | `price_history(source="…")` |
| Latest grain bids only | `latest_prices(kind="grain")` |