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.
- package/Dockerfile +18 -0
- package/README.md +184 -0
- package/claude_desktop_config.json +8 -0
- package/dist/auth.d.ts +38 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +75 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +234 -0
- package/dist/server.js.map +1 -0
- package/dist/supabase.d.ts +18 -0
- package/dist/supabase.d.ts.map +1 -0
- package/dist/supabase.js +86 -0
- package/dist/supabase.js.map +1 -0
- package/dist/tools/compare-prices.d.ts +16 -0
- package/dist/tools/compare-prices.d.ts.map +1 -0
- package/dist/tools/compare-prices.js +152 -0
- package/dist/tools/compare-prices.js.map +1 -0
- package/dist/tools/get-index-benchmarks.d.ts +14 -0
- package/dist/tools/get-index-benchmarks.d.ts.map +1 -0
- package/dist/tools/get-index-benchmarks.js +99 -0
- package/dist/tools/get-index-benchmarks.js.map +1 -0
- package/dist/tools/get-kpis.d.ts +10 -0
- package/dist/tools/get-kpis.d.ts.map +1 -0
- package/dist/tools/get-kpis.js +45 -0
- package/dist/tools/get-kpis.js.map +1 -0
- package/dist/tools/get-market-stats.d.ts +12 -0
- package/dist/tools/get-market-stats.d.ts.map +1 -0
- package/dist/tools/get-market-stats.js +95 -0
- package/dist/tools/get-market-stats.js.map +1 -0
- package/dist/tools/get-model-detail.d.ts +12 -0
- package/dist/tools/get-model-detail.d.ts.map +1 -0
- package/dist/tools/get-model-detail.js +96 -0
- package/dist/tools/get-model-detail.js.map +1 -0
- package/dist/tools/get-vendor-catalog.d.ts +15 -0
- package/dist/tools/get-vendor-catalog.d.ts.map +1 -0
- package/dist/tools/get-vendor-catalog.js +102 -0
- package/dist/tools/get-vendor-catalog.js.map +1 -0
- package/dist/tools/list-vendors.d.ts +13 -0
- package/dist/tools/list-vendors.d.ts.map +1 -0
- package/dist/tools/list-vendors.js +49 -0
- package/dist/tools/list-vendors.js.map +1 -0
- package/dist/tools/search-models.d.ts +22 -0
- package/dist/tools/search-models.d.ts.map +1 -0
- package/dist/tools/search-models.js +128 -0
- package/dist/tools/search-models.js.map +1 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/env.example +17 -0
- package/package.json +52 -0
- package/railway.json +13 -0
- package/smithery.yaml +36 -0
- package/src/auth.ts +101 -0
- package/src/index.ts +94 -0
- package/src/server.ts +278 -0
- package/src/supabase.ts +125 -0
- package/src/tools/compare-prices.ts +175 -0
- package/src/tools/get-index-benchmarks.ts +122 -0
- package/src/tools/get-kpis.ts +62 -0
- package/src/tools/get-market-stats.ts +112 -0
- package/src/tools/get-model-detail.ts +119 -0
- package/src/tools/get-vendor-catalog.ts +121 -0
- package/src/tools/list-vendors.ts +60 -0
- package/src/tools/search-models.ts +146 -0
- package/src/types.ts +145 -0
- 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":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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":"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"}
|
package/dist/types.js
ADDED
|
@@ -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
|
+
}
|