pricing.md 1.0.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/SKILL.md +593 -0
  4. package/data/tools/algolia.json +101 -0
  5. package/data/tools/amplitude.json +73 -0
  6. package/data/tools/auth0-b2b.json +175 -0
  7. package/data/tools/auth0-b2c.json +183 -0
  8. package/data/tools/buildkite.json +104 -0
  9. package/data/tools/checkly.json +112 -0
  10. package/data/tools/circleci.json +102 -0
  11. package/data/tools/clerk.json +109 -0
  12. package/data/tools/cloudflare.json +71 -0
  13. package/data/tools/contentful.json +71 -0
  14. package/data/tools/convex.json +74 -0
  15. package/data/tools/courier.json +80 -0
  16. package/data/tools/deno-deploy.json +145 -0
  17. package/data/tools/depot.json +66 -0
  18. package/data/tools/descope.json +130 -0
  19. package/data/tools/engagespot.json +84 -0
  20. package/data/tools/fly-io.json +53 -0
  21. package/data/tools/github-actions.json +64 -0
  22. package/data/tools/honeycomb.json +65 -0
  23. package/data/tools/inngest.json +80 -0
  24. package/data/tools/launchdarkly.json +81 -0
  25. package/data/tools/mergent.json +116 -0
  26. package/data/tools/mixpanel.json +70 -0
  27. package/data/tools/mux.json +164 -0
  28. package/data/tools/neon.json +173 -0
  29. package/data/tools/netlify.json +99 -0
  30. package/data/tools/new-relic.json +104 -0
  31. package/data/tools/novu.json +94 -0
  32. package/data/tools/orama.json +153 -0
  33. package/data/tools/paddle.json +46 -0
  34. package/data/tools/planetscale-postgres.json +108 -0
  35. package/data/tools/planetscale-vitess.json +82 -0
  36. package/data/tools/plausible.json +75 -0
  37. package/data/tools/posthog.json +101 -0
  38. package/data/tools/postmark.json +93 -0
  39. package/data/tools/railway.json +161 -0
  40. package/data/tools/render.json +94 -0
  41. package/data/tools/resend.json +135 -0
  42. package/data/tools/sanity.json +98 -0
  43. package/data/tools/sendgrid.json +78 -0
  44. package/data/tools/sentry.json +276 -0
  45. package/data/tools/statsig.json +70 -0
  46. package/data/tools/storyblok.json +121 -0
  47. package/data/tools/stripe.json +46 -0
  48. package/data/tools/supabase.json +100 -0
  49. package/data/tools/tigris.json +70 -0
  50. package/data/tools/trigger-dev.json +90 -0
  51. package/data/tools/turso.json +226 -0
  52. package/data/tools/unosend.json +88 -0
  53. package/data/tools/upstash.json +85 -0
  54. package/data/tools/vercel.json +146 -0
  55. package/data/tools/weaviate.json +93 -0
  56. package/data/tools/workos.json +127 -0
  57. package/dist/src/index.d.ts +2 -0
  58. package/dist/src/index.js +23 -0
  59. package/dist/src/registry/registry.d.ts +52 -0
  60. package/dist/src/registry/registry.js +216 -0
  61. package/dist/src/schema/pricing.d.ts +191 -0
  62. package/dist/src/schema/pricing.js +89 -0
  63. package/dist/src/server.d.ts +3 -0
  64. package/dist/src/server.js +32 -0
  65. package/dist/src/tools/compare-tools.d.ts +61 -0
  66. package/dist/src/tools/compare-tools.js +115 -0
  67. package/dist/src/tools/estimate-cost.d.ts +44 -0
  68. package/dist/src/tools/estimate-cost.js +61 -0
  69. package/dist/src/tools/find-cheapest.d.ts +54 -0
  70. package/dist/src/tools/find-cheapest.js +93 -0
  71. package/dist/src/tools/get-pricing.d.ts +51 -0
  72. package/dist/src/tools/get-pricing.js +23 -0
  73. package/dist/src/tools/search-tools.d.ts +55 -0
  74. package/dist/src/tools/search-tools.js +52 -0
  75. package/package.json +40 -0
