@jadenrazo/cloudcost-mcp 0.5.0 → 1.0.1

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.
@@ -121,6 +121,17 @@ function getRegionPriceMultipliers() {
121
121
  }
122
122
  return _regionPriceMultipliers;
123
123
  }
124
+ var _fallbackMeta = {};
125
+ function getFallbackMetadata(provider) {
126
+ if (_fallbackMeta[provider] !== void 0) return _fallbackMeta[provider];
127
+ const relPath = `${provider}-pricing/metadata.json`;
128
+ try {
129
+ _fallbackMeta[provider] = loadJsonFile(relPath);
130
+ } catch {
131
+ _fallbackMeta[provider] = null;
132
+ }
133
+ return _fallbackMeta[provider];
134
+ }
124
135
  function _resetLoaderCache() {
125
136
  _resourceEquivalents = null;
126
137
  _instanceMap = null;
@@ -149,6 +160,7 @@ export {
149
160
  getGcpStoragePricing,
150
161
  getGcpDiskPricing,
151
162
  getRegionPriceMultipliers,
163
+ getFallbackMetadata,
152
164
  _resetLoaderCache
153
165
  };
154
- //# sourceMappingURL=chunk-TRRAOOVF.js.map
166
+ //# sourceMappingURL=chunk-MNFT5YKN.js.map
package/dist/cli.js CHANGED
@@ -9,8 +9,8 @@ import {
9
9
  loadConfig,
10
10
  optimizeCost,
11
11
  whatIf
12
- } from "./chunk-E7KOWAMW.js";
13
- import "./chunk-TRRAOOVF.js";
12
+ } from "./chunk-6O2Y6MKU.js";
13
+ import "./chunk-MNFT5YKN.js";
14
14
 
15
15
  // src/cli.ts
16
16
  import { readFileSync, existsSync, statSync } from "fs";
package/dist/index.js CHANGED
@@ -13,6 +13,8 @@ import {
13
13
  convertCurrency,
14
14
  estimateCost,
15
15
  estimateCostSchema,
16
+ fileContentSchema,
17
+ filePathSchema,
16
18
  loadConfig,
17
19
  logger,
18
20
  mapInstance,
@@ -21,14 +23,20 @@ import {
21
23
  optimizeCost,
22
24
  optimizeCostSchema,
23
25
  parseTerraform,
26
+ planJsonSchema,
27
+ safeJsonParse,
28
+ sanitizeForMessage,
24
29
  setLogLevel,
30
+ stateJsonSchema,
25
31
  str,
32
+ tfvarsSchema,
26
33
  whatIf,
27
34
  whatIfSchema
28
- } from "./chunk-E7KOWAMW.js";
35
+ } from "./chunk-6O2Y6MKU.js";
29
36
  import {
37
+ getFallbackMetadata,
30
38
  getResourceEquivalents
31
- } from "./chunk-TRRAOOVF.js";
39
+ } from "./chunk-MNFT5YKN.js";
32
40
 
33
41
  // src/server.ts
34
42
  import { createRequire } from "module";
@@ -120,8 +128,11 @@ var getPricingSchema = z2.object({
120
128
  "network",
121
129
  "load_balancer",
122
130
  "nat_gateway",
123
- "kubernetes"
124
- ]).describe("Service category (network defaults to nat_gateway for backward compatibility)"),
131
+ "kubernetes",
132
+ "gpu"
133
+ ]).describe(
134
+ "Service category (network defaults to nat_gateway for backward compatibility; gpu returns accelerator pricing where supported)"
135
+ ),
125
136
  resource_type: z2.string().describe(
126
137
  "Instance type, storage type, or resource identifier (e.g. t3.large, gp3, Standard_D4s_v3)"
127
138
  ),
@@ -136,7 +147,8 @@ async function getPricing(params, pricingEngine) {
136
147
  network: "nat-gateway",
137
148
  load_balancer: "load-balancer",
138
149
  nat_gateway: "nat-gateway",
139
- kubernetes: "kubernetes"
150
+ kubernetes: "kubernetes",
151
+ gpu: "gpu"
140
152
  };
141
153
  const service = serviceMap[params.service] ?? params.service;
