atom-mcp-server 1.1.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 (73) hide show
  1. package/Dockerfile +18 -0
  2. package/README.md +184 -0
  3. package/claude_desktop_config.json +8 -0
  4. package/dist/auth.d.ts +38 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +75 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +92 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/server.d.ts +3 -0
  13. package/dist/server.d.ts.map +1 -0
  14. package/dist/server.js +234 -0
  15. package/dist/server.js.map +1 -0
  16. package/dist/supabase.d.ts +18 -0
  17. package/dist/supabase.d.ts.map +1 -0
  18. package/dist/supabase.js +86 -0
  19. package/dist/supabase.js.map +1 -0
  20. package/dist/tools/compare-prices.d.ts +16 -0
  21. package/dist/tools/compare-prices.d.ts.map +1 -0
  22. package/dist/tools/compare-prices.js +152 -0
  23. package/dist/tools/compare-prices.js.map +1 -0
  24. package/dist/tools/get-index-benchmarks.d.ts +14 -0
  25. package/dist/tools/get-index-benchmarks.d.ts.map +1 -0
  26. package/dist/tools/get-index-benchmarks.js +99 -0
  27. package/dist/tools/get-index-benchmarks.js.map +1 -0
  28. package/dist/tools/get-kpis.d.ts +10 -0
  29. package/dist/tools/get-kpis.d.ts.map +1 -0
  30. package/dist/tools/get-kpis.js +45 -0
  31. package/dist/tools/get-kpis.js.map +1 -0
  32. package/dist/tools/get-market-stats.d.ts +12 -0
  33. package/dist/tools/get-market-stats.d.ts.map +1 -0
  34. package/dist/tools/get-market-stats.js +95 -0
  35. package/dist/tools/get-market-stats.js.map +1 -0
  36. package/dist/tools/get-model-detail.d.ts +12 -0
  37. package/dist/tools/get-model-detail.d.ts.map +1 -0
  38. package/dist/tools/get-model-detail.js +96 -0
  39. package/dist/tools/get-model-detail.js.map +1 -0
  40. package/dist/tools/get-vendor-catalog.d.ts +15 -0
  41. package/dist/tools/get-vendor-catalog.d.ts.map +1 -0
  42. package/dist/tools/get-vendor-catalog.js +102 -0
  43. package/dist/tools/get-vendor-catalog.js.map +1 -0
  44. package/dist/tools/list-vendors.d.ts +13 -0
  45. package/dist/tools/list-vendors.d.ts.map +1 -0
  46. package/dist/tools/list-vendors.js +49 -0
  47. package/dist/tools/list-vendors.js.map +1 -0
  48. package/dist/tools/search-models.d.ts +22 -0
  49. package/dist/tools/search-models.d.ts.map +1 -0
  50. package/dist/tools/search-models.js +128 -0
  51. package/dist/tools/search-models.js.map +1 -0
  52. package/dist/types.d.ts +119 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +5 -0
  55. package/dist/types.js.map +1 -0
  56. package/env.example +17 -0
  57. package/package.json +52 -0
  58. package/railway.json +13 -0
  59. package/smithery.yaml +36 -0
  60. package/src/auth.ts +101 -0
  61. package/src/index.ts +94 -0
  62. package/src/server.ts +278 -0
  63. package/src/supabase.ts +125 -0
  64. package/src/tools/compare-prices.ts +175 -0
  65. package/src/tools/get-index-benchmarks.ts +122 -0
  66. package/src/tools/get-kpis.ts +62 -0
  67. package/src/tools/get-market-stats.ts +112 -0
  68. package/src/tools/get-model-detail.ts +119 -0
  69. package/src/tools/get-vendor-catalog.ts +121 -0
  70. package/src/tools/list-vendors.ts +60 -0
  71. package/src/tools/search-models.ts +146 -0
  72. package/src/types.ts +145 -0
  73. package/tsconfig.json +19 -0
