Skip to content

CheckPriceResult declares fields check_price has never returned (consumers work around it with casts) #116

Description

@lonniev

CheckPriceResult in frontend/src/lib/mcp.ts:911-920 declares three fields that
the check_price tool has never returned, so all three are permanently undefined.

// frontend/src/lib/mcp.ts:911-920
export interface CheckPriceResult {
  success: boolean;
  tool_id?: string;
  tool_name?: string;
  base_cost?: number;       // <- never returned
  effective_cost?: number;  // <- never returned
  cost?: number;            // alternate field name some wheel versions return  <- never returned
  error?: string;
  error_code?: string;
}

The UI is NOT broken — but only because both consumers work around the type

Unlike the sibling frontends, optionality genuinely consumes checkPrice(), and
both call sites already fall through to the real field names via as unknown casts:

  • frontend/src/components/Optionality.tsx:1990-1994 — deal price on the setup screen:
    const eff = r.effective_cost ?? r.cost
      ?? ((r as unknown as { effective_cost_api_sats?: number }).effective_cost_api_sats)
      ?? ((r as unknown as { base_cost_api_sats?: number }).base_cost_api_sats);
  • frontend/src/components/Welcome.tsx:36-50readCost() walks the same four
    candidates in the same order.

In both, the first two candidates are statically dead and the third is what
actually supplies the number. Prices render correctly today. Hence sev/low,
not sev/medium — this is a correctness/clarity defect, not a user-visible outage.

Why it is still worth fixing

The workaround is load-bearing and invisible. The declared type says the typed
fields work and the cast fields are the fallback; the truth is the exact opposite.
The comment on cost — "alternate field name some wheel versions return" — is
also inaccurate: no wheel version has ever returned it.

Fixing the interface lets both call sites drop the as unknown casts entirely and
read r.effective_cost_api_sats ?? r.base_cost_api_sats against a type that is
actually true.

Evidence (live, wheel 0.89.0)

check_price against https://goodearth-mcp.fastmcp.app/mcp returns:

{"success":true,"tool_id":"...","tool_name":"...","constraints_enabled":false,
 "constraint_effects":[],"pricing_type":"flat",
 "base_cost_api_sats":0,"effective_cost_api_sats":0}

The wheel's authoritative source is tollbooth-dpyc/src/tollbooth/tools/pricing.py
(build_pricing_preview), which sets only base_cost_api_sats / effective_cost_api_sats.

This was never right

The unsuffixed spellings are not a rename — they never existed. Verified with a
pickaxe over the SDK's entire history:

git log --all -S '"effective_cost"' -- '*.py'   -> no commits
git log --all -S '"base_cost"'      -- '*.py'   -> no commits

*_api_sats has been the shape since 0.1.126 (2026-03-27, commit ca3a22f).
So this type has been fiction since the day it was written, in every copy.

Fix

Rename the three fields to the wheel's actual names (and drop cost, which the
SDK has never returned):

base_cost_api_sats?: number;
effective_cost_api_sats?: number;

goodearth-mcp/frontend/src/lib/mcp.ts already carries the corrected interface,
plus pricing_type / constraints_enabled / constraint_effects, if you want a
reference shape.

Related

The same stale interface is present in every frontend that copied this file
(excalibur-mcp, cypher-mcp, roastify-mcp, optionality-mcp). The structural cause —
five hand-copied forks of the identity/MCP-client stack — is filed separately in
lonniev/dpyc-community.


Filed by Scout after verifying the field names against the live service and the
SDK source. No source files were modified.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/retriageReplay marker: re-fire Porter after a funding outagearea/uiFrontend / UIsev/lowMinor / cosmetictype/bugA defect in existing behavior

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions