@costlens/mcp-server 0.4.1 → 0.4.3

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 (2) hide show
  1. package/dist/cli.js +444 -61
  2. package/package.json +2 -5
package/dist/cli.js CHANGED
@@ -1,73 +1,458 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ function resolveApiKey() {
33
+ const envKey = process.env.COSTLENS_API_KEY || process.env.COSTLENS_MCP_KEY || process.argv.find((a) => a.startsWith("--api-key="))?.split("=")[1];
34
+ if (envKey) return envKey;
35
+ try {
36
+ const config = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)((0, import_os.homedir)(), ".costlens", "config.json"), "utf-8"));
37
+ return config.apiKey;
38
+ } catch {
39
+ return void 0;
40
+ }
41
+ }
42
+ async function loadPricing() {
43
+ try {
44
+ const headers = {};
45
+ if (API_KEY) headers["Authorization"] = `Bearer ${API_KEY}`;
46
+ const res = await fetch(`${API_BASE}/v1/pricing`, { headers, signal: AbortSignal.timeout(5e3) });
47
+ if (res.ok) {
48
+ const data = await res.json();
49
+ for (const m of data.models) {
50
+ import_classifier.PRICING[m.model] = { input: m.input, output: m.output };
51
+ }
52
+ }
53
+ } catch {
54
+ }
55
+ }
56
+ function startHeartbeat(sessionId) {
57
+ stopHeartbeat();
58
+ activeSessionId = sessionId;
59
+ heartbeatInterval = setInterval(async () => {
60
+ if (!activeSessionId || !API_KEY) return;
61
+ try {
62
+ const res = await fetch(`${API_BASE}/v1/productivity/heartbeat`, {
63
+ method: "POST",
64
+ headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" },
65
+ body: JSON.stringify({ session_id: activeSessionId }),
66
+ signal: AbortSignal.timeout(5e3)
67
+ });
68
+ if (res.status === 410) {
69
+ activeSessionId = null;
70
+ stopHeartbeat();
71
+ }
72
+ } catch {
73
+ }
74
+ }, 6e4);
75
+ }
76
+ function stopHeartbeat() {
77
+ if (heartbeatInterval) {
78
+ clearInterval(heartbeatInterval);
79
+ heartbeatInterval = null;
80
+ }
81
+ }
82
+ async function syncEvent(result, params) {
83
+ try {
84
+ await fetch(`${API_BASE}/v1/classifier/events`, {
85
+ method: "POST",
86
+ headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" },
87
+ body: JSON.stringify({
88
+ complexity: result.level,
89
+ confidence: result.confidence,
90
+ suggestedModel: result.suggestedModel,
91
+ currentModel: params.currentModel,
92
+ provider: params.provider,
93
+ staticScore: result.signals.staticScore
94
+ }),
95
+ signal: AbortSignal.timeout(5e3)
96
+ });
97
+ } catch {
98
+ }
99
+ }
100
+ async function checkFeature(feature) {
101
+ if (!API_KEY) return false;
102
+ try {
103
+ const res = await fetch(`${API_BASE}/v1/plan`, {
104
+ headers: { "Authorization": `Bearer ${API_KEY}` },
105
+ signal: AbortSignal.timeout(5e3)
106
+ });
107
+ if (!res.ok) return false;
108
+ const plan = await res.json();
109
+ return plan.features?.includes(feature) ?? false;
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+ async function ensureProductivity() {
115
+ if (!API_KEY) return "Not connected to CostLens. Run: npx @costlens/mcp-server setup";
116
+ if (productivityEnabled === null) {
117
+ productivityEnabled = await checkFeature("productivity_basic");
118
+ }
119
+ if (!productivityEnabled) return "Productivity tracking requires Business plan or Productivity add-on.";
120
+ return null;
121
+ }
122
+ async function main() {
123
+ await loadPricing();
124
+ const transport = new import_stdio.StdioServerTransport();
125
+ await server.connect(transport);
126
+ const cleanup = async () => {
127
+ if (activeSessionId && API_KEY) {
128
+ try {
129
+ await fetch(`${API_BASE}/v1/productivity/sessions/${activeSessionId}`, {
130
+ method: "PATCH",
131
+ headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" },
132
+ body: JSON.stringify({ outcome: "completed" }),
133
+ signal: AbortSignal.timeout(3e3)
134
+ });
135
+ } catch {
136
+ }
137
+ }
138
+ stopHeartbeat();
139
+ process.exit(0);
140
+ };
141
+ process.on("SIGTERM", cleanup);
142
+ process.on("SIGINT", cleanup);
143
+ }
144
+ var import_mcp, import_stdio, import_zod, import_classifier, import_fs, import_path, import_os, API_KEY, API_BASE, server, activeSessionId, heartbeatInterval, productivityEnabled;
145
+ var init_index = __esm({
146
+ "src/index.ts"() {
147
+ "use strict";
148
+ import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
149
+ import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
150
+ import_zod = require("zod");
151
+ import_classifier = require("@lens360/classifier");
152
+ import_fs = require("fs");
153
+ import_path = require("path");
154
+ import_os = require("os");
155
+ API_KEY = resolveApiKey();
156
+ API_BASE = process.env.COSTLENS_API_URL || "https://api.costlens.dev";
157
+ server = new import_mcp.McpServer({
158
+ name: "@lens360/mcp-server",
159
+ version: "0.3.1"
160
+ });
161
+ activeSessionId = null;
162
+ heartbeatInterval = null;
163
+ server.tool(
164
+ "get_spend_summary",
165
+ "Get current spend summary \u2014 daily, weekly, monthly, and session totals",
166
+ {},
167
+ async () => {
168
+ if (!API_KEY) {
169
+ return { content: [{ type: "text", text: "Not connected to CostLens. Run: npx @costlens/mcp-server setup" }] };
170
+ }
171
+ try {
172
+ const res = await fetch(`${API_BASE}/v1/spend`, {
173
+ headers: { "Authorization": `Bearer ${API_KEY}` },
174
+ signal: AbortSignal.timeout(5e3)
175
+ });
176
+ if (!res.ok) {
177
+ return { content: [{ type: "text", text: `Error: ${res.status} \u2014 ${await res.text()}` }] };
178
+ }
179
+ const data = await res.json();
180
+ const lines = [
181
+ `\u{1F4B0} Spend Summary (${data.key})`,
182
+ ``,
183
+ `Today: $${data.daily.cost.toFixed(4)} (${data.daily.requests} requests)`,
184
+ `Week: $${data.weekly.cost.toFixed(4)} (${data.weekly.requests} requests)`,
185
+ `Month: $${data.monthly.cost.toFixed(4)} (${data.monthly.requests} requests)`
186
+ ];
187
+ if (data.session) {
188
+ lines.push(`Session: $${data.session.cost.toFixed(4)} (${data.session.requests} requests) [${data.session.correlationId}]`);
189
+ }
190
+ if (data.dailyBudget) {
191
+ lines.push(``, `Budget: $${data.budgetRemaining.toFixed(2)} remaining of $${data.dailyBudget.toFixed(2)}/day`);
192
+ }
193
+ return { content: [{ type: "text", text: lines.join("\n") }] };
194
+ } catch (err) {
195
+ return { content: [{ type: "text", text: `Error fetching spend: ${err.message}` }] };
196
+ }
197
+ }
198
+ );
199
+ server.tool(
200
+ "costlens_suggest_model",
201
+ "Analyze a prompt and suggest the cheapest adequate model",
202
+ {
203
+ prompt: import_zod.z.string(),
204
+ currentModel: import_zod.z.string(),
205
+ provider: import_zod.z.string(),
206
+ messageCount: import_zod.z.number().optional(),
207
+ routingMode: import_zod.z.string().optional(),
208
+ isFirstMessage: import_zod.z.boolean().optional()
209
+ },
210
+ async (params) => {
211
+ const result = (0, import_classifier.classify)({
212
+ prompt: params.prompt,
213
+ currentModel: params.currentModel,
214
+ provider: params.provider,
215
+ messageCount: params.messageCount,
216
+ routingMode: params.routingMode || void 0,
217
+ isFirstMessage: params.isFirstMessage
218
+ });
219
+ if (API_KEY && result.suggestedModel) {
220
+ syncEvent(result, params).catch(() => {
221
+ });
222
+ }
223
+ return {
224
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
225
+ };
226
+ }
227
+ );
228
+ productivityEnabled = null;
229
+ server.tool(
230
+ "start_session",
231
+ "Start a productivity tracking session",
232
+ {
233
+ task_description: import_zod.z.string().optional(),
234
+ branch: import_zod.z.string().optional(),
235
+ repo: import_zod.z.string().optional()
236
+ },
237
+ async (params) => {
238
+ const err = await ensureProductivity();
239
+ if (err) return { content: [{ type: "text", text: err }] };
240
+ try {
241
+ const res = await fetch(`${API_BASE}/v1/productivity/sessions`, {
242
+ method: "POST",
243
+ headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" },
244
+ body: JSON.stringify(params),
245
+ signal: AbortSignal.timeout(5e3)
246
+ });
247
+ if (!res.ok) return { content: [{ type: "text", text: `Error: ${res.status} \u2014 ${await res.text()}` }] };
248
+ const data = await res.json();
249
+ if (data.data?.id) startHeartbeat(data.data.id);
250
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
251
+ } catch (e) {
252
+ return { content: [{ type: "text", text: `Error: ${e.message}` }] };
253
+ }
254
+ }
255
+ );
256
+ server.tool(
257
+ "end_session",
258
+ "End a productivity tracking session",
259
+ {
260
+ session_id: import_zod.z.string(),
261
+ outcome: import_zod.z.enum(["completed", "abandoned", "paused"]).optional(),
262
+ artifacts: import_zod.z.array(import_zod.z.string()).optional()
263
+ },
264
+ async (params) => {
265
+ const err = await ensureProductivity();
266
+ if (err) return { content: [{ type: "text", text: err }] };
267
+ try {
268
+ const res = await fetch(`${API_BASE}/v1/productivity/sessions/${params.session_id}`, {
269
+ method: "PATCH",
270
+ headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" },
271
+ body: JSON.stringify({ outcome: params.outcome, artifacts: params.artifacts }),
272
+ signal: AbortSignal.timeout(5e3)
273
+ });
274
+ if (!res.ok) return { content: [{ type: "text", text: `Error: ${res.status} \u2014 ${await res.text()}` }] };
275
+ const data = await res.json();
276
+ stopHeartbeat();
277
+ activeSessionId = null;
278
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
279
+ } catch (e) {
280
+ return { content: [{ type: "text", text: `Error: ${e.message}` }] };
281
+ }
282
+ }
283
+ );
284
+ server.tool(
285
+ "log_event",
286
+ "Log a productivity event within a session",
287
+ {
288
+ session_id: import_zod.z.string(),
289
+ event_type: import_zod.z.enum(["prompt", "commit", "pr_created", "test_run", "deploy", "file_edit"]),
290
+ metadata: import_zod.z.record(import_zod.z.any()).optional()
291
+ },
292
+ async (params) => {
293
+ if (!API_KEY) return { content: [{ type: "text", text: "Not connected to CostLens. Run: npx @costlens/mcp-server setup" }] };
294
+ if (productivityEnabled === null) {
295
+ productivityEnabled = await checkFeature("productivity_basic");
296
+ }
297
+ const hasFull = await checkFeature("productivity_full");
298
+ if (!hasFull) return { content: [{ type: "text", text: "Event logging requires Productivity Insights add-on." }] };
299
+ try {
300
+ const res = await fetch(`${API_BASE}/v1/productivity/events`, {
301
+ method: "POST",
302
+ headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" },
303
+ body: JSON.stringify(params),
304
+ signal: AbortSignal.timeout(5e3)
305
+ });
306
+ if (!res.ok) return { content: [{ type: "text", text: `Error: ${res.status} \u2014 ${await res.text()}` }] };
307
+ return { content: [{ type: "text", text: "Event logged." }] };
308
+ } catch (e) {
309
+ return { content: [{ type: "text", text: `Error: ${e.message}` }] };
310
+ }
311
+ }
312
+ );
313
+ server.tool(
314
+ "get_productivity_summary",
315
+ "Get productivity summary for a time period",
316
+ {
317
+ period: import_zod.z.enum(["today", "week", "month"]).optional()
318
+ },
319
+ async (params) => {
320
+ const err = await ensureProductivity();
321
+ if (err) return { content: [{ type: "text", text: err }] };
322
+ try {
323
+ const period = params.period || "week";
324
+ const res = await fetch(`${API_BASE}/v1/productivity/summary?period=${period}`, {
325
+ headers: { "Authorization": `Bearer ${API_KEY}` },
326
+ signal: AbortSignal.timeout(5e3)
327
+ });
328
+ if (!res.ok) return { content: [{ type: "text", text: `Error: ${res.status} \u2014 ${await res.text()}` }] };
329
+ const data = await res.json();
330
+ const lines = [
331
+ `\u{1F4CA} Productivity Summary (${period})`,
332
+ ``,
333
+ `Sessions: ${data.sessions}`,
334
+ `Time: ${data.total_time_minutes} min`,
335
+ `Tokens: ${data.tokens_used?.toLocaleString() || 0}`,
336
+ `Cost: $${data.cost?.toFixed(4) || "0.00"}`,
337
+ `Commits: ${data.commits || 0}`,
338
+ `PRs merged: ${data.prs_merged || 0}`
339
+ ];
340
+ if (data.cost_per_commit) lines.push(`Cost/commit: $${data.cost_per_commit.toFixed(4)}`);
341
+ if (data.cost_per_pr) lines.push(`Cost/PR: $${data.cost_per_pr.toFixed(4)}`);
342
+ if (data.estimated_time_saved_hours) lines.push(`Time saved: ~${data.estimated_time_saved_hours.toFixed(1)}h`);
343
+ return { content: [{ type: "text", text: lines.join("\n") }] };
344
+ } catch (e) {
345
+ return { content: [{ type: "text", text: `Error: ${e.message}` }] };
346
+ }
347
+ }
348
+ );
349
+ server.tool(
350
+ "get_github_metrics",
351
+ "Get GitHub PR metrics \u2014 PRs merged, review rounds, first-pass rate, cost per PR",
352
+ {
353
+ period: import_zod.z.enum(["today", "week", "month"]).optional()
354
+ },
355
+ async (params) => {
356
+ if (!API_KEY) {
357
+ return { content: [{ type: "text", text: "Not connected to CostLens. Run: npx @costlens/mcp-server setup" }] };
358
+ }
359
+ try {
360
+ const period = params.period || "week";
361
+ const res = await fetch(`${API_BASE}/v1/productivity/github?period=${period}`, {
362
+ headers: { "Authorization": `Bearer ${API_KEY}` },
363
+ signal: AbortSignal.timeout(5e3)
364
+ });
365
+ if (res.status === 404) {
366
+ return { content: [{ type: "text", text: "GitHub not connected. Connect in Settings \u2192 GitHub at costlens.dev" }] };
367
+ }
368
+ if (!res.ok) {
369
+ return { content: [{ type: "text", text: `Error: ${res.status} \u2014 ${await res.text()}` }] };
370
+ }
371
+ const { data } = await res.json();
372
+ const lines = [
373
+ `GitHub Metrics (${period}):`,
374
+ `PRs merged: ${data.prsMerged}`,
375
+ `Avg review rounds: ${data.avgReviewRounds}`,
376
+ `First-pass rate: ${data.firstPassRate}%`,
377
+ `Avg time to merge: ${data.avgTimeToMergeHours}h`,
378
+ `Cost per PR: $${data.costPerPr}`
379
+ ];
380
+ return { content: [{ type: "text", text: lines.join("\n") }] };
381
+ } catch (e) {
382
+ return { content: [{ type: "text", text: `Error: ${e.message}` }] };
383
+ }
384
+ }
385
+ );
386
+ main().catch(console.error);
387
+ }
388
+ });
3
389
 
4
390
  // src/cli.ts
5
- var import_http = require("http");
6
391
  var import_child_process = require("child_process");
7
- var import_fs = require("fs");
8
- var import_os = require("os");
9
- var import_path = require("path");
10
- var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".costlens");
11
- var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
12
- var API_BASE = process.env.COSTLENS_API_URL || "https://api.costlens.dev";
392
+ var import_fs2 = require("fs");
393
+ var import_os2 = require("os");
394
+ var import_path2 = require("path");
395
+ var import_crypto = __toESM(require("crypto"));
396
+ var CONFIG_DIR = (0, import_path2.join)((0, import_os2.homedir)(), ".costlens");
397
+ var CONFIG_FILE = (0, import_path2.join)(CONFIG_DIR, "config.json");
398
+ var API_BASE2 = process.env.COSTLENS_API_URL || "https://api.costlens.dev";
13
399
  var APP_URL = process.env.COSTLENS_APP_URL || "https://costlens.dev";
14
400
  function readConfig() {
15
401
  try {
16
- return JSON.parse((0, import_fs.readFileSync)(CONFIG_FILE, "utf-8"));
402
+ return JSON.parse((0, import_fs2.readFileSync)(CONFIG_FILE, "utf-8"));
17
403
  } catch {
18
404
  return {};
19
405
  }
20
406
  }
21
407
  function writeConfig(config) {
22
- if (!(0, import_fs.existsSync)(CONFIG_DIR)) (0, import_fs.mkdirSync)(CONFIG_DIR, { recursive: true });
23
- (0, import_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2));
408
+ if (!(0, import_fs2.existsSync)(CONFIG_DIR)) (0, import_fs2.mkdirSync)(CONFIG_DIR, { recursive: true });
409
+ (0, import_fs2.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2));
24
410
  }
25
411
  async function login() {
26
412
  console.log("\u{1F511} CostLens \u2014 Authenticating...\n");
27
- const port = 9876 + Math.floor(Math.random() * 100);
28
- const server = (0, import_http.createServer)((req, res) => {
29
- const url = new URL(req.url, `http://localhost:${port}`);
30
- const key = url.searchParams.get("key");
31
- if (key) {
32
- writeConfig({ apiKey: key });
33
- res.writeHead(200, { "Content-Type": "text/html" });
34
- res.end('<html><body style="font-family:sans-serif;text-align:center;padding:60px"><h2>Authenticated</h2><p>You can close this tab and return to your terminal.</p></body></html>');
35
- console.log("\u2713 Authenticated successfully");
36
- console.log(` Key saved to ${CONFIG_FILE}
413
+ const sessionId = import_crypto.default.randomBytes(16).toString("hex");
414
+ const authUrl = `${APP_URL}/cli-auth?session=${sessionId}`;
415
+ console.log(` Opening browser: ${authUrl}
37
416
  `);
38
- setTimeout(() => {
39
- server.close();
40
- if (process.argv[2] === "setup") {
41
- init();
42
- } else {
43
- process.exit(0);
44
- }
45
- }, 500);
46
- } else {
47
- res.writeHead(400);
48
- res.end("Missing key");
49
- }
50
- });
51
- server.listen(port, () => {
52
- const authUrl = `${APP_URL}/cli-auth?port=${port}`;
53
- console.log(` Opening browser: ${authUrl}
54
- `);
55
- const platform = process.platform;
417
+ const platform = process.platform;
418
+ try {
419
+ if (platform === "darwin") (0, import_child_process.execSync)(`open "${authUrl}"`);
420
+ else if (platform === "linux") (0, import_child_process.execSync)(`xdg-open "${authUrl}"`);
421
+ else if (platform === "win32") (0, import_child_process.execSync)(`start "${authUrl}"`);
422
+ else console.log(` Open this URL manually: ${authUrl}`);
423
+ } catch {
424
+ console.log(` Open this URL manually: ${authUrl}`);
425
+ }
426
+ console.log(" Waiting for authentication...");
427
+ const startTime = Date.now();
428
+ const TIMEOUT = 12e4;
429
+ const INTERVAL = 2e3;
430
+ while (Date.now() - startTime < TIMEOUT) {
431
+ await new Promise((r) => setTimeout(r, INTERVAL));
56
432
  try {
57
- if (platform === "darwin") (0, import_child_process.execSync)(`open "${authUrl}"`);
58
- else if (platform === "linux") (0, import_child_process.execSync)(`xdg-open "${authUrl}"`);
59
- else if (platform === "win32") (0, import_child_process.execSync)(`start "${authUrl}"`);
60
- else console.log(` Open this URL manually: ${authUrl}`);
433
+ const res = await fetch(`${APP_URL}/api/cli-auth/poll?session=${sessionId}`, {
434
+ signal: AbortSignal.timeout(5e3)
435
+ });
436
+ if (res.ok) {
437
+ const data = await res.json();
438
+ if (data.key) {
439
+ writeConfig({ apiKey: data.key });
440
+ console.log("\n\u2713 Authenticated successfully");
441
+ console.log(` Key saved to ${CONFIG_FILE}
442
+ `);
443
+ if (process.argv[2] === "setup") {
444
+ init();
445
+ } else {
446
+ process.exit(0);
447
+ }
448
+ return;
449
+ }
450
+ }
61
451
  } catch {
62
- console.log(` Open this URL manually: ${authUrl}`);
63
452
  }
64
- console.log(" Waiting for authentication...");
65
- });
66
- setTimeout(() => {
67
- console.log("\n\u2717 Timed out. Try again.");
68
- server.close();
69
- process.exit(1);
70
- }, 12e4);
453
+ }
454
+ console.log("\n\u2717 Timed out. Try again.");
455
+ process.exit(1);
71
456
  }
72
457
  function init() {
73
458
  const config = readConfig();
@@ -94,21 +479,21 @@ function init() {
94
479
  console.log(" Claude Code: ~/.claude/mcp_servers.json");
95
480
  console.log(" VS Code: .vscode/mcp.json");
96
481
  console.log("");
97
- const kiroPath = (0, import_path.join)((0, import_os.homedir)(), ".kiro", "settings", "mcp.json");
98
- const cursorPath = (0, import_path.join)((0, import_os.homedir)(), ".cursor", "mcp.json");
99
- const claudePath = (0, import_path.join)((0, import_os.homedir)(), ".claude", "mcp_servers.json");
482
+ const kiroPath = (0, import_path2.join)((0, import_os2.homedir)(), ".kiro", "settings", "mcp.json");
483
+ const cursorPath = (0, import_path2.join)((0, import_os2.homedir)(), ".cursor", "mcp.json");
484
+ const claudePath = (0, import_path2.join)((0, import_os2.homedir)(), ".claude", "mcp_servers.json");
100
485
  const paths = [
101
486
  { name: "Kiro", path: kiroPath },
102
487
  { name: "Cursor", path: cursorPath },
103
488
  { name: "Claude Code", path: claudePath }
104
489
  ];
105
490
  for (const { name, path } of paths) {
106
- if ((0, import_fs.existsSync)(path)) {
491
+ if ((0, import_fs2.existsSync)(path)) {
107
492
  try {
108
- const existing = JSON.parse((0, import_fs.readFileSync)(path, "utf-8"));
493
+ const existing = JSON.parse((0, import_fs2.readFileSync)(path, "utf-8"));
109
494
  if (!existing.mcpServers?.costlens) {
110
495
  existing.mcpServers = { ...existing.mcpServers, ...mcpConfig.mcpServers };
111
- (0, import_fs.writeFileSync)(path, JSON.stringify(existing, null, 2));
496
+ (0, import_fs2.writeFileSync)(path, JSON.stringify(existing, null, 2));
112
497
  console.log(` \u2713 Auto-configured ${name} (${path})`);
113
498
  } else {
114
499
  console.log(` \xB7 ${name} already configured`);
@@ -137,7 +522,7 @@ async function status() {
137
522
  }
138
523
  console.log("\u{1F4CA} CostLens Status\n");
139
524
  try {
140
- const res = await fetch(`${API_BASE}/v1/spend`, {
525
+ const res = await fetch(`${API_BASE2}/v1/spend`, {
141
526
  headers: { Authorization: `Bearer ${config.apiKey}` },
142
527
  signal: AbortSignal.timeout(5e3)
143
528
  });
@@ -161,10 +546,8 @@ if (command === "login") login();
161
546
  else if (command === "init") init();
162
547
  else if (command === "status") status();
163
548
  else if (command === "setup") setup();
164
- else if (command) {
165
- console.log(`Unknown command: ${command}`);
166
- console.log("Available: setup, login, init, status");
167
- process.exit(1);
549
+ else {
550
+ init_index();
168
551
  }
169
552
  async function setup() {
170
553
  console.log("\u{1F527} CostLens \u2014 One-step setup\n");
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "@costlens/mcp-server",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "MCP server for AI cost optimization with prompt complexity classification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
- "bin": {
8
- "costlens-mcp": "./dist/index.js",
9
- "costlens": "./dist/cli.js"
10
- },
7
+ "bin": "./dist/cli.js",
11
8
  "scripts": {
12
9
  "build": "tsup src/index.ts src/cli.ts --format cjs --clean",
13
10
  "test": "echo 'no tests yet'"