@@ -0,0 +1,128 @@
1
+ // ============================================================
2
+ // Tool: search_models
3
+ // Multi-filter search across the SKU index.
4
+ // ============================================================
5
+ import { z } from "zod";
6
+ import { queryTable } from "../supabase.js";
7
+ import { gateResults, freeTierNote } from "../auth.js";
8
+ export const searchModelsSchema = {
9
+ modality: z
10
+ .string()
11
+ .optional()
12
+ .describe("Filter by modality: Text, Image, Audio, Video, Voice, Multimodal, Embedding"),
13
+ vendor: z
14
+ .string()
15
+ .optional()
16
+ .describe("Filter by vendor name, e.g. 'OpenAI', 'Anthropic'"),
17
+ creator: z
18
+ .string()
19
+ .optional()
20
+ .describe("Filter by model creator/developer"),
21
+ model_family: z
22
+ .string()
23
+ .optional()
24
+ .describe("Filter by model family, e.g. 'GPT-4o', 'Claude 3.5'"),
25
+ open_source: z
26
+ .string()
27
+ .optional()
28
+ .describe("Filter by open-source status: 'true' or 'false'"),
29
+ direction: z
30
+ .enum(["Input", "Output", "Cached Input"])
31
+ .optional()
32
+ .describe("Filter by pricing direction"),
33
+ max_price: z
34
+ .coerce.number()
35
+ .optional()
36
+ .describe("Maximum normalized price (USD per unit)"),
37
+ min_context_window: z
38
+ .coerce.number()
39
+ .int()
40
+ .optional()
41
+ .describe("Minimum context window in tokens"),
42
+ min_parameter_count: z
43
+ .string()
44
+ .optional()
45
+ .describe("Minimum parameter count, e.g. '7B', '70B'"),
46
+ limit: z
47
+ .coerce.number()
48
+ .int()
49
+ .min(1)
50
+ .max(100)
51
+ .default(20)
52
+ .describe("Maximum results to return (default 20)"),
53
+ offset: z
54
+ .coerce.number()
55
+ .int()
56
+ .min(0)
57
+ .default(0)
58
+ .describe("Offset for pagination"),
59
+ };
60
+ export async function handleSearchModels(params, tier) {
61
+ // Build SKU-level filters
62
+ const skuFilters = [];
63
+ if (params.modality)
64
+ skuFilters.push(`modality=ilike.*${params.modality}*`);
65
+ if (params.vendor)
66
+ skuFilters.push(`vendor_name=ilike.*${params.vendor}*`);
67
+ if (params.direction)
68
+ skuFilters.push(`direction=eq.${params.direction}`);
69
+ if (params.max_price !== undefined)
70
+ skuFilters.push(`normalized_price=lte.${params.max_price}`);
71
+ skuFilters.push("normalized_price=gt.0");
72
+ // Query SKU index
73
+ let skus = await queryTable("sku_index", skuFilters, {
74
+ select: "sku_id,model_id,vendor_id,vendor_name,model_name,modality,modality_subtype,direction,normalized_price,normalized_price_unit,billing_method",
75
+ order: "normalized_price.asc",
76
+ limit: params.limit + 50, // extra buffer for model-level filtering
77
+ offset: params.offset,
78
+ });
79
+ // Apply model-level filters if needed
80
+ if (params.creator || params.model_family || params.open_source || params.min_context_window) {
81
+ const modelFilters = [];
82
+ if (params.creator)
83
+ modelFilters.push(`creator=ilike.*${params.creator}*`);
84
+ if (params.model_family)
85
+ modelFilters.push(`model_family=ilike.*${params.model_family}*`);
86
+ if (params.open_source !== undefined) {
87
+ const boolVal = params.open_source === "true";
88
+ modelFilters.push(`open_source=is.${boolVal}`);
89
+ }
90
+ if (params.min_context_window)
91
+ modelFilters.push(`context_window=gte.${params.min_context_window}`);
92
+ const models = await queryTable("model_registry", modelFilters, {
93
+ select: "model_id",
94
+ });
95
+ const modelIds = new Set(models.map((m) => m.model_id));
96
+ skus = skus.filter((s) => modelIds.has(s.model_id));
97
+ }
98
+ // Trim to requested limit
99
+ const trimmed = skus.slice(0, params.limit);
100
+ // Gate results based on tier
101
+ const gated = gateResults(trimmed, tier);
102
+ const content = [
103
+ {
104
+ type: "text",
105
+ text: JSON.stringify({
106
+ tool: "search_models",
107
+ tier,
108
+ filters: {
109
+ modality: params.modality,
110
+ vendor: params.vendor,
111
+ creator: params.creator,
112
+ model_family: params.model_family,
113
+ open_source: params.open_source,
114
+ direction: params.direction,
115
+ max_price: params.max_price,
116
+ },
117
+ total_results: skus.length,
118
+ showing: trimmed.length,
119
+ results: gated,
120
+ }, null, 2),
121
+ },
122
+ ];
123
+ if (tier === "free") {
124
+ content.push(freeTierNote("Full model search results with exact pricing"));
125
+ }
126
+ return { content };
127
+ }
128
+ //# sourceMappingURL=search-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-models.js","sourceRoot":"","sources":["../../src/tools/search-models.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,sBAAsB;AACtB,4CAA4C;AAC5C,+DAA+D;AAE/D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAwB,YAAY,EAAE,MAAM,YAAY,CAAC;AAG7E,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,6EAA6E,CAAC;IAC1F,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,mDAAmD,CAAC;IAChE,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,mCAAmC,CAAC;IAChD,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,qDAAqD,CAAC;IAClE,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,iDAAiD,CAAC;IAC9D,SAAS,EAAE,CAAC;SACT,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;SACzC,QAAQ,EAAE;SACV,QAAQ,CAAC,6BAA6B,CAAC;IAC1C,SAAS,EAAE,CAAC;SACT,MAAM,CAAC,MAAM,EAAE;SACf,QAAQ,EAAE;SACV,QAAQ,CAAC,yCAAyC,CAAC;IACtD,kBAAkB,EAAE,CAAC;SAClB,MAAM,CAAC,MAAM,EAAE;SACf,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,CAAC,kCAAkC,CAAC;IAC/C,mBAAmB,EAAE,CAAC;SACnB,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,2CAA2C,CAAC;IACxD,KAAK,EAAE,CAAC;SACL,MAAM,CAAC,MAAM,EAAE;SACf,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,OAAO,CAAC,EAAE,CAAC;SACX,QAAQ,CAAC,wCAAwC,CAAC;IACrD,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,MAAM,EAAE;SACf,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CAAC,uBAAuB,CAAC;CACrC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAuD,EACvD,IAAU;IAEV,0BAA0B;IAC1B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,MAAM,CAAC,QAAQ;QAAE,UAAU,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC5E,IAAI,MAAM,CAAC,MAAM;QAAE,UAAU,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3E,IAAI,MAAM,CAAC,SAAS;QAAE,UAAU,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC1E,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS;QAChC,UAAU,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9D,UAAU,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAEzC,kBAAkB;IAClB,IAAI,IAAI,GAAG,MAAM,UAAU,CAAW,WAAW,EAAE,UAAU,EAAE;QAC7D,MAAM,EACJ,4IAA4I;QAC9I,KAAK,EAAE,sBAAsB;QAC7B,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,yCAAyC;QACnE,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;IAEH,sCAAsC;IACtC,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC7F,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,IAAI,MAAM,CAAC,OAAO;YAAE,YAAY,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;QAC3E,IAAI,MAAM,CAAC,YAAY;YAAE,YAAY,CAAC,IAAI,CAAC,uBAAuB,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;QAC1F,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,KAAK,MAAM,CAAC;YAC9C,YAAY,CAAC,IAAI,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,MAAM,CAAC,kBAAkB;YAC3B,YAAY,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;QAEvE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAgB,gBAAgB,EAAE,YAAY,EAAE;YAC7E,MAAM,EAAE,UAAU;SACnB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACxD,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,0BAA0B;IAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAE5C,6BAA6B;IAC7B,MAAM,KAAK,GAAG,WAAW,CACvB,OAA+C,EAC/C,IAAI,CACL,CAAC;IAEF,MAAM,OAAO,GAAqC;QAChD;YACE,IAAI,EAAE,MAAe;YACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;gBACE,IAAI,EAAE,eAAe;gBACrB,IAAI;gBACJ,OAAO,EAAE;oBACP,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,YAAY,EAAE,MAAM,CAAC,YAAY;oBACjC,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;iBAC5B;gBACD,aAAa,EAAE,IAAI,CAAC,MAAM;gBAC1B,OAAO,EAAE,OAAO,CAAC,MAAM;gBACvB,OAAO,EAAE,KAAK;aACf,EACD,IAAI,EACJ,CAAC,CACF;SACF;KACF,CAAC;IAEF,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,8CAA8C,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
@@ -0,0 +1,119 @@
1
+ /** Access tier for API consumers */
2
+ export type Tier = "free" | "paid";
3
+ /** Supabase query response wrapper */
4
+ export interface SupabaseResponse<T> {
5
+ data: T[] | null;
6
+ error: string | null;
7
+ count?: number;
8
+ }
9
+ export interface VendorRegistry {
10
+ vendor_id: string;
11
+ vendor_name: string;
12
+ country: string;
13
+ region: string;
14
+ vendor_url: string | null;
15
+ pricing_page_url: string | null;
16
+ model_specifications_url: string | null;
17
+ }
18
+ export interface ModelRegistry {
19
+ model_id: string;
20
+ model_name: string;
21
+ creator: string;
22
+ model_family: string | null;
23
+ modality_input: string[] | null;
24
+ modality_output: string[] | null;
25
+ open_source: boolean | null;
26
+ parameter_count: string | null;
27
+ context_window: number | null;
28
+ max_output_tokens: number | null;
29
+ tool_calling: boolean | null;
30
+ json_mode: boolean | null;
31
+ streaming: boolean | null;
32
+ training_cutoff: string | null;
33
+ source_url: string | null;
34
+ null_reasons: Record<string, string> | null;
35
+ last_verified: string | null;
36
+ tier: string | null;
37
+ }
38
+ export interface SkuIndex {
39
+ sku_id: string;
40
+ sku_plan_name: string | null;
41
+ model_id: string;
42
+ vendor_id: string;
43
+ vendor_name: string;
44
+ model_name: string;
45
+ modality: string;
46
+ modality_subtype: string;
47
+ direction: string;
48
+ normalized_price: number | null;
49
+ normalized_price_unit: string | null;
50
+ price_normalization_method: string | null;
51
+ original_price: string | null;
52
+ billing_method: string | null;
53
+ delivery_method: string | null;
54
+ pricing_notes: string | null;
55
+ verification_date: string | null;
56
+ run_id: string | null;
57
+ aipi_eligible: boolean | null;
58
+ aipi_indexes: string | null;
59
+ exclusion_reason: string | null;
60
+ }
61
+ export interface PriceIndex {
62
+ id: number;
63
+ sku_id: string;
64
+ vendor_id: string;
65
+ model_id: string;
66
+ vendor_name: string;
67
+ sku_plan_name: string | null;
68
+ model_name: string;
69
+ modality: string;
70
+ modality_subtype: string;
71
+ direction: string;
72
+ original_price: string | null;
73
+ normalized_price: number | null;
74
+ normalized_price_unit: string | null;
75
+ price_normalization_method: string | null;
76
+ verification_date: string | null;
77
+ billing_method: string | null;
78
+ delivery_method: string | null;
79
+ pricing_notes: string | null;
80
+ run_id: string;
81
+ }
82
+ export interface IndexValues {
83
+ id: number;
84
+ index_code: string;
85
+ index_category: string;
86
+ index_description: string;
87
+ unit: string;
88
+ date: string;
89
+ input_price: number | null;
90
+ cached_price: number | null;
91
+ output_price: number | null;
92
+ sku_count: number;
93
+ created_at: string;
94
+ }
95
+ /** v_summary_stats returns rows of {metric, value} */
96
+ export interface SummaryStatRow {
97
+ metric: string;
98
+ value: string;
99
+ }
100
+ /** v_pricing_intel KPI rows */
101
+ export interface PricingIntel {
102
+ kpi_name: string;
103
+ kpi_value: number;
104
+ kpi_unit: string;
105
+ kpi_description: string;
106
+ }
107
+ export interface RedactedModel {
108
+ model_id: string;
109
+ modality: string;
110
+ direction: string;
111
+ vendor_name: "[UPGRADE TO ATOM MCP Pro — $49/mo]";
112
+ model_name: "[UPGRADE TO ATOM MCP Pro — $49/mo]";
113
+ normalized_price: "[UPGRADE TO ATOM MCP Pro — $49/mo]";
114
+ }
115
+ export interface ToolContext {
116
+ tier: Tier;
117
+ apiKey?: string;
118
+ }
119
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,oCAAoC;AACpC,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnC,sCAAsC;AACtC,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC5C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,+BAA+B;AAC/B,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAMD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,EAAE,oCAAoC,CAAC;IAClD,UAAU,EAAE,oCAAoC,CAAC;IACjD,gBAAgB,EAAE,oCAAoC,CAAC;CACxD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ // ============================================================
2
+ // ATOM MCP Server — Type Definitions
3
+ // ============================================================
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,qCAAqC;AACrC,+DAA+D"}
package/env.example ADDED
@@ -0,0 +1,17 @@
1
+ # ============================================================
2
+ # ATOM MCP Server — Environment Variables
3
+ # ============================================================
4
+
5
+ # Supabase connection (required)
6
+ SUPABASE_URL=https://jonncmzxvxzwyaznokba.supabase.co
7
+ SUPABASE_ANON_KEY=your_supabase_anon_key_here
8
+
9
+ # Valid API keys for paid tier (comma-separated)
10
+ # Generate these however you like — UUID, random string, etc.
11
+ ATOM_API_KEYS=key1,key2,key3
12
+
13
+ # Transport: "stdio" (default) or "http"
14
+ TRANSPORT=stdio
15
+
16
+ # Port for HTTP transport (default 3000)
17
+ PORT=3000
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "atom-mcp-server",
3
+ "version": "1.1.0",
4
+ "description": "ATOM Inference Price Index — AI pricing intelligence for agents and developers. 1,600+ SKUs, 40+ vendors, 25 AIPI indexes, 8 tools.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "atom-mcp-server": "./dist/index.js"
9
+ },
10
+ "homepage": "https://a7om.com/mcp",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/A7OM-AI/atom-mcp-server"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "start": "node dist/index.js",
18
+ "dev": "tsc --watch",
19
+ "start:http": "TRANSPORT=http node dist/index.js",
20
+ "start:stdio": "TRANSPORT=stdio node dist/index.js",
21
+ "inspector": "npx @modelcontextprotocol/inspector dist/index.js"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "ai",
26
+ "pricing",
27
+ "inference",
28
+ "llm",
29
+ "model-context-protocol",
30
+ "atom",
31
+ "aipi",
32
+ "ai-pricing",
33
+ "inference-pricing",
34
+ "finops",
35
+ "cost-optimization"
36
+ ],
37
+ "author": "ATOM (A7OM) — Member One EOOD",
38
+ "license": "MIT",
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.27.0",
41
+ "express": "^5.0.0",
42
+ "zod": "^3.24.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/express": "^5.0.0",
46
+ "@types/node": "^22.0.0",
47
+ "typescript": "^5.7.0"
48
+ },
49
+ "engines": {
50
+ "node": ">=18.0.0"
51
+ }
52
+ }
package/railway.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://railway.app/railway.schema.json",
3
+ "build": {
4
+ "builder": "DOCKERFILE",
5
+ "dockerfilePath": "Dockerfile"
6
+ },
7
+ "deploy": {
8
+ "startCommand": "node dist/index.js",
9
+ "healthcheckPath": "/health",
10
+ "restartPolicyType": "ON_FAILURE",
11
+ "restartPolicyMaxRetries": 10
12
+ }
13
+ }
package/smithery.yaml ADDED
@@ -0,0 +1,36 @@
1
+ # Smithery.ai configuration
2
+ # https://smithery.ai/docs/build/project-config/smithery-yaml
3
+
4
+ runtime: typescript
5
+
6
+ startCommand:
7
+ type: stdio
8
+ configSchema:
9
+ type: object
10
+ required:
11
+ - supabaseUrl
12
+ - supabaseAnonKey
13
+ properties:
14
+ supabaseUrl:
15
+ type: string
16
+ description: "Supabase project URL for ATOM pricing database"
17
+ supabaseAnonKey:
18
+ type: string
19
+ description: "Supabase anonymous/public API key"
20
+ atomApiKeys:
21
+ type: string
22
+ description: "Comma-separated API keys for paid tier access (optional — omit for free tier)"
23
+ commandFunction: |-
24
+ (config) => ({
25
+ command: 'node',
26
+ args: ['./dist/index.js'],
27
+ env: {
28
+ SUPABASE_URL: config.supabaseUrl,
29
+ SUPABASE_ANON_KEY: config.supabaseAnonKey,
30
+ ...(config.atomApiKeys ? { ATOM_API_KEYS: config.atomApiKeys } : {}),
31
+ TRANSPORT: 'stdio'
32
+ }
33
+ })
34
+ exampleConfig:
35
+ supabaseUrl: "https://your-project.supabase.co"
36
+ supabaseAnonKey: "your-anon-key"
package/src/auth.ts ADDED
@@ -0,0 +1,101 @@
1
+ // ============================================================
2
+ // ATOM MCP Server — Authentication & Tier Gating
3
+ // ============================================================
4
+
5
+ import type { Tier } from "./types.js";
6
+
7
+ // Valid API keys are loaded from environment.
8
+ // In production, these would be validated against a database.
9
+ const VALID_API_KEYS = new Set(
10
+ (process.env.ATOM_API_KEYS || "").split(",").filter(Boolean)
11
+ );
12
+
13
+ /**
14
+ * Determine the access tier from an API key.
15
+ * No key or invalid key = free tier.
16
+ */
17
+ export function resolveTier(apiKey?: string): Tier {
18
+ if (!apiKey) return "free";
19
+ return VALID_API_KEYS.has(apiKey.trim()) ? "paid" : "free";
20
+ }
21
+
22
+ /**
23
+ * Redact sensitive pricing fields for free-tier users.
24
+ * Shows structure but hides vendor, model, and price.
25
+ */
26
+ export function redactForFreeTier(
27
+ rows: Record<string, unknown>[],
28
+ fieldsToRedact: string[] = ["vendor_name", "model_name", "normalized_price", "sku_id", "model_id", "vendor_id", "sku_plan_name"]
29
+ ): Record<string, unknown>[] {
30
+ const REDACTED = "[UPGRADE TO ATOM MCP Pro — $49/mo]";
31
+ return rows.map((row) => {
32
+ const redacted = { ...row };
33
+ for (const field of fieldsToRedact) {
34
+ if (field in redacted) {
35
+ redacted[field] = REDACTED;
36
+ }
37
+ }
38
+ return redacted;
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Build a free-tier summary: count + price range (no individual records).
44
+ */
45
+ export function buildFreeTierSummary(rows: Record<string, unknown>[]): {
46
+ total_results: number;
47
+ price_range: { min: number | null; max: number | null };
48
+ modalities: string[];
49
+ directions: string[];
50
+ upgrade_message: string;
51
+ } {
52
+ const prices = rows
53
+ .map((r) => r.normalized_price as number)
54
+ .filter((p): p is number => p !== null && p > 0);
55
+
56
+ const modalities = [...new Set(rows.map((r) => r.modality as string).filter(Boolean))];
57
+ const directions = [...new Set(rows.map((r) => r.direction as string).filter(Boolean))];
58
+
59
+ return {
60
+ total_results: rows.length,
61
+ price_range: {
62
+ min: prices.length > 0 ? Math.min(...prices) : null,
63
+ max: prices.length > 0 ? Math.max(...prices) : null,
64
+ },
65
+ modalities,
66
+ directions,
67
+ upgrade_message:
68
+ "Full vendor, model, and pricing details require an ATOM MCP Pro ($49/mo). Visit https://a7om.com/mcp",
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Returns an additional content block for free-tier responses.
74
+ * This plain-text note ensures the AI assistant surfaces the upgrade path.
75
+ */
76
+ export function freeTierNote(toolContext: string): {
77
+ type: "text";
78
+ text: string;
79
+ } {
80
+ return {
81
+ type: "text" as const,
82
+ text: `Note: The data above is summary-level only (counts, ranges, and redacted samples). ${toolContext} — including exact vendor names, model names, and per-SKU pricing — is available exclusively through ATOM MCP Pro ($49/mo). Learn more and subscribe at https://a7om.com/mcp`,
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Gate a full result set based on tier.
88
+ * Paid tier gets everything. Free tier gets redacted + summary.
89
+ */
90
+ export function gateResults(
91
+ rows: Record<string, unknown>[],
92
+ tier: Tier
93
+ ): unknown {
94
+ if (tier === "paid") {
95
+ return rows;
96
+ }
97
+ return {
98
+ summary: buildFreeTierSummary(rows),
99
+ sample: redactForFreeTier(rows.slice(0, 5)),
100
+ };
101
+ }
package/src/index.ts ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ // ============================================================
3
+ // ATOM MCP Server — Entry Point
4
+ // ============================================================
5
+ // Dual transport: stdio (local) and Streamable HTTP (remote).
6
+ // Set TRANSPORT=http for HTTP mode, default is stdio.
7
+ // ============================================================
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
10
+ import express from "express";
11
+ import { createServer } from "./server.js";
12
+ // ----------------------------------------------------------
13
+ // stdio transport (for Cursor, Claude Desktop, etc.)
14
+ // ----------------------------------------------------------
15
+ async function runStdio(): Promise<void> {
16
+ const server = createServer();
17
+ const transport = new StdioServerTransport();
18
+ await server.connect(transport);
19
+ console.error("ATOM MCP Server running via stdio");
20
+ }
21
+ // ----------------------------------------------------------
22
+ // Streamable HTTP transport (for hosted / remote access)
23
+ // ----------------------------------------------------------
24
+ async function runHTTP(): Promise<void> {
25
+ const app = express();
26
+ app.use(express.json());
27
+
28
+ // CORS — allow browser-based MCP clients and test tools
29
+ app.use((req, res, next) => {
30
+ res.header('Access-Control-Allow-Origin', '*');
31
+ res.header('Access-Control-Allow-Headers', 'Content-Type');
32
+ res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
33
+ if (req.method === 'OPTIONS') { res.sendStatus(200); return; }
34
+ next();
35
+ });
36
+
37
+ // Health check
38
+ app.get("/health", (_req, res) => {
39
+ res.json({
40
+ status: "ok",
41
+ server: "atom-mcp-server",
42
+ version: "1.0.0",
43
+ });
44
+ });
45
+
46
+ // MCP endpoint — stateless mode (new transport per request)
47
+ app.post("/mcp", async (req, res) => {
48
+ const server = createServer();
49
+ const transport = new StreamableHTTPServerTransport({
50
+ sessionIdGenerator: undefined, // stateless
51
+ enableJsonResponse: true,
52
+ });
53
+ res.on("close", () => {
54
+ transport.close();
55
+ });
56
+ await server.connect(transport);
57
+ await transport.handleRequest(req, res, req.body);
58
+ });
59
+
60
+ // Handle GET and DELETE for MCP protocol completeness
61
+ app.get("/mcp", (_req, res) => {
62
+ res.status(405).json({
63
+ error: "Method Not Allowed. Use POST for MCP requests.",
64
+ });
65
+ });
66
+
67
+ app.delete("/mcp", (_req, res) => {
68
+ res.status(405).json({
69
+ error: "Method Not Allowed. Stateless server — no sessions to delete.",
70
+ });
71
+ });
72
+
73
+ const port = parseInt(process.env.PORT || "3000", 10);
74
+ app.listen(port, () => {
75
+ console.error(`ATOM MCP Server running on http://localhost:${port}/mcp`);
76
+ console.error(`Health check: http://localhost:${port}/health`);
77
+ });
78
+ }
79
+
80
+ // ----------------------------------------------------------
81
+ // Transport selection
82
+ // ----------------------------------------------------------
83
+ const transport = process.env.TRANSPORT || "stdio";
84
+ if (transport === "http") {
85
+ runHTTP().catch((error) => {
86
+ console.error("ATOM MCP Server HTTP error:", error);
87
+ process.exit(1);
88
+ });
89
+ } else {
90
+ runStdio().catch((error) => {
91
+ console.error("ATOM MCP Server stdio error:", error);
92
+ process.exit(1);
93
+ });
94
+ }