sample-aws-pricing-calculator-mcp 1.3.3 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - **Verified Configs Catalog** - 21 per-service entries declaring the smallest config that produces a priced estimate, with documented gotchas.
13
13
  - **Lint refusal before save** - refuses estimates the calculator would render read-only or required-input, with actionable recovery hints.
14
14
  - **Import existing estimates** - download by URL or ID as JSON (for region swaps, modifications) or Markdown (for LLM analysis).
15
- - **Read computed costs** - `get_estimate_cost` hydrates a saved estimate in a headless browser to return the calculator's actual monthly cost; uses an on-demand Playwright/Chromium dependency that is only needed for this tool.
15
+ - **Read computed costs** - `get_estimate_cost` re-shares an estimate through the calculator (Update estimate → Share) so AWS persists the computed costs, then reads them back: upfront, monthly, and 12-month totals, the pricing date, and every per-line cost. Uses an on-demand Playwright/Chromium dependency only needed for this tool.
16
16
  - **Two transport modes** - stdio (default, for local clients like Claude Desktop, Kiro, Cursor) and optional HTTP (`MCP_TRANSPORT=http`) for hosted deployments.
17
17
 
18
18
  ## Example
@@ -78,7 +78,7 @@ Then point your client at the built bundle:
78
78
  | `build_estimate` | One-shot create + add services + lint preflight + save. Returns the shareable URL on success, or a structured envelope identifying which services need field discovery before retry. |
79
79
  | `export_estimate` | Save the in-flight estimate to calculator.aws and return a shareable URL. Refuses with an actionable `next_step` if the static linter predicts the saved blob would rehydrate read-only. |
80
80
  | `import_estimate` | Download an existing estimate by URL or ID. Returns JSON (raw) or Markdown. |
81
- | `get_estimate_cost` | Hydrate a saved estimate (by URL or ID) in a headless browser and return the calculator-computed monthly cost plus per-service rows. Requires Playwright and the Chromium browser — an on-demand dependency NOT installed by default; when either is missing the tool returns `cost_available:false` with the exact install commands instead of failing. |
81
+ | `get_estimate_cost` | Return the AWS-computed cost of a saved estimate (by URL or ID): upfront, monthly, and 12-month totals, the pricing date, and every per-line cost (complete, un-paginated). It re-shares the estimate through the calculator (Update estimate → Share) so AWS persists the costs, then reads them back — this saves a fresh copy under a new id (`priced_estimate_url`; the original is untouched). Requires Playwright and the Chromium browser — an on-demand dependency NOT installed by default; when either is missing the tool returns `cost_available:false` with the exact install commands instead of failing. **Runs only on an explicit user request for the cost — agents should not call it automatically after `export_estimate`/`build_estimate`.** |
82
82
  | `get_server_info` | Return version and capability information. |
83
83
 
84
84
  ## Project Structure
@@ -109,7 +109,7 @@ lib/
109
109
  store/ # In-flight estimate persistence
110
110
  estimate-store.js # Pluggable store (memory default)
111
111
  estimate-store-dynamodb.js # DynamoDB-backed store for stateless multi-replica deployments
112
- dom-cost.js # Playwright DOM scrape of the calculator's rendered cost (backs get_estimate_cost + the eval oracle; Playwright/Chromium is an on-demand dependency)
112
+ dom-cost.js # Playwright flows: re-share for computed costs (get_estimate_cost) + single-page DOM read (eval oracle). Playwright/Chromium is an on-demand dependency
113
113
  catalog/
114
114
  schema.json # JSON Schema for catalog entries
115
115
  services/ # Verified configs catalog (minimalConfig, traps, subServices)
@@ -226,13 +226,18 @@ AWS recalculates the actual costs when someone opens the link.
226
226
 
227
227
  ### Reading computed costs
228
228
 
