@jadenrazo/cloudcost-mcp 0.3.0 → 0.5.0

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
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">CloudCost MCP Server</h1>
2
2
 
3
3
  <p align="center">
4
- Multi-cloud cost analysis for Terraform powered by live pricing data from AWS, Azure, and GCP.
4
+ Multi-cloud cost analysis for Terraform, CloudFormation, Pulumi, and Bicep/ARM. Live pricing from AWS, Azure, and GCP.
5
5
  <br />
6
6
  Built on the <a href="https://modelcontextprotocol.io">Model Context Protocol</a> for seamless AI agent integration.
7
7
  </p>
@@ -28,16 +28,30 @@
28
28
 
29
29
  ---
30
30
 
31
- CloudCost MCP is a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server a standardized way to give AI assistants like Claude access to external tools that lets AI agents parse Terraform codebases, query real-time pricing data, and generate multi-cloud cost comparison reports. It connects directly to public pricing APIs from AWS and Azure no API keys or cloud credentials required. GCP pricing is bundled from public catalog data.
31
+ CloudCost MCP is a [Model Context Protocol](https://modelcontextprotocol.io) server that lets AI agents parse infrastructure-as-code across multiple formats (Terraform, CloudFormation, Pulumi, Bicep/ARM), query real-time pricing data, and generate multi-cloud cost comparison reports. It connects directly to public pricing APIs from AWS, Azure, and GCP. No API keys or cloud credentials required.
32
32
 
33
33
  ### What it does
34
34
 
35
- - Parses Terraform HCL files and extracts resource inventories with variable resolution
36
- - Queries live on-demand pricing from AWS Bulk Pricing CSV and Azure Retail Prices REST API
37
- - Maps equivalent resources across AWS, Azure, and GCP (compute, database, storage, networking, Kubernetes)
38
- - Generates cost estimates with per-resource breakdowns (monthly and yearly)
39
- - Compares costs across all three providers side-by-side in markdown, JSON, or CSV
40
- - Provides optimization recommendations: right-sizing, reserved pricing, provider switching
35
+ - Parses Terraform HCL files, CloudFormation templates, Pulumi stack exports, and Bicep/ARM templates with automatic format detection
36
+ - Extracts resource inventories with variable resolution, including referenced modules and OpenTofu `.tofu` files
37
+ - Queries live on-demand pricing from AWS Bulk Pricing CSV and Azure Retail Prices REST API; GCP via live Cloud Billing Catalog API with bundled fallback
38
+ - Maps equivalent resources across AWS, Azure, and GCP (compute, database, storage, networking, Kubernetes, container registries, secrets management, DNS)
39
+ - Generates cost estimates with per-resource breakdowns (monthly and yearly) across multiple currencies
40
+ - Compares costs across all three providers side-by-side in markdown, JSON, CSV, or FOCUS format
41
+ - Provides optimization recommendations: right-sizing, reserved pricing, provider switching, spot/preemptible instances
42
+ - Models hypothetical scenarios (instance type changes, region moves, commitment levels) without modifying Terraform files
43
+ - Projects costs over 3, 6, 12, and 36-month horizons with reserved instance comparisons
44
+ - Tags resources for cost attribution and groups report output by team, environment, or any custom label
45
+ - Posts cost estimate comments to pull requests via a reusable GitHub Actions composite action
46
+
47
+ ### Supported IaC Formats
48
+
49
+ | Format | Extensions | Auto-detected |
50
+ |---|---|---|
51
+ | Terraform/OpenTofu | `.tf`, `.tofu` | Yes |
52
+ | CloudFormation | `.yaml`, `.yml`, `.json`, `.template` | Yes |
53
+ | Pulumi | `.json` (stack export) | Yes |
54
+ | Bicep/ARM | `.json` (ARM template) | Yes |
41
55
 
42
56
  ---
43
57
 
@@ -113,7 +127,7 @@ node dist/index.js
113
127
 
114
128
  ## Tools
115
129
 
116
- The server exposes six MCP tools. Each accepts JSON input and returns structured JSON output.
130
+ The server exposes eleven MCP tools. Each accepts JSON input and returns structured JSON output.
117
131
 
118
132
  ### `analyze_terraform`
119
133
 
@@ -134,6 +148,7 @@ Calculate costs for parsed resources on a specific provider. Returns monthly and
134
148
  | `tfvars` | `string` | No | Variable overrides |
135
149
  | `provider` | `aws \| azure \| gcp` | Yes | Target provider for pricing |
136
150
  | `region` | `string` | No | Target region (auto-mapped if omitted) |
151
+ | `currency` | `string` | No | Output currency (default: `USD`). Supports: USD, EUR, GBP, JPY, CAD, AUD, INR, BRL |
137
152
 
138
153
  ### `compare_providers`
139
154
 
@@ -143,8 +158,9 @@ Full pipeline: parse Terraform, map resources across providers, fetch pricing, a
143
158
  |-----------|------|----------|-------------|
144
159
  | `files` | `{path, content}[]` | Yes | Terraform files |
145
160
  | `tfvars` | `string` | No | Variable overrides |
146
- | `format` | `markdown \| json \| csv` | No | Report format (default: `markdown`) |
161
+ | `format` | `markdown \| json \| csv \| focus` | No | Report format (default: `markdown`) |
147
162
  | `providers` | `string[]` | No | Providers to compare (default: all three) |
163
+ | `currency` | `string` | No | Output currency (default: `USD`). Supports: USD, EUR, GBP, JPY, CAD, AUD, INR, BRL |
148
164
 
149
165
  ### `get_equivalents`
150
166
 
@@ -178,6 +194,74 @@ Analyze Terraform resources and return optimization recommendations. Includes ri
178
194
  | `tfvars` | `string` | No | Variable overrides |
179
195
  | `providers` | `string[]` | No | Providers to evaluate (default: all three) |
180
196
 
197
+ ### `what_if`
198
+
199
+ Run hypothetical pricing scenarios against existing Terraform resources. Change instance types, regions, providers, or commitment levels and see the cost delta without modifying your actual configuration.
200
+
201
+ | Parameter | Type | Required | Description |
202
+ |-----------|------|----------|-------------|
203
+ | `files` | `{path, content}[]` | Yes | Terraform files |
204
+ | `tfvars` | `string` | No | Variable overrides |
205
+ | `scenarios` | `object[]` | Yes | Changes to model. Each specifies a resource name and the attributes to override |
206
+ | `providers` | `string[]` | No | Providers to evaluate (default: all three) |
207
+ | `currency` | `string` | No | Output currency (default: `USD`) |
208
+
209
+ **Example**: model the cost impact of switching compute from on-demand to spot across providers:
210
+
211
+ ```json
212
+ {
213
+ "files": [{ "path": "main.tf", "content": "..." }],
214
+ "scenarios": [
215
+ { "resource": "aws_instance.web", "pricing_model": "spot" },
216
+ { "resource": "aws_instance.app", "instance_type": "m6i.2xlarge" }
217
+ ]
218
+ }
219
+ ```
220
+
221
+ ### `analyze_plan`
222
+
223
+ Parse terraform plan JSON output for before/after cost-of-change analysis. Shows what resources are being added, changed, or destroyed and the cost impact of each change.
224
+
225
+ | Parameter | Type | Required | Description |
226
+ |-----------|------|----------|-------------|
227
+ | `plan_json` | `string` | Yes | JSON output from `terraform show -json planfile` |
228
+ | `provider` | `aws \| azure \| gcp` | No | Target provider for pricing (auto-detected if omitted) |
229
+ | `currency` | `string` | No | Output currency (default: `USD`) |
230
+
231
+ ### `compare_actual`
232
+
233
+ Parse `.tfstate` files to compare actual infrastructure costs vs estimates. Identifies drift between planned and deployed resources.
234
+
235
+ | Parameter | Type | Required | Description |
236
+ |-----------|------|----------|-------------|
237
+ | `state_json` | `string` | Yes | Contents of a `terraform.tfstate` file |
238
+ | `provider` | `aws \| azure \| gcp` | No | Target provider for pricing (auto-detected if omitted) |
239
+ | `currency` | `string` | No | Output currency (default: `USD`) |
240
+
241
+ ### `price_trends`
242
+
243
+ Query historical pricing trends and price change tracking. Shows how pricing has changed over time for specific resource types.
244
+
245
+ | Parameter | Type | Required | Description |
246
+ |-----------|------|----------|-------------|
247
+ | `provider` | `aws \| azure \| gcp` | Yes | Cloud provider |
248
+ | `service` | `string` | Yes | Service category |
249
+ | `resource_type` | `string` | Yes | Instance type, storage type, etc. |
250
+ | `region` | `string` | Yes | Cloud region |
251
+ | `period_days` | `number` | No | Lookback period in days (default: `90`) |
252
+
253
+ ### `detect_anomalies`
254
+
255
+ Cost anomaly detection with budget checks, price changes, concentration risk, and right-sizing hints. Analyzes parsed resources and flags potential cost issues.
256
+
257
+ | Parameter | Type | Required | Description |
258
+ |-----------|------|----------|-------------|
259
+ | `files` | `{path, content}[]` | Yes | IaC files to analyze |
260
+ | `tfvars` | `string` | No | Variable overrides |
261
+ | `provider` | `aws \| azure \| gcp` | No | Target provider (auto-detected if omitted) |
262
+ | `budget_monthly` | `number` | No | Monthly budget cap in USD |
263
+ | `currency` | `string` | No | Output currency (default: `USD`) |
264
+
181
265
  ---
182
266
 
183
267
  ## How Pricing Works
@@ -186,30 +270,32 @@ CloudCost uses a tiered approach to get the most accurate pricing available with
186
270
 
187
271
  ### AWS
188
272
 
189
- 1. **Live CSV streaming** (primary) For EC2 compute pricing, the server streams the AWS Bulk Pricing CSV for the target region line-by-line. This avoids loading the full ~267 MB file into memory. All on-demand compute prices for the region are extracted in a single pass and cached in SQLite for 24 hours. Concurrent requests for the same region share a single download.
273
+ 1. **Live CSV streaming** (primary). For EC2 compute pricing, the server streams the AWS Bulk Pricing CSV for the target region line-by-line. This avoids loading the full ~267 MB file into memory. All on-demand compute prices for the region are extracted in a single pass and cached in SQLite for 24 hours. Concurrent requests for the same region share a single download.
190
274
 
191
- 2. **Live JSON API** (secondary) For RDS (~24 MB), S3, ELB, and VPC, the server fetches regional JSON from the [AWS Price List Bulk API](https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json). These files are small enough to parse directly.
275
+ 2. **Live JSON API** (secondary). For RDS (~24 MB), S3, ELB, and VPC, the server fetches regional JSON from the [AWS Price List Bulk API](https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json). These files are small enough to parse directly.
192
276
 
193
- 3. **Fallback tables + interpolation** If live fetching fails (network issues, timeouts), the server falls back to built-in pricing tables covering 85+ EC2 and 29 RDS instance types. A size-interpolation algorithm estimates prices for unlisted sizes within known families by following AWS's predictable doubling pattern (e.g., `large` `xlarge` doubles the price).
277
+ 3. **Fallback tables + interpolation**. If live fetching fails (network issues, timeouts), the server falls back to built-in pricing tables covering 85+ EC2 and 29 RDS instance types. A size-interpolation algorithm estimates prices for unlisted sizes within known families by following AWS's predictable doubling pattern (e.g., `large` to `xlarge` doubles the price).
194
278
 
195
279
  ### Azure
196
280
 
197
- 1. **Live REST API** (primary) Queries the [Azure Retail Prices API](https://prices.azure.com/api/retail/prices) with OData filters for exact SKU matching (`armSkuName`). This is a fast, free, unauthenticated API that returns precise per-SKU pricing. Results are paginated and fully consumed.
281
+ 1. **Live REST API** (primary). Queries the [Azure Retail Prices API](https://prices.azure.com/api/retail/prices) with OData filters for exact SKU matching (`armSkuName`). Fast, free, unauthenticated. Returns precise per-SKU pricing. Results are paginated and fully consumed.
198
282
 
199
- 2. **Fallback tables + interpolation** If the API is unreachable, falls back to built-in tables covering 40+ VM sizes and 14 database tiers. A vCPU-proportional interpolation algorithm estimates prices for unlisted sizes.
283
+ 2. **Fallback tables + interpolation**. If the API is unreachable, falls back to built-in tables covering 40+ VM sizes and 14 database tiers. A vCPU-proportional interpolation algorithm estimates prices for unlisted sizes.
200
284
 
201
285
  ### GCP
202
286
 
203
- 1. **Bundled pricing data** — GCP's Cloud Billing Catalog API requires an API key, so the server ships with curated pricing data in `data/gcp-pricing/`. This covers Compute Engine machine types, Cloud SQL tiers, Cloud Storage classes, and Persistent Disk types across all major regions.
287
+ 1. **Live Cloud Billing Catalog API** (primary). Queries the GCP Cloud Billing Catalog API (`cloudbilling.googleapis.com`) using unauthenticated public endpoints. Results are cached for 24 hours.
288
+
289
+ 2. **Bundled pricing data** (fallback). If the live API is unreachable, falls back to curated pricing data in `data/gcp-pricing/` that ships with the package. Covers Compute Engine machine types, Cloud SQL tiers, Cloud Storage classes, and Persistent Disk types across all major regions.
204
290
 
205
- 2. **Infrastructure services** Load balancer, Cloud NAT, and GKE pricing use fixed public rates.
291
+ 3. **Infrastructure services**. Load balancer, Cloud NAT, and GKE pricing use fixed public rates.
206
292
 
207
293
  ### Pricing Source Transparency
208
294
 
209
295
  Every price returned includes a `pricing_source` attribute indicating its origin:
210
- - `"live"` fetched from a public API in real time
211
- - `"fallback"` from built-in tables (approximate, but reasonable for estimates)
212
- - `"bundled"` from bundled data files shipped with the package
296
+ - `"live"`: fetched from a public API in real time
297
+ - `"fallback"`: from built-in tables (approximate, but reasonable for estimates)
298
+ - `"bundled"`: from bundled data files shipped with the package
213
299
 
214
300
  All pricing data is cached in a local SQLite database (`~/.cloudcost/cache.db`) with a 24-hour TTL to minimize redundant API calls.
215
301
 
@@ -288,6 +374,12 @@ All configuration is optional. The server works out of the box with sensible def
288
374
  | `CLOUDCOST_CACHE_PATH` | `~/.cloudcost/cache.db` | SQLite cache file location |
289
375
  | `CLOUDCOST_LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
290
376
  | `CLOUDCOST_MONTHLY_HOURS` | `730` | Hours per month for cost calculations |
377
+ | `CLOUDCOST_INCLUDE_DATA_TRANSFER` | `false` | Include estimated data transfer costs in reports |
378
+ | `CLOUDCOST_PRICING_MODEL` | `on_demand` | Default pricing model: `on_demand`, `spot`, or `reserved` |
379
+ | `CLOUDCOST_RESOLVE_MODULES` | `true` | Expand referenced Terraform modules during parsing |
380
+ | `CLOUDCOST_BUDGET_MONTHLY` | | Monthly budget cap in USD. Triggers a warning when exceeded |
381
+ | `CLOUDCOST_BUDGET_PER_RESOURCE` | | Per-resource monthly budget cap in USD |
382
+ | `CLOUDCOST_BUDGET_WARN_PCT` | `80` | Percentage of budget at which a warning is surfaced (default: 80%) |
291
383
 
292
384
  ### Config File
293
385
 
@@ -351,11 +443,11 @@ Configuration priority: environment variables > config file > built-in defaults.
351
443
 
352
444
  ### Key Design Decisions
353
445
 
354
- - **Zero API keys** All pricing comes from public endpoints. AWS uses the unauthenticated Bulk Pricing files. Azure uses the free Retail Prices REST API. GCP uses bundled data from public catalog information.
355
- - **SQLite cache** A single `better-sqlite3` database caches all pricing lookups with configurable TTL. Shared across all tools per server lifetime.
356
- - **Streaming for large files** AWS EC2 pricing data (~267 MB CSV) is streamed line-by-line rather than loaded into memory. All prices for a region are extracted in one pass and cached.
357
- - **Graceful degradation** If any live pricing source is unavailable, the server falls back to built-in tables with size-interpolation. Every response includes the pricing source so the consumer knows the confidence level.
358
- - **ESM-only** Requires Node 20+. All internal imports use `.js` extensions.
446
+ - **Zero API keys.** All pricing comes from public endpoints. AWS uses the unauthenticated Bulk Pricing files. Azure uses the free Retail Prices REST API. GCP queries the Cloud Billing Catalog API with bundled fallback.
447
+ - **SQLite cache.** A single `better-sqlite3` database caches all pricing lookups with configurable TTL. Shared across all tools per server lifetime.
448
+ - **Streaming for large files.** AWS EC2 pricing data (~267 MB CSV) is streamed line-by-line rather than loaded into memory. Prices for a region are extracted in one pass and cached.
449
+ - **Graceful degradation.** If any live pricing source is unavailable, the server falls back to built-in tables with size-interpolation. Every response includes the pricing source so the consumer knows the confidence level.
450
+ - **ESM-only.** Requires Node 20+. All internal imports use `.js` extensions.
359
451
 
360
452
  ---
361
453
 
@@ -363,11 +455,19 @@ Configuration priority: environment variables > config file > built-in defaults.
363
455
 
364
456
  | Category | AWS | Azure | GCP |
365
457
  |----------|-----|-------|-----|
366
- | **Compute** | `aws_instance`, `aws_launch_template` | `azurerm_virtual_machine`, `azurerm_linux_virtual_machine` | `google_compute_instance` |
458
+ | **Compute** | `aws_instance` | `azurerm_virtual_machine`, `azurerm_linux_virtual_machine` | `google_compute_instance` |
367
459
  | **Database** | `aws_db_instance`, `aws_rds_cluster` | `azurerm_postgresql_flexible_server`, `azurerm_mysql_flexible_server` | `google_sql_database_instance` |
368
460
  | **Storage** | `aws_ebs_volume`, `aws_s3_bucket` | `azurerm_managed_disk`, `azurerm_storage_account` | `google_compute_disk`, `google_storage_bucket` |
369
461
  | **Network** | `aws_lb`, `aws_nat_gateway` | `azurerm_lb`, `azurerm_nat_gateway` | `google_compute_forwarding_rule` |
370
462
  | **Kubernetes** | `aws_eks_cluster` | `azurerm_kubernetes_cluster` | `google_container_cluster` |
463
+ | **Container Registries** | `aws_ecr_repository` | `azurerm_container_registry` | `google_artifact_registry_repository` |
464
+ | **Secrets Management** | `aws_secretsmanager_secret` | `azurerm_key_vault` | `google_secret_manager_secret` |
465
+ | **DNS** | `aws_route53_zone` | `azurerm_dns_zone` | `google_dns_managed_zone` |
466
+ | **API Gateway** | `aws_api_gateway_rest_api`, `aws_apigatewayv2_api` | `azurerm_api_management` | `google_api_gateway_api` |
467
+ | **WAF** | `aws_wafv2_web_acl` | `azurerm_web_application_firewall_policy` | |
468
+ | **OpenSearch** | `aws_opensearch_domain` | | |
469
+ | **Messaging** | `aws_sns_topic`, `aws_mq_broker` | `azurerm_servicebus_namespace`, `azurerm_eventhub_namespace` | `google_pubsub_topic` |
470
+ | **ML/AI** | `aws_sagemaker_endpoint`, `aws_sagemaker_notebook_instance` | | `google_vertex_ai_endpoint` |
371
471
 
372
472
  Instance type mapping covers 70+ AWS instance types (including Graviton/ARM families: m6g, m7g, c6g, c7g, r6g, r7g, t4g), 40+ Azure VM sizes, and 20+ GCP machine types with full bidirectional cross-provider mapping.
373
473
 
@@ -375,10 +475,8 @@ Instance type mapping covers 70+ AWS instance types (including Graviton/ARM fami
375
475
 
376
476
  ## Limitations
377
477
 
378
- - **Data transfer costs** are not included. Inter-region, inter-AZ, and internet egress charges are excluded from estimates.
379
- - **On-demand pricing only** by default. Prices reflect pay-as-you-go rates. The `optimize_cost` tool will recommend reserved instances and savings plans, but base estimates use on-demand.
380
- - **No Terraform module expansion**. Only direct resource blocks in the provided files are parsed. Resources defined inside referenced modules (`source = "..."`) are not resolved.
381
- - **GCP pricing is bundled**, not live. Prices may lag behind actual rates. AWS and Azure pricing is fetched in real time.
478
+ - **On-demand pricing only** by default. Prices reflect pay-as-you-go rates. The `optimize_cost` tool will recommend reserved instances and savings plans, but base estimates use on-demand. Pass `pricing_model: "spot"` in `what_if` scenarios to model spot/preemptible pricing.
479
+ - **GCP live pricing** is fetched from the Cloud Billing Catalog API with automatic fallback to bundled data when the API is unreachable. Bundled prices may lag slightly behind actual rates.
382
480
  - **First request latency**. The initial EC2 pricing lookup for a new AWS region may take 30-120 seconds as the CSV file is streamed. Subsequent lookups for the same region are instant (cached for 24 hours).
383
481
  - **Specialty instance types**. GPU instances (p4d, g5, etc.), high-memory (x2idn), and bare-metal types may fall back to interpolated pricing if not in the built-in tables and live fetch fails.
384
482
 
@@ -386,13 +484,49 @@ Instance type mapping covers 70+ AWS instance types (including Graviton/ARM fami
386
484
 
387
485
  ## Troubleshooting
388
486
 
389
- **$0 cost estimates** This usually means the instance type string in your Terraform code doesn't match any known pricing data. Check that you're using a real instance type (e.g., `t3.xlarge`) rather than a variable reference that wasn't resolved. Pass your `terraform.tfvars` content via the `tfvars` parameter to resolve variables.
487
+ **$0 cost estimates.** The instance type string in your Terraform code probably doesn't match any known pricing data. Check that you're using a real instance type (e.g., `t3.xlarge`) rather than a variable reference that wasn't resolved. Pass your `terraform.tfvars` content via the `tfvars` parameter to resolve variables.
390
488
 
391
- **Slow first request** The first EC2 pricing lookup for a new region streams the full AWS pricing CSV (~267 MB). This is a one-time cost per region; all subsequent lookups hit the local SQLite cache. Set `CLOUDCOST_LOG_LEVEL=debug` to see progress.
489
+ **Slow first request.** The first EC2 pricing lookup for a new region streams the full AWS pricing CSV (~267 MB). One-time cost per region; subsequent lookups hit the local SQLite cache. Set `CLOUDCOST_LOG_LEVEL=debug` to see progress.
392
490
 
393
- **Cache issues** Delete `~/.cloudcost/cache.db` to clear all cached pricing data. The cache rebuilds automatically on the next request.
491
+ **Cache issues.** Delete `~/.cloudcost/cache.db` to clear all cached pricing data. The cache rebuilds automatically on the next request.
492
+
493
+ **Node version.** Requires Node.js 20+. Uses ESM modules, Web Streams API (`TextDecoderStream`), and `AbortSignal.timeout()`.
494
+
495
+ ---
496
+
497
+ ## GitHub Actions
498
+
499
+ A reusable composite action is included at `.github/actions/cost-estimate/`. It detects changed `.tf` files, runs a cost comparison, and posts the result as a PR comment.
500
+
501
+ ```yaml
502
+ # .github/workflows/cost-estimate.yml
503
+ name: Terraform Cost Estimate
504
+
505
+ on:
506
+ pull_request:
507
+ types: [opened, synchronize]
508
+
509
+ jobs:
510
+ cost-estimate:
511
+ runs-on: ubuntu-latest
512
+ permissions:
513
+ contents: read
514
+ pull-requests: write
515
+ steps:
516
+ - uses: actions/checkout@v4
517
+ with:
518
+ fetch-depth: 0
519
+
520
+ - uses: ./.github/actions/cost-estimate
521
+ with:
522
+ github_token: ${{ secrets.GITHUB_TOKEN }}
523
+ # terraform_dir: "./terraform"
524
+ # providers: "aws,azure,gcp"
525
+ # format: "markdown"
526
+ # currency: "USD"
527
+ ```
394
528
 
395
- **Node version** The server requires Node.js 20+. It uses ESM modules, Web Streams API (`TextDecoderStream`), and `AbortSignal.timeout()`.
529
+ The action auto-detects the directory containing changed `.tf` files and skips gracefully when no Terraform changes are present in the PR.
396
530
 
397
531
  ---
398
532
 
@@ -414,17 +548,32 @@ src/
414
548
  ├── server.ts # MCP server setup, tool registration
415
549
  ├── config.ts # Config loader (defaults → file → env vars)
416
550
  ├── logger.ts # Structured logger
417
- ├── tools/ # MCP tool handlers + Zod schemas
418
- ├── parsers/ # HCL parsing, variable resolution
551
+ ├── currency.ts # Multi-currency conversion and formatting
552
+ ├── tools/
553
+ │ ├── ... # MCP tool handlers + Zod schemas
554
+ │ └── what-if.ts # Hypothetical scenario modeling
555
+ ├── parsers/
556
+ │ ├── ... # HCL parsing, variable resolution
557
+ │ ├── module-resolver.ts # Terraform module expansion
558
+ │ └── dependency-graph.ts # Resource dependency graph builder
419
559
  ├── pricing/
420
560
  │ ├── pricing-engine.ts # Router: dispatches to provider adapters
421
561
  │ ├── cache.ts # SQLite-backed pricing cache
422
562
  │ ├── aws/ # Bulk CSV streaming + JSON + fallback
423
563
  │ ├── azure/ # Retail Prices REST API + fallback
424
- │ └── gcp/ # Bundled pricing data loader
425
- ├── calculator/ # Cost calculations per resource type
564
+ │ └── gcp/
565
+ ├── bundled-loader.ts # Static bundled pricing fallback
566
+ │ └── cloud-billing-client.ts # Live GCP Cloud Billing Catalog API
567
+ ├── calculator/
568
+ │ ├── ... # Cost calculations per resource type
569
+ │ ├── projection.ts # Multi-horizon cost projections
570
+ │ ├── container-registry.ts
571
+ │ ├── secrets.ts
572
+ │ └── dns.ts
426
573
  ├── mapping/ # Cross-provider resource/instance mapping
427
- ├── reporting/ # Output formatters (markdown, JSON, CSV)
574
+ ├── reporting/
575
+ │ ├── ... # Markdown, JSON, CSV formatters
576
+ │ └── focus-report.ts # FOCUS-compliant export format
428
577
  └── types/ # Shared TypeScript interfaces
429
578
 
430
579
  data/
@@ -438,4 +587,4 @@ data/
438
587
 
439
588
  ## License
440
589
 
441
- MIT see [LICENSE](LICENSE) for details.
590
+ MIT. See [LICENSE](LICENSE) for details.
@@ -1,72 +1,107 @@
1
1
  {
2
2
  "us-central1": {
3
- "db-custom-1-3840": 0.0500,
4
- "db-custom-2-7680": 0.1000,
5
- "db-custom-4-15360": 0.2000,
3
+ "db-custom-1-3840": 0.0515,
4
+ "db-custom-2-7680": 0.1030,
5
+ "db-custom-4-15360": 0.2060,
6
6
  "storage_per_gb": 0.170,
7
7
  "ha_multiplier": 2.0
8
8
  },
9
9
  "us-east1": {
10
- "db-custom-1-3840": 0.0500,
11
- "db-custom-2-7680": 0.1000,
12
- "db-custom-4-15360": 0.2000,
10
+ "db-custom-1-3840": 0.0515,
11
+ "db-custom-2-7680": 0.1030,
12
+ "db-custom-4-15360": 0.2060,
13
13
  "storage_per_gb": 0.170,
14
14
  "ha_multiplier": 2.0
15
15
  },
16
- "europe-west1": {
17
- "db-custom-1-3840": 0.0550,
18
- "db-custom-2-7680": 0.1100,
19
- "db-custom-4-15360": 0.2200,
20
- "storage_per_gb": 0.187,
16
+ "us-east4": {
17
+ "db-custom-1-3840": 0.0580,
18
+ "db-custom-2-7680": 0.1161,
19
+ "db-custom-4-15360": 0.2322,
20
+ "storage_per_gb": 0.192,
21
21
  "ha_multiplier": 2.0
22
22
  },
23
23
  "us-west1": {
24
- "db-custom-1-3840": 0.0500,
25
- "db-custom-2-7680": 0.1000,
26
- "db-custom-4-15360": 0.2000,
24
+ "db-custom-1-3840": 0.0515,
25
+ "db-custom-2-7680": 0.1030,
26
+ "db-custom-4-15360": 0.2060,
27
27
  "storage_per_gb": 0.170,
28
28
  "ha_multiplier": 2.0
29
29
  },
30
- "asia-northeast1": {
31
- "db-custom-1-3840": 0.0575,
32
- "db-custom-2-7680": 0.1150,
33
- "db-custom-4-15360": 0.2300,
34
- "storage_per_gb": 0.196,
35
- "ha_multiplier": 2.0
36
- },
37
- "asia-south1": {
38
- "db-custom-1-3840": 0.0525,
39
- "db-custom-2-7680": 0.1050,
40
- "db-custom-4-15360": 0.2100,
30
+ "us-west4": {
31
+ "db-custom-1-3840": 0.0541,
32
+ "db-custom-2-7680": 0.1082,
33
+ "db-custom-4-15360": 0.2163,
41
34
  "storage_per_gb": 0.179,
42
35
  "ha_multiplier": 2.0
43
36
  },
44
- "australia-southeast1": {
45
- "db-custom-1-3840": 0.0590,
46
- "db-custom-2-7680": 0.1180,
47
- "db-custom-4-15360": 0.2360,
48
- "storage_per_gb": 0.201,
37
+ "europe-west1": {
38
+ "db-custom-1-3840": 0.0567,
39
+ "db-custom-2-7680": 0.1133,
40
+ "db-custom-4-15360": 0.2266,
41
+ "storage_per_gb": 0.187,
49
42
  "ha_multiplier": 2.0
50
43
  },
51
44
  "europe-west2": {
52
- "db-custom-1-3840": 0.0550,
53
- "db-custom-2-7680": 0.1100,
54
- "db-custom-4-15360": 0.2200,
45
+ "db-custom-1-3840": 0.0567,
46
+ "db-custom-2-7680": 0.1133,
47
+ "db-custom-4-15360": 0.2266,
55
48
  "storage_per_gb": 0.187,
56
49
  "ha_multiplier": 2.0
57
50
  },
51
+ "europe-west3": {
52
+ "db-custom-1-3840": 0.0577,
53
+ "db-custom-2-7680": 0.1154,
54
+ "db-custom-4-15360": 0.2307,
55
+ "storage_per_gb": 0.190,
56
+ "ha_multiplier": 2.0
57
+ },
58
58
  "europe-west4": {
59
- "db-custom-1-3840": 0.0550,
60
- "db-custom-2-7680": 0.1100,
61
- "db-custom-4-15360": 0.2200,
59
+ "db-custom-1-3840": 0.0567,
60
+ "db-custom-2-7680": 0.1133,
61
+ "db-custom-4-15360": 0.2266,
62
62
  "storage_per_gb": 0.187,
63
63
  "ha_multiplier": 2.0
64
64
  },
65
+ "europe-north1": {
66
+ "db-custom-1-3840": 0.0556,
67
+ "db-custom-2-7680": 0.1112,
68
+ "db-custom-4-15360": 0.2225,
69
+ "storage_per_gb": 0.184,
70
+ "ha_multiplier": 2.0
71
+ },
72
+ "asia-northeast1": {
73
+ "db-custom-1-3840": 0.0593,
74
+ "db-custom-2-7680": 0.1185,
75
+ "db-custom-4-15360": 0.2370,
76
+ "storage_per_gb": 0.196,
77
+ "ha_multiplier": 2.0
78
+ },
79
+ "asia-south1": {
80
+ "db-custom-1-3840": 0.0541,
81
+ "db-custom-2-7680": 0.1082,
82
+ "db-custom-4-15360": 0.2163,
83
+ "storage_per_gb": 0.179,
84
+ "ha_multiplier": 2.0
85
+ },
86
+ "australia-southeast1": {
87
+ "db-custom-1-3840": 0.0608,
88
+ "db-custom-2-7680": 0.1216,
89
+ "db-custom-4-15360": 0.2431,
90
+ "storage_per_gb": 0.201,
91
+ "ha_multiplier": 2.0
92
+ },
65
93
  "northamerica-northeast1": {
66
- "db-custom-1-3840": 0.0525,
67
- "db-custom-2-7680": 0.1050,
68
- "db-custom-4-15360": 0.2100,
94
+ "db-custom-1-3840": 0.0541,
95
+ "db-custom-2-7680": 0.1082,
96
+ "db-custom-4-15360": 0.2163,
69
97
  "storage_per_gb": 0.179,
70
98
  "ha_multiplier": 2.0
99
+ },
100
+ "southamerica-east1": {
101
+ "db-custom-1-3840": 0.0659,
102
+ "db-custom-2-7680": 0.1318,
103
+ "db-custom-4-15360": 0.2637,
104
+ "storage_per_gb": 0.218,
105
+ "ha_multiplier": 2.0
71
106
  }
72
107
  }
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "us-central1": { "STANDARD": 0.020, "NEARLINE": 0.010, "COLDLINE": 0.004, "ARCHIVE": 0.0012 },
3
3
  "us-east1": { "STANDARD": 0.020, "NEARLINE": 0.010, "COLDLINE": 0.004, "ARCHIVE": 0.0012 },
4
+ "us-east4": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
5
+ "us-west1": { "STANDARD": 0.020, "NEARLINE": 0.010, "COLDLINE": 0.004, "ARCHIVE": 0.0012 },
6
+ "us-west4": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
4
7
  "europe-west1": { "STANDARD": 0.020, "NEARLINE": 0.010, "COLDLINE": 0.004, "ARCHIVE": 0.0012 },
8
+ "europe-west2": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
9
+ "europe-west3": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
10
+ "europe-west4": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
11
+ "europe-north1": { "STANDARD": 0.020, "NEARLINE": 0.010, "COLDLINE": 0.004, "ARCHIVE": 0.0012 },
5
12
  "asia-southeast1": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
6
- "us-west1": { "STANDARD": 0.020, "NEARLINE": 0.010, "COLDLINE": 0.004, "ARCHIVE": 0.0012 },
7
13
  "asia-northeast1": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
8
- "asia-south1": { "STANDARD": 0.021, "NEARLINE": 0.011, "COLDLINE": 0.005, "ARCHIVE": 0.0013 },
9
- "australia-southeast1": { "STANDARD": 0.024, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
10
- "europe-west2": { "STANDARD": 0.022, "NEARLINE": 0.011, "COLDLINE": 0.005, "ARCHIVE": 0.0013 },
11
- "europe-west4": { "STANDARD": 0.022, "NEARLINE": 0.011, "COLDLINE": 0.005, "ARCHIVE": 0.0013 },
12
- "northamerica-northeast1": { "STANDARD": 0.021, "NEARLINE": 0.011, "COLDLINE": 0.005, "ARCHIVE": 0.0013 }
14
+ "asia-south1": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
15
+ "australia-southeast1": { "STANDARD": 0.023, "NEARLINE": 0.013, "COLDLINE": 0.006, "ARCHIVE": 0.0015 },
16
+ "northamerica-northeast1": { "STANDARD": 0.020, "NEARLINE": 0.010, "COLDLINE": 0.004, "ARCHIVE": 0.0012 },
17
+ "southamerica-east1": { "STANDARD": 0.035, "NEARLINE": 0.020, "COLDLINE": 0.007, "ARCHIVE": 0.0025 }
13
18
  }