@@ -0,0 +1,191 @@
1
+ import { z } from "zod";
2
+ export declare const CategoryEnum: z.ZodEnum<{
3
+ hosting: "hosting";
4
+ database: "database";
5
+ auth: "auth";
6
+ email: "email";
7
+ payments: "payments";
8
+ monitoring: "monitoring";
9
+ "ai-api": "ai-api";
10
+ storage: "storage";
11
+ "ci-cd": "ci-cd";
12
+ search: "search";
13
+ analytics: "analytics";
14
+ "feature-flags": "feature-flags";
15
+ cms: "cms";
16
+ queues: "queues";
17
+ edge: "edge";
18
+ testing: "testing";
19
+ scheduling: "scheduling";
20
+ notifications: "notifications";
21
+ }>;
22
+ export type Category = z.infer<typeof CategoryEnum>;
23
+ export declare const PricingModelEnum: z.ZodEnum<{
24
+ free: "free";
25
+ flat_rate: "flat_rate";
26
+ per_seat: "per_seat";
27
+ usage_based: "usage_based";
28
+ tiered: "tiered";
29
+ hybrid: "hybrid";
30
+ custom: "custom";
31
+ }>;
32
+ export type PricingModel = z.infer<typeof PricingModelEnum>;
33
+ export declare const SwitchingCostEnum: z.ZodEnum<{
34
+ "drop-in": "drop-in";
35
+ moderate: "moderate";
36
+ significant: "significant";
37
+ architectural: "architectural";
38
+ }>;
39
+ export type SwitchingCost = z.infer<typeof SwitchingCostEnum>;
40
+ export declare const FreshnessCategoryEnum: z.ZodEnum<{
41
+ stable: "stable";
42
+ volatile: "volatile";
43
+ }>;
44
+ export type FreshnessCategory = z.infer<typeof FreshnessCategoryEnum>;
45
+ export declare const SlugSchema: z.ZodString;
46
+ export declare const UsageMetricSchema: z.ZodObject<{
47
+ name: z.ZodString;
48
+ unit: z.ZodString;
49
+ pricePerUnit: z.ZodNumber;
50
+ unitQuantity: z.ZodNumber;
51
+ includedQuantity: z.ZodDefault<z.ZodNumber>;
52
+ tieredPricing: z.ZodOptional<z.ZodArray<z.ZodObject<{
53
+ upTo: z.ZodNullable<z.ZodNumber>;
54
+ pricePerUnit: z.ZodNumber;
55
+ }, z.core.$strip>>>;
56
+ }, z.core.$strip>;
57
+ export type UsageMetric = z.infer<typeof UsageMetricSchema>;
58
+ export declare const FeatureSchema: z.ZodObject<{
59
+ name: z.ZodString;
60
+ included: z.ZodBoolean;
61
+ }, z.core.$strip>;
62
+ export type Feature = z.infer<typeof FeatureSchema>;
63
+ export declare const PricingTierSchema: z.ZodObject<{
64
+ name: z.ZodString;
65
+ slug: z.ZodString;
66
+ pricingModel: z.ZodEnum<{
67
+ free: "free";
68
+ flat_rate: "flat_rate";
69
+ per_seat: "per_seat";
70
+ usage_based: "usage_based";
71
+ tiered: "tiered";
72
+ hybrid: "hybrid";
73
+ custom: "custom";
74
+ }>;
75
+ basePrice: z.ZodNullable<z.ZodNumber>;
76
+ billingPeriod: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
77
+ monthly: "monthly";
78
+ annual: "annual";
79
+ }>>>;
80
+ annualDiscount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
81
+ seatPrice: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
82
+ usageMetrics: z.ZodDefault<z.ZodArray<z.ZodObject<{
83
+ name: z.ZodString;
84
+ unit: z.ZodString;
85
+ pricePerUnit: z.ZodNumber;
86
+ unitQuantity: z.ZodNumber;
87
+ includedQuantity: z.ZodDefault<z.ZodNumber>;
88
+ tieredPricing: z.ZodOptional<z.ZodArray<z.ZodObject<{
89
+ upTo: z.ZodNullable<z.ZodNumber>;
90
+ pricePerUnit: z.ZodNumber;
91
+ }, z.core.$strip>>>;
92
+ }, z.core.$strip>>>;
93
+ features: z.ZodDefault<z.ZodArray<z.ZodObject<{
94
+ name: z.ZodString;
95
+ included: z.ZodBoolean;
96
+ }, z.core.$strip>>>;
97
+ limits: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
98
+ }, z.core.$strip>;
99
+ export type PricingTier = z.infer<typeof PricingTierSchema>;
100
+ export declare const PortabilityInfoSchema: z.ZodObject<{
101
+ switchingCost: z.ZodEnum<{
102
+ "drop-in": "drop-in";
103
+ moderate: "moderate";
104
+ significant: "significant";
105
+ architectural: "architectural";
106
+ }>;
107
+ openStandard: z.ZodNullable<z.ZodString>;
108
+ whatYouLose: z.ZodString;
109
+ }, z.core.$strip>;
110
+ export type PortabilityInfo = z.infer<typeof PortabilityInfoSchema>;
111
+ export declare const ToolEntrySchema: z.ZodObject<{
112
+ id: z.ZodString;
113
+ name: z.ZodString;
114
+ description: z.ZodString;
115
+ url: z.ZodString;
116
+ pricingUrl: z.ZodString;
117
+ category: z.ZodEnum<{
118
+ hosting: "hosting";
119
+ database: "database";
120
+ auth: "auth";
121
+ email: "email";
122
+ payments: "payments";
123
+ monitoring: "monitoring";
124
+ "ai-api": "ai-api";
125
+ storage: "storage";
126
+ "ci-cd": "ci-cd";
127
+ search: "search";
128
+ analytics: "analytics";
129
+ "feature-flags": "feature-flags";
130
+ cms: "cms";
131
+ queues: "queues";
132
+ edge: "edge";
133
+ testing: "testing";
134
+ scheduling: "scheduling";
135
+ notifications: "notifications";
136
+ }>;
137
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
138
+ tiers: z.ZodArray<z.ZodObject<{
139
+ name: z.ZodString;
140
+ slug: z.ZodString;
141
+ pricingModel: z.ZodEnum<{
142
+ free: "free";
143
+ flat_rate: "flat_rate";
144
+ per_seat: "per_seat";
145
+ usage_based: "usage_based";
146
+ tiered: "tiered";
147
+ hybrid: "hybrid";
148
+ custom: "custom";
149
+ }>;
150
+ basePrice: z.ZodNullable<z.ZodNumber>;
151
+ billingPeriod: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
152
+ monthly: "monthly";
153
+ annual: "annual";
154
+ }>>>;
155
+ annualDiscount: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
156
+ seatPrice: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
157
+ usageMetrics: z.ZodDefault<z.ZodArray<z.ZodObject<{
158
+ name: z.ZodString;
159
+ unit: z.ZodString;
160
+ pricePerUnit: z.ZodNumber;
161
+ unitQuantity: z.ZodNumber;
162
+ includedQuantity: z.ZodDefault<z.ZodNumber>;
163
+ tieredPricing: z.ZodOptional<z.ZodArray<z.ZodObject<{
164
+ upTo: z.ZodNullable<z.ZodNumber>;
165
+ pricePerUnit: z.ZodNumber;
166
+ }, z.core.$strip>>>;
167
+ }, z.core.$strip>>>;
168
+ features: z.ZodDefault<z.ZodArray<z.ZodObject<{
169
+ name: z.ZodString;
170
+ included: z.ZodBoolean;
171
+ }, z.core.$strip>>>;
172
+ limits: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
173
+ }, z.core.$strip>>;
174
+ lastVerified: z.ZodString;
175
+ freshnessCategory: z.ZodEnum<{
176
+ stable: "stable";
177
+ volatile: "volatile";
178
+ }>;
179
+ currency: z.ZodDefault<z.ZodString>;
180
+ portability: z.ZodObject<{
181
+ switchingCost: z.ZodEnum<{
182
+ "drop-in": "drop-in";
183
+ moderate: "moderate";
184
+ significant: "significant";
185
+ architectural: "architectural";
186
+ }>;
187
+ openStandard: z.ZodNullable<z.ZodString>;
188
+ whatYouLose: z.ZodString;
189
+ }, z.core.$strip>;
190
+ }, z.core.$strip>;
191
+ export type ToolEntry = z.infer<typeof ToolEntrySchema>;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToolEntrySchema = exports.PortabilityInfoSchema = exports.PricingTierSchema = exports.FeatureSchema = exports.UsageMetricSchema = exports.SlugSchema = exports.FreshnessCategoryEnum = exports.SwitchingCostEnum = exports.PricingModelEnum = exports.CategoryEnum = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.CategoryEnum = zod_1.z.enum([
6
+ "hosting",
7
+ "database",
8
+ "auth",
9
+ "email",
10
+ "payments",
11
+ "monitoring",
12
+ "ai-api",
13
+ "storage",
14
+ "ci-cd",
15
+ "search",
16
+ "analytics",
17
+ "feature-flags",
18
+ "cms",
19
+ "queues",
20
+ "edge",
21
+ "testing",
22
+ "scheduling",
23
+ "notifications",
24
+ ]);
25
+ exports.PricingModelEnum = zod_1.z.enum([
26
+ "free",
27
+ "flat_rate",
28
+ "per_seat",
29
+ "usage_based",
30
+ "tiered",
31
+ "hybrid",
32
+ "custom",
33
+ ]);
34
+ exports.SwitchingCostEnum = zod_1.z.enum([
35
+ "drop-in",
36
+ "moderate",
37
+ "significant",
38
+ "architectural",
39
+ ]);
40
+ exports.FreshnessCategoryEnum = zod_1.z.enum(["stable", "volatile"]);
41
+ exports.SlugSchema = zod_1.z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "Must be a lowercase slug (letters, numbers, hyphens)");
42
+ exports.UsageMetricSchema = zod_1.z.object({
43
+ name: zod_1.z.string(),
44
+ unit: zod_1.z.string().describe("Unit label, e.g. 'GB', 'emails', 'CU-hours', 'requests'"),
45
+ pricePerUnit: zod_1.z.number(),
46
+ unitQuantity: zod_1.z.number().positive().describe("Denominator, e.g. 1000000 for 'per 1M tokens'"),
47
+ includedQuantity: zod_1.z.number().default(0),
48
+ tieredPricing: zod_1.z
49
+ .array(zod_1.z.object({
50
+ upTo: zod_1.z.number().nullable().describe("null means unlimited"),
51
+ pricePerUnit: zod_1.z.number(),
52
+ }))
53
+ .optional(),
54
+ });
55
+ exports.FeatureSchema = zod_1.z.object({
56
+ name: zod_1.z.string(),
57
+ included: zod_1.z.boolean(),
58
+ });
59
+ exports.PricingTierSchema = zod_1.z.object({
60
+ name: zod_1.z.string(),
61
+ slug: exports.SlugSchema,
62
+ pricingModel: exports.PricingModelEnum,
63
+ basePrice: zod_1.z.number().nullable().describe("Monthly base. 0 for free, null for contact-us"),
64
+ billingPeriod: zod_1.z.enum(["monthly", "annual"]).nullable().default(null),
65
+ annualDiscount: zod_1.z.number().nullable().default(null).describe("Percentage discount for annual billing"),
66
+ seatPrice: zod_1.z.number().nullable().default(null).describe("Per-seat cost for per_seat/hybrid models"),
67
+ usageMetrics: zod_1.z.array(exports.UsageMetricSchema).default([]),
68
+ features: zod_1.z.array(exports.FeatureSchema).default([]),
69
+ limits: zod_1.z.record(zod_1.z.string(), zod_1.z.number()).default({}),
70
+ });
71
+ exports.PortabilityInfoSchema = zod_1.z.object({
72
+ switchingCost: exports.SwitchingCostEnum,
73
+ openStandard: zod_1.z.string().nullable().describe("e.g. 'SMTP', 'S3-compatible', 'PostgreSQL wire protocol', null if proprietary"),
74
+ whatYouLose: zod_1.z.string().describe("What's not portable when switching away"),
75
+ });
76
+ exports.ToolEntrySchema = zod_1.z.object({
77
+ id: exports.SlugSchema,
78
+ name: zod_1.z.string(),
79
+ description: zod_1.z.string(),
80
+ url: zod_1.z.string().url().startsWith("https://"),
81
+ pricingUrl: zod_1.z.string().url().startsWith("https://"),
82
+ category: exports.CategoryEnum,
83
+ tags: zod_1.z.array(zod_1.z.string()).default([]),
84
+ tiers: zod_1.z.array(exports.PricingTierSchema).min(1),
85
+ lastVerified: zod_1.z.string().date().describe("ISO date YYYY-MM-DD"),
86
+ freshnessCategory: exports.FreshnessCategoryEnum,
87
+ currency: zod_1.z.string().default("USD"),
88
+ portability: exports.PortabilityInfoSchema,
89
+ });
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { Registry } from "./registry/registry.js";
3
+ export declare function createServer(registry: Registry): McpServer;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createServer = createServer;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const search_tools_js_1 = require("./tools/search-tools.js");
6
+ const get_pricing_js_1 = require("./tools/get-pricing.js");
7
+ const compare_tools_js_1 = require("./tools/compare-tools.js");
8
+ const estimate_cost_js_1 = require("./tools/estimate-cost.js");
9
+ const find_cheapest_js_1 = require("./tools/find-cheapest.js");
10
+ function respond(result) {
11
+ if (result && typeof result === "object" && "error" in result) {
12
+ return {
13
+ content: [{ type: "text", text: result.error }],
14
+ isError: true,
15
+ };
16
+ }
17
+ return {
18
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
19
+ };
20
+ }
21
+ function createServer(registry) {
22
+ const server = new mcp_js_1.McpServer({
23
+ name: "pricing.md",
24
+ version: "1.0.0",
25
+ });
26
+ server.tool("search_tools", "Search and filter developer tools by category, price range, tags, or text query. Returns a summary of each matching tool with free tier info, lowest paid price, and portability assessment.", search_tools_js_1.searchToolsSchema.shape, async (params) => respond((0, search_tools_js_1.handleSearchTools)(registry, params)));
27
+ server.tool("get_pricing", "Get full pricing details for a specific developer tool, including all tiers, usage metrics, features, limits, and portability info.", get_pricing_js_1.getPricingSchema.shape, async (params) => respond((0, get_pricing_js_1.handleGetPricing)(registry, params)));
28
+ server.tool("compare_tools", "Compare 2-5 developer tools side by side. Shows pricing tiers, usage metrics, limits, and portability. Optionally pass usage quantities and seats to see which tool is cheapest at your volume. Warns when comparing tools with different currencies.", compare_tools_js_1.compareToolsSchema.shape, async (params) => respond((0, compare_tools_js_1.handleCompareTools)(registry, params)));
29
+ server.tool("estimate_cost", "Estimate monthly cost for a tool given specific usage quantities and team size. Returns cost breakdown per tier including seat costs, identifies the cheapest option, and flags breakpoints where upgrading becomes cheaper.", estimate_cost_js_1.estimateCostSchema.shape, async (params) => respond((0, estimate_cost_js_1.handleEstimateCost)(registry, params)));
30
+ server.tool("find_cheapest", "Find the cheapest tool in a category for your specific usage. Compares all tools side by side, showing the best tier per tool ranked by total monthly cost. Includes portability info to weigh cost vs lock-in.", find_cheapest_js_1.findCheapestSchema.shape, async (params) => respond((0, find_cheapest_js_1.handleFindCheapest)(registry, params)));
31
+ return server;
32
+ }
@@ -0,0 +1,61 @@
1
+ import { z } from "zod";
2
+ import type { Registry } from "../registry/registry.js";
3
+ export declare const compareToolsSchema: z.ZodObject<{
4
+ toolIds: z.ZodArray<z.ZodString>;
5
+ tierFilter: z.ZodOptional<z.ZodString>;
6
+ usage: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
7
+ seats: z.ZodOptional<z.ZodNumber>;
8
+ }, z.core.$strip>;
9
+ export declare function handleCompareTools(registry: Registry, params: z.infer<typeof compareToolsSchema>): {
10
+ currencyWarning?: string | undefined;
11
+ freeTierSummary?: string | undefined;
12
+ tools: ({
13
+ id: string;
14
+ error: string;
15
+ } | {
16
+ warning?: string | undefined;
17
+ lastVerified: string;
18
+ costEstimate?: {
19
+ limitWarnings?: string[] | undefined;
20
+ cheapestTier: string;
21
+ cheapestTierSlug: string;
22
+ totalMonthly: number;
23
+ basePrice: number;
24
+ seatCost: number;
25
+ usageCosts: {
26
+ metric: string;
27
+ quantity: number;
28
+ cost: number;
29
+ unit: string;
30
+ }[];
31
+ exceedsLimits: boolean;
32
+ } | undefined;
33
+ id: string;
34
+ name: string;
35
+ category: "hosting" | "database" | "auth" | "email" | "payments" | "monitoring" | "ai-api" | "storage" | "ci-cd" | "search" | "analytics" | "feature-flags" | "cms" | "queues" | "edge" | "testing" | "scheduling" | "notifications";
36
+ currency: string;
37
+ portability: {
38
+ switchingCost: "drop-in" | "moderate" | "significant" | "architectural";
39
+ openStandard: string | null;
40
+ whatYouLose: string;
41
+ };
42
+ tiers: {
43
+ name: string;
44
+ slug: string;
45
+ pricingModel: "free" | "flat_rate" | "per_seat" | "usage_based" | "tiered" | "hybrid" | "custom";
46
+ basePrice: number | null;
47
+ seatPrice: number | null;
48
+ annualDiscount: number | null;
49
+ usageMetrics: {
50
+ name: string;
51
+ unit: string;
52
+ pricePerUnit: number;
53
+ unitQuantity: number;
54
+ includedQuantity: number;
55
+ }[];
56
+ keyFeatures: string[];
57
+ limits: Record<string, number>;
58
+ }[];
59
+ error?: undefined;
60
+ })[];
61
+ };
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compareToolsSchema = void 0;
4
+ exports.handleCompareTools = handleCompareTools;
5
+ const zod_1 = require("zod");
6
+ exports.compareToolsSchema = zod_1.z.object({
7
+ toolIds: zod_1.z.array(zod_1.z.string().max(100).regex(/^[a-z0-9][a-z0-9-]*$/)).min(2).max(5).refine((ids) => new Set(ids).size === ids.length, "Duplicate tool IDs not allowed").describe("Tool IDs to compare (2-5)"),
8
+ tierFilter: zod_1.z.string().max(100).regex(/^[a-z0-9][a-z0-9-]*$/).optional().describe("Only show tiers matching this slug (e.g., 'free', 'pro')"),
9
+ usage: zod_1.z.record(zod_1.z.string().max(100), zod_1.z.number().nonnegative().max(999_999_999_999)).optional().describe("Usage quantities to estimate cost for each tool, e.g. {emails: 75000}"),
10
+ seats: zod_1.z.number().int().min(1).max(10_000).optional().describe("Number of team seats for cost estimation (default 1)"),
11
+ });
12
+ function handleCompareTools(registry, params) {
13
+ const tools = registry.compare(params.toolIds);
14
+ const STALE_DAYS = 90;
15
+ const now = new Date();
16
+ // Detect mixed currencies
17
+ const currencies = new Set();
18
+ for (const tool of tools) {
19
+ if (tool)
20
+ currencies.add(tool.currency);
21
+ }
22
+ const hasMixedCurrencies = currencies.size > 1;
23
+ const comparison = params.toolIds.map((id, i) => {
24
+ const tool = tools[i];
25
+ if (!tool)
26
+ return { id, error: "Tool not found" };
27
+ const lastVerified = new Date(tool.lastVerified);
28
+ const daysSinceVerified = Math.floor((now.getTime() - lastVerified.getTime()) / (1000 * 60 * 60 * 24));
29
+ const isStale = daysSinceVerified > STALE_DAYS;
30
+ let tiers = tool.tiers;
31
+ if (params.tierFilter) {
32
+ tiers = tiers.filter((t) => t.slug === params.tierFilter);
33
+ }
34
+ // Build cost estimates if usage was provided
35
+ let costEstimate = undefined;
36
+ if (params.usage && Object.keys(params.usage).length > 0) {
37
+ const estimates = registry.estimateCost({
38
+ toolId: tool.id,
39
+ usage: params.usage,
40
+ seats: params.seats,
41
+ tierSlug: params.tierFilter,
42
+ });
43
+ const viable = estimates.filter((e) => !e.exceedsLimits);
44
+ const cheapest = viable.length > 0
45
+ ? viable.reduce((min, e) => e.totalMonthly < min.totalMonthly ? e : min)
46
+ : estimates.length > 0
47
+ ? estimates.reduce((min, e) => e.totalMonthly < min.totalMonthly ? e : min)
48
+ : null;
49
+ if (cheapest) {
50
+ costEstimate = {
51
+ cheapestTier: cheapest.tierName,
52
+ cheapestTierSlug: cheapest.tierSlug,
53
+ totalMonthly: cheapest.totalMonthly,
54
+ basePrice: cheapest.basePrice,
55
+ seatCost: cheapest.seatCost,
56
+ usageCosts: cheapest.usageCosts,
57
+ exceedsLimits: cheapest.exceedsLimits,
58
+ ...(cheapest.limitWarnings.length > 0 ? { limitWarnings: cheapest.limitWarnings } : {}),
59
+ };
60
+ }
61
+ }
62
+ return {
63
+ id: tool.id,
64
+ name: tool.name,
65
+ category: tool.category,
66
+ currency: tool.currency,
67
+ portability: tool.portability,
68
+ tiers: tiers.map((tier) => ({
69
+ name: tier.name,
70
+ slug: tier.slug,
71
+ pricingModel: tier.pricingModel,
72
+ basePrice: tier.basePrice,
73
+ seatPrice: tier.seatPrice,
74
+ annualDiscount: tier.annualDiscount,
75
+ usageMetrics: tier.usageMetrics.map((m) => ({
76
+ name: m.name,
77
+ unit: m.unit,
78
+ pricePerUnit: m.pricePerUnit,
79
+ unitQuantity: m.unitQuantity,
80
+ includedQuantity: m.includedQuantity,
81
+ })),
82
+ keyFeatures: tier.features.filter((f) => f.included).map((f) => f.name),
83
+ limits: tier.limits,
84
+ })),
85
+ ...(costEstimate ? { costEstimate } : {}),
86
+ lastVerified: tool.lastVerified,
87
+ ...(isStale ? { warning: `Pricing last verified ${daysSinceVerified} days ago — confirm at ${tool.pricingUrl}` } : {}),
88
+ };
89
+ });
90
+ // Generate a quick summary of free tier differences
91
+ const freeTierSummary = buildFreeTierSummary(tools.filter((t) => t !== null));
92
+ return {
93
+ tools: comparison,
94
+ ...(freeTierSummary ? { freeTierSummary } : {}),
95
+ ...(hasMixedCurrencies ? {
96
+ currencyWarning: `Tools use different currencies (${[...currencies].join(", ")}). Costs are NOT directly comparable without conversion.`,
97
+ } : {}),
98
+ };
99
+ }
100
+ function buildFreeTierSummary(tools) {
101
+ const freeInfo = tools
102
+ .map((tool) => {
103
+ const freeTier = tool.tiers.find((t) => t.pricingModel === "free" || t.basePrice === 0);
104
+ if (!freeTier)
105
+ return null;
106
+ const limitStr = Object.entries(freeTier.limits)
107
+ .map(([k, v]) => `${k}: ${v}`)
108
+ .join(", ");
109
+ return `${tool.name}: ${freeTier.name}${limitStr ? ` (${limitStr})` : ""}`;
110
+ })
111
+ .filter(Boolean);
112
+ if (freeInfo.length === 0)
113
+ return null;
114
+ return freeInfo.join(" | ");
115
+ }
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import type { Registry } from "../registry/registry.js";
3
+ export declare const estimateCostSchema: z.ZodObject<{
4
+ toolId: z.ZodString;
5
+ usage: z.ZodRecord<z.ZodString, z.ZodNumber>;
6
+ seats: z.ZodOptional<z.ZodNumber>;
7
+ tierSlug: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strip>;
9
+ export declare function handleEstimateCost(registry: Registry, params: z.infer<typeof estimateCostSchema>): {
10
+ error: string;
11
+ } | {
12
+ warning?: string | undefined;
13
+ breakpoints?: Breakpoint[] | undefined;
14
+ toolId: string;
15
+ toolName: string;
16
+ currency: string;
17
+ estimates: {
18
+ isCheapest: boolean;
19
+ toolId: string;
20
+ toolName: string;
21
+ tierName: string;
22
+ tierSlug: string;
23
+ currency: string;
24
+ basePrice: number;
25
+ seatCost: number;
26
+ usageCosts: {
27
+ metric: string;
28
+ quantity: number;
29
+ cost: number;
30
+ unit: string;
31
+ }[];
32
+ totalMonthly: number;
33
+ exceedsLimits: boolean;
34
+ limitWarnings: string[];
35
+ }[];
36
+ cheapestTier: string;
37
+ error?: undefined;
38
+ };
39
+ interface Breakpoint {
40
+ fromTier: string;
41
+ toTier: string;
42
+ note: string;
43
+ }
44
+ export {};
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.estimateCostSchema = void 0;
4
+ exports.handleEstimateCost = handleEstimateCost;
5
+ const zod_1 = require("zod");
6
+ exports.estimateCostSchema = zod_1.z.object({
7
+ toolId: zod_1.z.string().max(100).regex(/^[a-z0-9][a-z0-9-]*$/).describe("The tool ID to estimate cost for"),
8
+ usage: zod_1.z.record(zod_1.z.string().max(100), zod_1.z.number().nonnegative().max(999_999_999_999)).describe("Usage quantities by metric name, e.g. {emails: 50000, storage: 10}"),
9
+ seats: zod_1.z.number().int().min(1).max(10_000).optional().describe("Number of team seats (default 1). Affects per-seat pricing on tools like Vercel, Buildkite, etc."),
10
+ tierSlug: zod_1.z.string().max(100).regex(/^[a-z0-9][a-z0-9-]*$/).optional().describe("Estimate for a specific tier only"),
11
+ });
12
+ function handleEstimateCost(registry, params) {
13
+ const tool = registry.get(params.toolId);
14
+ if (!tool) {
15
+ return { error: "Tool not found. Use search_tools to find available tools." };
16
+ }
17
+ const estimates = registry.estimateCost(params);
18
+ if (estimates.length === 0) {
19
+ return { error: "No estimable tiers found. Enterprise/custom tiers cannot be estimated." };
20
+ }
21
+ const STALE_DAYS = 90;
22
+ const now = new Date();
23
+ const lastVerified = new Date(tool.lastVerified);
24
+ const daysSinceVerified = Math.floor((now.getTime() - lastVerified.getTime()) / (1000 * 60 * 60 * 24));
25
+ const isStale = daysSinceVerified > STALE_DAYS;
26
+ // Find the cheapest viable tier (exclude tiers that exceed limits)
27
+ const viableEstimates = estimates.filter((est) => !est.exceedsLimits);
28
+ const cheapest = viableEstimates.length > 0
29
+ ? viableEstimates.reduce((min, est) => est.totalMonthly < min.totalMonthly ? est : min)
30
+ : estimates.reduce((min, est) => est.totalMonthly < min.totalMonthly ? est : min);
31
+ // Find breakpoints — where does the next tier become cheaper?
32
+ const breakpoints = findBreakpoints(estimates);
33
+ return {
34
+ toolId: tool.id,
35
+ toolName: tool.name,
36
+ currency: tool.currency,
37
+ estimates: estimates.map((est) => ({
38
+ ...est,
39
+ isCheapest: est.tierSlug === cheapest.tierSlug,
40
+ })),
41
+ cheapestTier: cheapest.tierSlug,
42
+ ...(breakpoints.length > 0 ? { breakpoints } : {}),
43
+ ...(isStale ? { warning: `Pricing last verified ${daysSinceVerified} days ago — confirm at ${tool.pricingUrl}` } : {}),
44
+ };
45
+ }
46
+ function findBreakpoints(estimates) {
47
+ const breakpoints = [];
48
+ const sorted = [...estimates].sort((a, b) => a.basePrice - b.basePrice);
49
+ for (let i = 0; i < sorted.length - 1; i++) {
50
+ const current = sorted[i];
51
+ const next = sorted[i + 1];
52
+ if (current.totalMonthly > next.totalMonthly) {
53
+ breakpoints.push({
54
+ fromTier: current.tierSlug,
55
+ toTier: next.tierSlug,
56
+ note: `${next.tierName} ($${next.totalMonthly.toFixed(2)}/mo) is cheaper than ${current.tierName} ($${current.totalMonthly.toFixed(2)}/mo) at this usage level`,
57
+ });
58
+ }
59
+ }
60
+ return breakpoints;
61
+ }
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import type { Registry, CostEstimate } from "../registry/registry.js";
3
+ export declare const findCheapestSchema: z.ZodObject<{
4
+ category: z.ZodEnum<{
5
+ hosting: "hosting";
6
+ database: "database";
7
+ auth: "auth";
8
+ email: "email";
9
+ payments: "payments";
10
+ monitoring: "monitoring";
11
+ "ai-api": "ai-api";
12
+ storage: "storage";
13
+ "ci-cd": "ci-cd";
14
+ search: "search";
15
+ analytics: "analytics";
16
+ "feature-flags": "feature-flags";
17
+ cms: "cms";
18
+ queues: "queues";
19
+ edge: "edge";
20
+ testing: "testing";
21
+ scheduling: "scheduling";
22
+ notifications: "notifications";
23
+ }>;
24
+ usage: z.ZodRecord<z.ZodString, z.ZodNumber>;
25
+ seats: z.ZodOptional<z.ZodNumber>;
26
+ }, z.core.$strip>;
27
+ interface RankedTool {
28
+ toolId: string;
29
+ toolName: string;
30
+ tierName: string;
31
+ tierSlug: string;
32
+ currency: string;
33
+ totalMonthly: number;
34
+ seatCost: number;
35
+ basePrice: number;
36
+ usageCosts: CostEstimate["usageCosts"];
37
+ portability: {
38
+ switchingCost: string;
39
+ openStandard: string | null;
40
+ };
41
+ exceedsLimits: boolean;
42
+ limitWarnings: string[];
43
+ }
44
+ export declare function handleFindCheapest(registry: Registry, params: z.infer<typeof findCheapestSchema>): {
45
+ error: string;
46
+ } | {
47
+ warnings?: string[] | undefined;
48
+ results: RankedTool[];
49
+ seats?: number | undefined;
50
+ category: "hosting" | "database" | "auth" | "email" | "payments" | "monitoring" | "ai-api" | "storage" | "ci-cd" | "search" | "analytics" | "feature-flags" | "cms" | "queues" | "edge" | "testing" | "scheduling" | "notifications";
51
+ usage: Record<string, number>;
52
+ error?: undefined;
53
+ };
54
+ export {};