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.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/SKILL.md +593 -0
- package/data/tools/algolia.json +101 -0
- package/data/tools/amplitude.json +73 -0
- package/data/tools/auth0-b2b.json +175 -0
- package/data/tools/auth0-b2c.json +183 -0
- package/data/tools/buildkite.json +104 -0
- package/data/tools/checkly.json +112 -0
- package/data/tools/circleci.json +102 -0
- package/data/tools/clerk.json +109 -0
- package/data/tools/cloudflare.json +71 -0
- package/data/tools/contentful.json +71 -0
- package/data/tools/convex.json +74 -0
- package/data/tools/courier.json +80 -0
- package/data/tools/deno-deploy.json +145 -0
- package/data/tools/depot.json +66 -0
- package/data/tools/descope.json +130 -0
- package/data/tools/engagespot.json +84 -0
- package/data/tools/fly-io.json +53 -0
- package/data/tools/github-actions.json +64 -0
- package/data/tools/honeycomb.json +65 -0
- package/data/tools/inngest.json +80 -0
- package/data/tools/launchdarkly.json +81 -0
- package/data/tools/mergent.json +116 -0
- package/data/tools/mixpanel.json +70 -0
- package/data/tools/mux.json +164 -0
- package/data/tools/neon.json +173 -0
- package/data/tools/netlify.json +99 -0
- package/data/tools/new-relic.json +104 -0
- package/data/tools/novu.json +94 -0
- package/data/tools/orama.json +153 -0
- package/data/tools/paddle.json +46 -0
- package/data/tools/planetscale-postgres.json +108 -0
- package/data/tools/planetscale-vitess.json +82 -0
- package/data/tools/plausible.json +75 -0
- package/data/tools/posthog.json +101 -0
- package/data/tools/postmark.json +93 -0
- package/data/tools/railway.json +161 -0
- package/data/tools/render.json +94 -0
- package/data/tools/resend.json +135 -0
- package/data/tools/sanity.json +98 -0
- package/data/tools/sendgrid.json +78 -0
- package/data/tools/sentry.json +276 -0
- package/data/tools/statsig.json +70 -0
- package/data/tools/storyblok.json +121 -0
- package/data/tools/stripe.json +46 -0
- package/data/tools/supabase.json +100 -0
- package/data/tools/tigris.json +70 -0
- package/data/tools/trigger-dev.json +90 -0
- package/data/tools/turso.json +226 -0
- package/data/tools/unosend.json +88 -0
- package/data/tools/upstash.json +85 -0
- package/data/tools/vercel.json +146 -0
- package/data/tools/weaviate.json +93 -0
- package/data/tools/workos.json +127 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +23 -0
- package/dist/src/registry/registry.d.ts +52 -0
- package/dist/src/registry/registry.js +216 -0
- package/dist/src/schema/pricing.d.ts +191 -0
- package/dist/src/schema/pricing.js +89 -0
- package/dist/src/server.d.ts +3 -0
- package/dist/src/server.js +32 -0
- package/dist/src/tools/compare-tools.d.ts +61 -0
- package/dist/src/tools/compare-tools.js +115 -0
- package/dist/src/tools/estimate-cost.d.ts +44 -0
- package/dist/src/tools/estimate-cost.js +61 -0
- package/dist/src/tools/find-cheapest.d.ts +54 -0
- package/dist/src/tools/find-cheapest.js +93 -0
- package/dist/src/tools/get-pricing.d.ts +51 -0
- package/dist/src/tools/get-pricing.js +23 -0
- package/dist/src/tools/search-tools.d.ts +55 -0
- package/dist/src/tools/search-tools.js +52 -0
- package/package.json +40 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findCheapestSchema = void 0;
|
|
4
|
+
exports.handleFindCheapest = handleFindCheapest;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const pricing_js_1 = require("../schema/pricing.js");
|
|
7
|
+
exports.findCheapestSchema = zod_1.z.object({
|
|
8
|
+
category: pricing_js_1.CategoryEnum.describe("Tool category to search within"),
|
|
9
|
+
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: 75000}"),
|
|
10
|
+
seats: zod_1.z.number().int().min(1).max(10_000).optional().describe("Number of team seats (default 1)"),
|
|
11
|
+
});
|
|
12
|
+
function handleFindCheapest(registry, params) {
|
|
13
|
+
const tools = registry.search({ category: params.category });
|
|
14
|
+
if (tools.length === 0) {
|
|
15
|
+
return { error: `No tools found in category "${params.category}".` };
|
|
16
|
+
}
|
|
17
|
+
const ranked = [];
|
|
18
|
+
for (const tool of tools) {
|
|
19
|
+
const estimates = registry.estimateCost({
|
|
20
|
+
toolId: tool.id,
|
|
21
|
+
usage: params.usage,
|
|
22
|
+
seats: params.seats,
|
|
23
|
+
});
|
|
24
|
+
// Pick the cheapest viable tier per tool
|
|
25
|
+
const viable = estimates.filter((e) => !e.exceedsLimits);
|
|
26
|
+
const best = viable.length > 0
|
|
27
|
+
? viable.reduce((min, e) => e.totalMonthly < min.totalMonthly ? e : min)
|
|
28
|
+
: null;
|
|
29
|
+
if (best) {
|
|
30
|
+
ranked.push({
|
|
31
|
+
toolId: best.toolId,
|
|
32
|
+
toolName: best.toolName,
|
|
33
|
+
tierName: best.tierName,
|
|
34
|
+
tierSlug: best.tierSlug,
|
|
35
|
+
currency: best.currency,
|
|
36
|
+
totalMonthly: best.totalMonthly,
|
|
37
|
+
seatCost: best.seatCost,
|
|
38
|
+
basePrice: best.basePrice,
|
|
39
|
+
usageCosts: best.usageCosts,
|
|
40
|
+
portability: {
|
|
41
|
+
switchingCost: tool.portability.switchingCost,
|
|
42
|
+
openStandard: tool.portability.openStandard,
|
|
43
|
+
},
|
|
44
|
+
exceedsLimits: false,
|
|
45
|
+
limitWarnings: [],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else if (estimates.length > 0) {
|
|
49
|
+
// All tiers exceed limits — show the least-bad option
|
|
50
|
+
const leastBad = estimates.reduce((min, e) => e.totalMonthly < min.totalMonthly ? e : min);
|
|
51
|
+
ranked.push({
|
|
52
|
+
toolId: leastBad.toolId,
|
|
53
|
+
toolName: leastBad.toolName,
|
|
54
|
+
tierName: leastBad.tierName,
|
|
55
|
+
tierSlug: leastBad.tierSlug,
|
|
56
|
+
currency: leastBad.currency,
|
|
57
|
+
totalMonthly: leastBad.totalMonthly,
|
|
58
|
+
seatCost: leastBad.seatCost,
|
|
59
|
+
basePrice: leastBad.basePrice,
|
|
60
|
+
usageCosts: leastBad.usageCosts,
|
|
61
|
+
portability: {
|
|
62
|
+
switchingCost: tool.portability.switchingCost,
|
|
63
|
+
openStandard: tool.portability.openStandard,
|
|
64
|
+
},
|
|
65
|
+
exceedsLimits: true,
|
|
66
|
+
limitWarnings: leastBad.limitWarnings,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
ranked.sort((a, b) => a.totalMonthly - b.totalMonthly);
|
|
71
|
+
const STALE_DAYS = 90;
|
|
72
|
+
const now = new Date();
|
|
73
|
+
const staleTools = tools.filter((t) => {
|
|
74
|
+
const days = Math.floor((now.getTime() - new Date(t.lastVerified).getTime()) / (1000 * 60 * 60 * 24));
|
|
75
|
+
return days > STALE_DAYS;
|
|
76
|
+
});
|
|
77
|
+
const currencies = new Set(tools.map((t) => t.currency));
|
|
78
|
+
const hasMixedCurrencies = currencies.size > 1;
|
|
79
|
+
const warnings = [];
|
|
80
|
+
if (staleTools.length > 0) {
|
|
81
|
+
warnings.push(`${staleTools.length} tool(s) have pricing data older than 90 days: ${staleTools.map((t) => t.id).join(", ")}`);
|
|
82
|
+
}
|
|
83
|
+
if (hasMixedCurrencies) {
|
|
84
|
+
warnings.push(`Tools use different currencies (${[...currencies].join(", ")}). Cost ranking may be misleading without conversion.`);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
category: params.category,
|
|
88
|
+
usage: params.usage,
|
|
89
|
+
...(params.seats ? { seats: params.seats } : {}),
|
|
90
|
+
results: ranked,
|
|
91
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Registry } from "../registry/registry.js";
|
|
3
|
+
export declare const getPricingSchema: z.ZodObject<{
|
|
4
|
+
toolId: z.ZodString;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
export declare function handleGetPricing(registry: Registry, params: z.infer<typeof getPricingSchema>): {
|
|
7
|
+
error: string;
|
|
8
|
+
} | {
|
|
9
|
+
warning?: string | undefined;
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
url: string;
|
|
14
|
+
pricingUrl: string;
|
|
15
|
+
category: "hosting" | "database" | "auth" | "email" | "payments" | "monitoring" | "ai-api" | "storage" | "ci-cd" | "search" | "analytics" | "feature-flags" | "cms" | "queues" | "edge" | "testing" | "scheduling" | "notifications";
|
|
16
|
+
tags: string[];
|
|
17
|
+
tiers: {
|
|
18
|
+
name: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
pricingModel: "free" | "flat_rate" | "per_seat" | "usage_based" | "tiered" | "hybrid" | "custom";
|
|
21
|
+
basePrice: number | null;
|
|
22
|
+
billingPeriod: "monthly" | "annual" | null;
|
|
23
|
+
annualDiscount: number | null;
|
|
24
|
+
seatPrice: number | null;
|
|
25
|
+
usageMetrics: {
|
|
26
|
+
name: string;
|
|
27
|
+
unit: string;
|
|
28
|
+
pricePerUnit: number;
|
|
29
|
+
unitQuantity: number;
|
|
30
|
+
includedQuantity: number;
|
|
31
|
+
tieredPricing?: {
|
|
32
|
+
upTo: number | null;
|
|
33
|
+
pricePerUnit: number;
|
|
34
|
+
}[] | undefined;
|
|
35
|
+
}[];
|
|
36
|
+
features: {
|
|
37
|
+
name: string;
|
|
38
|
+
included: boolean;
|
|
39
|
+
}[];
|
|
40
|
+
limits: Record<string, number>;
|
|
41
|
+
}[];
|
|
42
|
+
lastVerified: string;
|
|
43
|
+
freshnessCategory: "stable" | "volatile";
|
|
44
|
+
currency: string;
|
|
45
|
+
portability: {
|
|
46
|
+
switchingCost: "drop-in" | "moderate" | "significant" | "architectural";
|
|
47
|
+
openStandard: string | null;
|
|
48
|
+
whatYouLose: string;
|
|
49
|
+
};
|
|
50
|
+
error?: undefined;
|
|
51
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPricingSchema = void 0;
|
|
4
|
+
exports.handleGetPricing = handleGetPricing;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
exports.getPricingSchema = zod_1.z.object({
|
|
7
|
+
toolId: zod_1.z.string().max(100).regex(/^[a-z0-9][a-z0-9-]*$/).describe("The tool ID (slug) to get full pricing for"),
|
|
8
|
+
});
|
|
9
|
+
function handleGetPricing(registry, params) {
|
|
10
|
+
const tool = registry.get(params.toolId);
|
|
11
|
+
if (!tool) {
|
|
12
|
+
return { error: "Tool not found. Use search_tools to find available tools." };
|
|
13
|
+
}
|
|
14
|
+
const STALE_DAYS = 90;
|
|
15
|
+
const now = new Date();
|
|
16
|
+
const lastVerified = new Date(tool.lastVerified);
|
|
17
|
+
const daysSinceVerified = Math.floor((now.getTime() - lastVerified.getTime()) / (1000 * 60 * 60 * 24));
|
|
18
|
+
const isStale = daysSinceVerified > STALE_DAYS;
|
|
19
|
+
return {
|
|
20
|
+
...tool,
|
|
21
|
+
...(isStale ? { warning: `Pricing last verified ${daysSinceVerified} days ago — confirm at ${tool.pricingUrl}` } : {}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Registry } from "../registry/registry.js";
|
|
3
|
+
export declare const searchToolsSchema: z.ZodObject<{
|
|
4
|
+
query: z.ZodOptional<z.ZodString>;
|
|
5
|
+
category: z.ZodOptional<z.ZodEnum<{
|
|
6
|
+
hosting: "hosting";
|
|
7
|
+
database: "database";
|
|
8
|
+
auth: "auth";
|
|
9
|
+
email: "email";
|
|
10
|
+
payments: "payments";
|
|
11
|
+
monitoring: "monitoring";
|
|
12
|
+
"ai-api": "ai-api";
|
|
13
|
+
storage: "storage";
|
|
14
|
+
"ci-cd": "ci-cd";
|
|
15
|
+
search: "search";
|
|
16
|
+
analytics: "analytics";
|
|
17
|
+
"feature-flags": "feature-flags";
|
|
18
|
+
cms: "cms";
|
|
19
|
+
queues: "queues";
|
|
20
|
+
edge: "edge";
|
|
21
|
+
testing: "testing";
|
|
22
|
+
scheduling: "scheduling";
|
|
23
|
+
notifications: "notifications";
|
|
24
|
+
}>>;
|
|
25
|
+
hasFreeOption: z.ZodOptional<z.ZodBoolean>;
|
|
26
|
+
maxBasePrice: z.ZodOptional<z.ZodNumber>;
|
|
27
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
28
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
}, z.core.$strip>;
|
|
30
|
+
export declare function handleSearchTools(registry: Registry, params: z.infer<typeof searchToolsSchema>): {
|
|
31
|
+
truncated?: boolean | undefined;
|
|
32
|
+
showing?: number | undefined;
|
|
33
|
+
tools: {
|
|
34
|
+
warning?: string | undefined;
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
category: "hosting" | "database" | "auth" | "email" | "payments" | "monitoring" | "ai-api" | "storage" | "ci-cd" | "search" | "analytics" | "feature-flags" | "cms" | "queues" | "edge" | "testing" | "scheduling" | "notifications";
|
|
39
|
+
url: string;
|
|
40
|
+
currency: string;
|
|
41
|
+
freeTier: {
|
|
42
|
+
name: string;
|
|
43
|
+
keyLimits: Record<string, number>;
|
|
44
|
+
} | null;
|
|
45
|
+
lowestPaidPrice: number | null;
|
|
46
|
+
lowestPaidTierName: string;
|
|
47
|
+
portability: {
|
|
48
|
+
switchingCost: "drop-in" | "moderate" | "significant" | "architectural";
|
|
49
|
+
openStandard: string | null;
|
|
50
|
+
whatYouLose: string;
|
|
51
|
+
};
|
|
52
|
+
lastVerified: string;
|
|
53
|
+
}[];
|
|
54
|
+
total: number;
|
|
55
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.searchToolsSchema = void 0;
|
|
4
|
+
exports.handleSearchTools = handleSearchTools;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const pricing_js_1 = require("../schema/pricing.js");
|
|
7
|
+
const MAX_RESULTS = 25;
|
|
8
|
+
exports.searchToolsSchema = zod_1.z.object({
|
|
9
|
+
query: zod_1.z.string().max(200).optional().describe("Text search across tool names, descriptions, tags, and categories"),
|
|
10
|
+
category: pricing_js_1.CategoryEnum.optional().describe("Filter by category"),
|
|
11
|
+
hasFreeOption: zod_1.z.boolean().optional().describe("Only show tools with a free tier"),
|
|
12
|
+
maxBasePrice: zod_1.z.number().nonnegative().max(999_999_999).optional().describe("Maximum base price for any tier"),
|
|
13
|
+
tags: zod_1.z.array(zod_1.z.string().max(50)).max(10).optional().describe("Filter by tags"),
|
|
14
|
+
limit: zod_1.z.number().int().min(1).max(50).optional().describe("Max results to return (default 25)"),
|
|
15
|
+
});
|
|
16
|
+
function handleSearchTools(registry, params) {
|
|
17
|
+
const limit = params.limit ?? MAX_RESULTS;
|
|
18
|
+
const allResults = registry.search(params);
|
|
19
|
+
const results = allResults.slice(0, limit);
|
|
20
|
+
const STALE_DAYS = 90;
|
|
21
|
+
const now = new Date();
|
|
22
|
+
const tools = results.map((tool) => {
|
|
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
|
+
const freeTier = tool.tiers.find((t) => t.pricingModel === "free" || t.basePrice === 0);
|
|
27
|
+
const lowestPaidTier = tool.tiers
|
|
28
|
+
.filter((t) => t.basePrice !== null && t.basePrice > 0)
|
|
29
|
+
.sort((a, b) => (a.basePrice ?? 0) - (b.basePrice ?? 0))[0];
|
|
30
|
+
return {
|
|
31
|
+
id: tool.id,
|
|
32
|
+
name: tool.name,
|
|
33
|
+
description: tool.description,
|
|
34
|
+
category: tool.category,
|
|
35
|
+
url: tool.url,
|
|
36
|
+
currency: tool.currency,
|
|
37
|
+
freeTier: freeTier
|
|
38
|
+
? { name: freeTier.name, keyLimits: freeTier.limits }
|
|
39
|
+
: null,
|
|
40
|
+
lowestPaidPrice: lowestPaidTier?.basePrice ?? null,
|
|
41
|
+
lowestPaidTierName: lowestPaidTier?.name ?? null,
|
|
42
|
+
portability: tool.portability,
|
|
43
|
+
lastVerified: tool.lastVerified,
|
|
44
|
+
...(isStale ? { warning: `Pricing last verified ${daysSinceVerified} days ago — confirm at ${tool.pricingUrl}` } : {}),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
tools,
|
|
49
|
+
total: allResults.length,
|
|
50
|
+
...(allResults.length > limit ? { truncated: true, showing: limit } : {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pricing.md",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Pricing registry for developer tools, exposed as an MCP server",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pricing.md": "dist/src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/src/",
|
|
11
|
+
"data/tools/",
|
|
12
|
+
"SKILL.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"discover": "tsx scripts/discover.ts",
|
|
16
|
+
"parse": "tsx scripts/parse.ts",
|
|
17
|
+
"validate": "tsx scripts/validate.ts",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"start": "node dist/src/index.js",
|
|
20
|
+
"check-freshness": "tsx scripts/check-freshness.ts",
|
|
21
|
+
"test": "tsx scripts/test-server.ts",
|
|
22
|
+
"sanity-check": "tsx scripts/sanity-check.ts",
|
|
23
|
+
"pre-push": "tsx scripts/pre-push.ts"
|
|
24
|
+
},
|
|
25
|
+
"keywords": ["mcp", "pricing", "developer-tools", "claude-code"],
|
|
26
|
+
"author": "Roger Byrne",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"type": "commonjs",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
31
|
+
"zod": "^4.3.6"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
35
|
+
"@types/node": "^25.5.0",
|
|
36
|
+
"better-sqlite3": "^12.8.0",
|
|
37
|
+
"tsx": "^4.21.0",
|
|
38
|
+
"typescript": "^6.0.2"
|
|
39
|
+
}
|
|
40
|
+
}
|