229
- `get_estimate_cost` hydrates a saved estimate (by URL or ID) in headless Chromium and reads the calculator's rendered cost — the summary monthly cost plus per-service rows. The summary `monthly_cost` is authoritative; a per-row value can render stale/$0 until the calculator recomputes, so when the per-row total disagrees the response includes a `note` and you should not attribute per-row costs.
229
+ `get_estimate_cost` returns AWS's computed cost for a saved estimate (by URL or ID). Because the calculator prices in the browser, the tool re-shares the estimate through the UI — it presses **Update estimate** (recompute to current pricing) then **Share**, which makes AWS persist the costs — and then reads the resulting saved estimate via the public read API. The response carries `upfront_cost`, `monthly_cost`, `total_12_months_cost` (derived as upfront + 12×monthly, which is how the calculator defines the 12-month total), `pricing_as_of`, and `rows` — every group/line with its own monthly, upfront, and config. There is no pagination, so `rows_complete` is always true.
230
+
231
+ Because it carries an optional, heavyweight dependency (Playwright + Chromium, not installed by default), `get_estimate_cost` is meant to run **only when the user explicitly asks for the computed cost**. Agents should not invoke it automatically after `export_estimate` or `build_estimate` — those already return the shareable URL, which is the normal end of the flow.
232
+
233
+ Because Update+Share re-saves, the tool creates a **fresh copy under a new id** (returned as `priced_estimate_url`); the original estimate is unchanged. This avoids two dead ends we ruled out: scraping the rendered table (it paginates at 10 rows) and the Export→file download (which does not fire in headless Chromium).
230
234
 
231
235
  This is the only part of the server that needs a browser. **Playwright and the Chromium binary are an on-demand dependency, not installed by default.** The tool checks for them first and, when either is missing, returns `cost_available:false` with the exact install commands rather than failing:
232
236
 
233
237
  ```bash
234
238
  npm install playwright # if the library itself is missing
235
239
  npx playwright install chromium # downloads the ~150MB browser binary
240
+ npx playwright install-deps chromium # installs system dependencies
236
241
  ```
237
242
 
238
243
  Every other tool works without Playwright.
@@ -8,6 +8,7 @@
8
8
  "The template id for RDS for SQL Server is literally 'rdsForOracle' — this is a shared/legacy template id the calculator reuses across RDS engines, NOT a mis-mapping. A saved SQL Server estimate correctly carries estimateFor: 'rdsForOracle'; the pricing engine selects the SQL Server rate card from serviceCode + the columnFormIPM mappingDefinitionName 'rds-sqlserver-calc', not from the template id.",
9
9
  "GP3 storage silently requires companion fields. When storageType is 'General Purpose-GP3' the calculator ALWAYS emits gp3Iops (baseline 3000) and gp3Throughput (baseline 125 MiBps). The manifest does NOT mark them validations.required, so get_service_fields doesn't flag them and the lint can't refuse without them — but omitting them makes the pricing engine charge for (0 - 3000) IOPS and (0 - 125) MiBps, driving the whole estimate NEGATIVE (issue #44: a db.t3.small Single-AZ Express estimate rendered -35.98 USD instead of 48.02 USD). These are injected automatically via defaultFields; keep them present. Injecting them when a non-GP3 storage type is selected is harmless — the engine ignores them.",
10
10
  "Provisioned IOPS storage has its own companions: storageType 'Provisioned IOPS' (io1) needs provisioningIOPS, and 'Provisioned IOPS-IO2' (io2) needs provisionedIOPSIO2 (both baseline 1000). If you switch storageType to io1/io2, supply the matching IOPS field or the storage line will mis-price for the same reason GP3 does.",
11
+ "'Unbundled Licensing' is a dependent columnFormIPM cell fixed by instance class: it is TRUE for 7th-generation+ instances (m7i, r7i, r8i, x2m, ...) and FALSE for older ones, with no instance offering both (per the selector aggregations). Omitting it on a 7th-gen instance makes a directly-saved estimate silently drop the entire SQL Server/Windows licence (roughly halving the price), even though the calculator's edit view auto-selects it. The lint REFUSES to save a row that omits it when the aggregations pin it to a single value, and tells you the exact value to add (issue #48) — set e.g. {\"Unbundled Licensing\": {\"value\": \"TRUE\"}} in the row for a 7th-gen instance, FALSE for older ones.",
11
12
  "Instance configuration uses a columnFormIPM matrix — send the WRAPPED shape, NOT dotted-path keys. Shape: { columnFormIPM: { value: [{ <selectorId>: { value: '<n>' }, ... }] } }. Selector ids: 'Number of Nodes', 'Instance Type', 'Deployment Option', 'License Model', 'Database Edition', 'TermType', 'LeaseContractLength', 'PurchaseOption', 'Unbundled Licensing'. The literal string 'undefined' is the utilization slot key, and its inner value is itself an object: { value: { unit: '<percent>', selectedId: '%Utilized/Month' } }.",
12
13
  "storageType options: 'General Purpose' (gp2), 'General Purpose-GP3' (gp3), 'Provisioned IOPS' (io1), 'Provisioned IOPS-IO2' (io2), 'Magnetic'. Note the field id is 'storageType' for SQL Server (RDS for PostgreSQL uses 'storageVolume')."
13
14
  ],
@@ -36,7 +36,8 @@
36
36
  "The dataTransferForEC2 field is a composite widget. Its value must be an array of transfer entries, each with entryType, value, unit, and a region field. Valid entryTypes: INBOUND, OUTBOUND, INTRA_REGION.",
37
37
  "Unit must be \"tb_month\" or \"gb_month\".",
38
38
  "OUTBOUND entries require a \"toRegion\" field — use \"External\" for internet or \"other\" for other AWS regions. INBOUND entries require a \"fromRegion\" field with the same options. INTRA_REGION requires neither.",
39
- "All three entryTypes (INBOUND, OUTBOUND, INTRA_REGION) must be present in the array even if their value is \"0\"."
39
+ "All three entryTypes (INBOUND, OUTBOUND, INTRA_REGION) must be present in the array even if their value is \"0\".",
40
+ "tenancy is a dropdown with exactly three valid values: \"shared\" (default — used automatically whenever tenancy is omitted), \"dedicated\", \"host\". There is no validations.required on it, so nothing errors if you omit it — but if the user asks for 'Dedicated Instances' or 'Dedicated Host' pricing and you don't explicitly set tenancy, the estimate silently prices as Shared instead (usually cheaper, and not what was asked for; no lint predicate catches this because the saved blob is structurally valid). tenancy also gates which Reserved Instance models are visible (see the Reserved/Convertible trap above) and, when tenancy is \"host\", EstimateBuilder auto-promotes the plain EBS storage fields (storageType, storageAmount, gp3Iops, gp3Throughput, iops, iops2, storageAmountIo2) into their *DH equivalents (storageTypeDH, storageAmountDH, gp3IopsDH, gp3ThroughputDH, iopsDH, iops2DH) and drops the plain fields from the saved blob — set the plain field names as usual, you don't need the *DH names yourself. Don't set both a plain field and its *DH counterpart for host tenancy: an explicitly-set *DH field always wins over the auto-promoted value, so a stray *DH value (e.g. left over from reusing a config) silently overrides the plain field you intended."
40
41
  ],
41
42
  "minimalConfig": {
42
43
  "region": "us-east-1",
@@ -44,6 +45,7 @@
44
45
  "instanceType": "m5.large",
45
46
  "workload": 1,
46
47
  "selectedOS": "linux",
48
+ "tenancy": "shared",
47
49
  "pricingStrategy": "ondemand"
48
50
  },
49
51
  "defaultFields": {
@@ -7,7 +7,7 @@
7
7
  "traps": [
8
8
  "NAT Gateway has two mutually exclusive field sets, make sure to confirm with the user to either use 'Regional NAT Gateway' or 'NAT Gateway'. Regional (regionalNatGatewayCount, regionalNatGatewayAzCount, regionalNatGatewayDataProcessed) is the current UI default. Non-regional/legacy (numberOfGateways, dataProcessedPerNATGateway) also works. Using both will double-count costs.",
9
9
  "Regional NAT Gateway is not available in AWS European Sovereign Cloud",
10
- "All five fields are marked required by the manifest. When using one set, explicitly set the other set to \"0\" to avoid inflated defaults and linter refusals.",
10
+ "All five fields are marked required by the manifest. When using one set, explicitly zero out the other — but the two sets aren't symmetric: numberOfGateways and dataProcessedPerNATGateway accept \"0\" (manifest minValue 0). regionalNatGatewayCount and regionalNatGatewayAzCount do NOT — the manifest enforces minValue: 1, so \"0\" is rejected (\"value 0 is below minimum 1\"). To zero out the regional set, use regionalNatGatewayCount: \"1\", regionalNatGatewayAzCount: \"1\", regionalNatGatewayDataProcessed: 0 (1/1/0 GB) — this adds no cost and matches this entry's own minimalConfig. There is no literal all-zero combination for the regional set.",
11
11
  "Manifest defaults are 5 gateways × 3 AZs — wildly over-sized for a 'hello world'. minimalConfig overrides to 1×1; agents reading manifest defaults directly will over-estimate ~5×."
12
12
  ],
13
13
  "minimalConfig": {