142
154
  const rawPrice = await pricingEngine.getPrice(
@@ -157,7 +169,14 @@ async function getPricing(params, pricingEngine) {
157
169
  price.attributes = attributes;
158
170
  }
159
171
  }
160
- return { price };
172
+ const meta = getFallbackMetadata(provider);
173
+ const fallback_metadata = meta ? {
174
+ last_updated: meta.last_updated,
175
+ source: meta.source,
176
+ sku_count: meta.sku_count,
177
+ refresh_script_version: meta.refresh_script_version
178
+ } : void 0;
179
+ return fallback_metadata ? { price, fallback_metadata } : { price };
161
180
  }
162
181
 
163
182
  // src/tools/analyze-plan.ts
@@ -307,10 +326,10 @@ function buildInventory(resources, warnings) {
307
326
  function parseTerraformPlan(planJson) {
308
327
  let plan;
309
328
  try {
310
- plan = JSON.parse(planJson);
329
+ plan = safeJsonParse(planJson);
311
330
  } catch (err) {
312
331
  const msg = err instanceof Error ? err.message : String(err);
313
- throw new Error(`Invalid plan JSON: ${msg}`);
332
+ throw new Error(`Invalid plan JSON: ${sanitizeForMessage(msg, 512)}`);
314
333
  }
315
334
  if (!Array.isArray(plan.resource_changes)) {
316
335
  throw new Error("Invalid plan JSON: missing or non-array resource_changes field");
@@ -375,7 +394,9 @@ function parseTerraformPlan(planJson) {
375
394
 
376
395
  // src/tools/analyze-plan.ts
377
396
  var analyzePlanSchema = z3.object({
378
- plan_json: z3.string().describe("Output of 'terraform show -json <planfile>' or 'terraform plan -json'"),
397
+ plan_json: planJsonSchema.describe(
398
+ "Output of 'terraform show -json <planfile>' or 'terraform plan -json'"
399
+ ),
379
400
  provider: z3.enum(["aws", "azure", "gcp"]).optional().describe("Target provider for cost estimation. If omitted, auto-detected from plan."),
380
401
  region: z3.string().optional().describe("Target region. If omitted, detected from plan resources."),
381
402
  currency: z3.enum(SUPPORTED_CURRENCIES).optional().default("USD").describe("Output currency")
@@ -567,17 +588,18 @@ function parseTerraformState(stateJson) {
567
588
  const warnings = [];
568
589
  let state;
569
590
  try {
570
- state = JSON.parse(stateJson);
591
+ state = safeJsonParse(stateJson);
571
592
  } catch (err) {
572
593
  const msg = err instanceof Error ? err.message : String(err);
573
- logger.error("Failed to parse Terraform state JSON", { error: msg });
594
+ const safeMsg = sanitizeForMessage(msg, 512);
595
+ logger.error("Failed to parse Terraform state JSON", { error: safeMsg });
574
596
  return {
575
597
  provider: "aws",
576
598
  region: PROVIDER_DEFAULTS.aws,
577
599
  resources: [],
578
600
  total_count: 0,
579
601
  by_type: {},
580
- parse_warnings: [`Invalid state JSON: ${msg}`]
602
+ parse_warnings: [`Invalid state JSON: ${safeMsg}`]
581
603
  };
582
604
  }
583
605
  if (!Array.isArray(state.resources)) {
@@ -657,14 +679,14 @@ function parseTerraformState(stateJson) {
657
679
 
658
680
  // src/tools/compare-actual.ts
659
681
  var compareActualSchema = z4.object({
660
- state_json: z4.string().describe("Contents of terraform.tfstate file (JSON)"),
682
+ state_json: stateJsonSchema.describe("Contents of terraform.tfstate file (JSON)"),
661
683
  files: z4.array(
662
684
  z4.object({
663
- path: z4.string().describe("File path"),
664
- content: z4.string().describe("File content (HCL)")
685
+ path: filePathSchema.describe("File path"),
686
+ content: fileContentSchema.describe("File content (HCL)")
665
687
  })
666
- ).optional().describe("Optional Terraform files to compare planned costs against actual"),
667
- tfvars: z4.string().optional().describe("Contents of terraform.tfvars file"),
688
+ ).max(2e3, "files array exceeds 2000 entries").optional().describe("Optional Terraform files to compare planned costs against actual"),
689
+ tfvars: tfvarsSchema.optional().describe("Contents of terraform.tfvars file"),
668
690
  provider: z4.enum(["aws", "azure", "gcp"]).optional().describe("Target cloud provider override. Defaults to auto-detecting from the state."),
669
691
  region: z4.string().optional().describe(
670
692
  "Target region override for pricing. Defaults to the region detected from the state."
@@ -787,11 +809,11 @@ import { z as z6 } from "zod";
787
809
  var detectAnomaliesSchema = z6.object({
788
810
  files: z6.array(
789
811
  z6.object({
790
- path: z6.string().describe("File path"),
791
- content: z6.string().describe("File content")
812
+ path: filePathSchema.describe("File path"),
813
+ content: fileContentSchema.describe("File content")
792
814
  })
793
- ),
794
- tfvars: z6.string().optional().describe("Variable overrides"),
815
+ ).max(2e3, "files array exceeds 2000 entries"),
816
+ tfvars: tfvarsSchema.optional().describe("Variable overrides"),
795
817
  provider: z6.enum(["aws", "azure", "gcp"]).describe("Target cloud provider"),
796
818
  region: z6.string().optional().describe("Target region"),
797
819
  currency: z6.enum(SUPPORTED_CURRENCIES).optional().default("USD").describe("Output currency for cost estimates. Defaults to USD."),
@@ -3,6 +3,7 @@ import {
3
3
  _resetLoaderCache,
4
4
  getAwsInstances,
5
5
  getAzureVmSizes,
6
+ getFallbackMetadata,
6
7
  getGcpComputePricing,
7
8
  getGcpDiskPricing,
8
9
  getGcpMachineTypes,
@@ -13,11 +14,12 @@ import {
13
14
  getRegionPriceMultipliers,
14
15
  getResourceEquivalents,
15
16
  getStorageMap
16
- } from "./chunk-TRRAOOVF.js";
17
+ } from "./chunk-MNFT5YKN.js";
17
18
  export {
18
19
  _resetLoaderCache,
19
20
  getAwsInstances,
20
21
  getAzureVmSizes,
22
+ getFallbackMetadata,
21
23
  getGcpComputePricing,
22
24
  getGcpDiskPricing,
23
25
  getGcpMachineTypes,
@@ -29,4 +31,4 @@ export {
29
31
  getResourceEquivalents,
30
32
  getStorageMap
31
33
  };
32
- //# sourceMappingURL=loader-VXYJYDIH.js.map
34
+ //# sourceMappingURL=loader-UWVEXYMR.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jadenrazo/cloudcost-mcp",
3
- "version": "0.5.0",
3
+ "version": "1.0.1",
4
4
  "description": "MCP server for multi-cloud cost analysis of Terraform, CloudFormation, Pulumi, and Bicep/ARM codebases",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,7 +39,10 @@
39
39
  "!dist/**/*.map",
40
40
  "data",
41
41
  "README.md",
42
- "LICENSE"
42
+ "LICENSE",
43
+ "STABILITY.md",
44
+ "MIGRATION.md",
45
+ "CHANGELOG.md"
43
46
  ],
44
47
  "main": "dist/index.js",
45
48
  "types": "dist/index.d.ts",
@@ -66,11 +69,17 @@
66
69
  },
67
70
  "dependencies": {
68
71
  "@cdktf/hcl2json": "^0.21.0",
69
- "@modelcontextprotocol/sdk": "^1.12.1",
72
+ "@modelcontextprotocol/sdk": "^1.25.2",
70
73
  "better-sqlite3": "^11.9.1",
71
74
  "yaml": "^2.8.3",
72
75
  "zod": "^3.24.4"
73
76
  },
77
+ "overrides": {
78
+ "hono": "^4.12.12",
79
+ "@hono/node-server": "^1.19.13",
80
+ "path-to-regexp": "^8.4.0",
81
+ "vite": "^7.3.2"
82
+ },
74
83
  "devDependencies": {
75
84
  "@eslint/js": "^10.0.1",
76
85
  "@types/better-sqlite3": "^7.6.13",