Location params on best_local_bid / latest_prices (zip / GPS + radius)
Thread zip/lat/lng/radius_miles through the client and both tools; friendly guard for the zip-XOR-gps rule. Formatters surface distance, the searched center, and the nearest-source hint when nothing is in range. - client: best()/latest() take zip/lat/lng/radius_miles - server: location params + docstrings (note Ohio-concentrated coverage) - format: distance column + center/nearest rendering - README + CHANGELOG + advisor prompt library updated - tests: location formatting cases Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+57
-6
@@ -48,19 +48,50 @@ VALID_INPUT = {"lime", "map", "potash"}
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def _location_error(zip: str | None, lat: float | None, lng: float | None) -> str | None:
|
||||
"""Friendly guard for the (zip XOR gps) rule before hitting the API."""
|
||||
if zip and (lat is not None or lng is not None):
|
||||
return "Pass either `zip` or `lat`+`lng`, not both."
|
||||
if (lat is None) != (lng is None):
|
||||
return "GPS needs both `lat` and `lng`."
|
||||
return None
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def best_local_bid(
|
||||
commodity: Annotated[
|
||||
str, Field(description="Grain to look up: 'corn', 'soy' (soybeans), or 'wheat'.")
|
||||
],
|
||||
zip: Annotated[
|
||||
str | None,
|
||||
Field(description="Your 5-digit ZIP — limits the search to nearby elevators."),
|
||||
] = None,
|
||||
lat: Annotated[
|
||||
float | None, Field(description="Your latitude (use with `lng` instead of a ZIP).")
|
||||
] = None,
|
||||
lng: Annotated[float | None, Field(description="Your longitude (use with `lat`).")] = None,
|
||||
radius_miles: Annotated[
|
||||
float | None,
|
||||
Field(description="Search radius in miles around the location (default 50). "
|
||||
"Ignored unless a ZIP or lat/lng is given."),
|
||||
] = None,
|
||||
) -> str:
|
||||
"""Return the highest local bid for *this calendar month's* delivery for
|
||||
the given grain. This is the "where should I haul today" answer."""
|
||||
the given grain — the "where should I haul today" answer.
|
||||
|
||||
Pass a `zip` (or `lat`+`lng`) with optional `radius_miles` to restrict to
|
||||
elevators near the farmer; results then show the distance to each. Without a
|
||||
location it considers every scraped elevator. If nothing is in range, it
|
||||
reports the nearest source and its distance (a coverage gap, not an error).
|
||||
Note: cash-bid coverage is currently concentrated in Ohio."""
|
||||
commodity = commodity.strip().lower()
|
||||
with track("best_local_bid", commodity=commodity):
|
||||
with track("best_local_bid", commodity=commodity, zip=zip or "", radius=radius_miles or 0):
|
||||
if commodity not in VALID_GRAIN:
|
||||
return f"`commodity` must be one of: {sorted(VALID_GRAIN)}"
|
||||
payload = client.best(commodity)
|
||||
err = _location_error(zip, lat, lng)
|
||||
if err:
|
||||
return err
|
||||
payload = client.best(commodity, zip=zip, lat=lat, lng=lng, radius_miles=radius_miles)
|
||||
return fmt.fmt_best(commodity, payload)
|
||||
|
||||
|
||||
@@ -132,14 +163,34 @@ def latest_prices(
|
||||
str | None,
|
||||
Field(description="Filter to one commodity kind: 'grain' or 'fertilizer'. Omit for all."),
|
||||
] = None,
|
||||
zip: Annotated[
|
||||
str | None,
|
||||
Field(description="Your 5-digit ZIP — keep only nearby sources, nearest first."),
|
||||
] = None,
|
||||
lat: Annotated[
|
||||
float | None, Field(description="Your latitude (use with `lng` instead of a ZIP).")
|
||||
] = None,
|
||||
lng: Annotated[float | None, Field(description="Your longitude (use with `lat`).")] = None,
|
||||
radius_miles: Annotated[
|
||||
float | None,
|
||||
Field(description="Search radius in miles around the location (default 50). "
|
||||
"Ignored unless a ZIP or lat/lng is given."),
|
||||
] = None,
|
||||
) -> str:
|
||||
"""Snapshot of the latest scraped bid per (source, commodity, delivery).
|
||||
|
||||
Every filter is optional and AND'd together — pivot by elevator, crop,
|
||||
delivery, or kind in any combination."""
|
||||
delivery, or kind in any combination. Pass a `zip` (or `lat`+`lng`) with
|
||||
optional `radius_miles` to keep only elevators near the farmer, sorted
|
||||
nearest-first with the distance shown."""
|
||||
cm = commodity.strip().lower() if commodity else None
|
||||
with track("latest_prices", commodity=cm, source=source, delivery=delivery, kind=kind):
|
||||
payload = client.latest(commodity=cm, source=source, delivery=delivery, kind=kind)
|
||||
with track("latest_prices", commodity=cm, source=source, delivery=delivery, kind=kind,
|
||||
zip=zip or "", radius=radius_miles or 0):
|
||||
err = _location_error(zip, lat, lng)
|
||||
if err:
|
||||
return err
|
||||
payload = client.latest(commodity=cm, source=source, delivery=delivery, kind=kind,
|
||||
zip=zip, lat=lat, lng=lng, radius_miles=radius_miles)
|
||||
return fmt.fmt_latest(payload)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user