Add price_trend / price_series MCP tools (USDA NASS grain)
CI / test (push) Failing after 0s
CI / build-push (push) Has been skipped

Real $/bu price + MoM/YoY change + seasonal percentile context for corn/soy/
wheat, US + states, deep history. Wraps the new /api/data/price-trend and
/api/data/price-series endpoints. Changelog updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 12:02:13 -04:00
parent 7b6661e3d9
commit 47cac9b521
5 changed files with 215 additions and 0 deletions
+57
View File
@@ -246,6 +246,63 @@ def basis_detail(
return fmt.fmt_basis_detail(_filter_source(payload, source))
@mcp.tool()
def price_trend(
commodity: Annotated[
str, Field(description="Grain: 'corn', 'soy', or 'wheat'.")
],
geo: Annotated[
str,
Field(description="'US' or a 2-letter state (e.g. 'OH', 'IA'). Default US."),
] = "US",
years: Annotated[
int, Field(ge=1, le=120, description="Baseline window for the seasonal normal/percentile."),
] = 10,
) -> str:
"""USDA NASS monthly price *received by farmers* ($/bu) with the change.
Returns the latest real price plus month-over-month and year-over-year moves
(dollars + percent) and where it sits seasonally (percentile vs the same
month over the last N years, normal, and range). This is the macro/seasonal
benchmark to compare local cash bids against — corn history goes back to 1908."""
cm = commodity.strip().lower()
g = geo.strip().upper()
with track("price_trend", commodity=cm, geo=g, years=years):
if cm not in VALID_GRAIN:
return f"`commodity` must be one of: {sorted(VALID_GRAIN)}"
return fmt.fmt_price_trend(client.price_trend(commodity=cm, geo=g, years=years))
@mcp.tool()
def price_series(
commodity: Annotated[
str, Field(description="Grain: 'corn', 'soy', or 'wheat'.")
],
geo: Annotated[
str, Field(description="'US' or a 2-letter state. Default US."),
] = "US",
start_year: Annotated[
int | None, Field(description="Optional first year (default: ~last 5 yrs of the full series).")
] = None,
end_year: Annotated[int | None, Field(description="Optional last year.")] = None,
) -> str:
"""Raw monthly price-received series ($/bu) for charting / drill-down.
Use price_trend for the headline; use this when you need the actual monthly
numbers. Defaults to the recent window unless you pass a start_year."""
cm = commodity.strip().lower()
g = geo.strip().upper()
with track("price_series", commodity=cm, geo=g):
if cm not in VALID_GRAIN:
return f"`commodity` must be one of: {sorted(VALID_GRAIN)}"
# Default to a readable recent window if no range given.
if start_year is None and end_year is None:
from datetime import datetime
start_year = datetime.utcnow().year - 5
payload = client.price_series(commodity=cm, geo=g, start_year=start_year, end_year=end_year)
return fmt.fmt_price_series(payload)
@mcp.tool()
def list_sources() -> str:
"""All active scrapers + their last-success timestamps and any pending failures."""