@agentistics/mcp 1.3.3 → 1.7.5

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/README.md CHANGED
@@ -4,7 +4,7 @@ MCP server that exposes your [Claude Code](https://claude.ai/code) analytics as
4
4
 
5
5
  ## What it does
6
6
 
7
- Reads data from `~/.claude/` (the same data [agentistics](https://github.com/blpsoares/agentistics) visualizes) and exposes it as 12 structured MCP tools.
7
+ Reads data from `~/.claude/` (the same data [agentistics](https://github.com/blpsoares/agentistics) visualizes) and exposes it as 13 structured MCP tools.
8
8
 
9
9
  ## Usage
10
10
 
@@ -36,10 +36,11 @@ The `AGENTISTICS_API` env var must point to a running agentistics server.
36
36
 
37
37
  | Tool | Returns |
38
38
  |------|---------|
39
- | `agentistics_summary` | All-time totals: tokens, cost, sessions, streak, cache hit rate |
40
- | `agentistics_projects` | Per-project token and cost breakdown |
41
- | `agentistics_sessions` | Recent sessions with duration, model, cost |
42
- | `agentistics_costs` | Model pricing breakdown and cache savings |
39
+ | `agentistics_summary` | All-time totals: tokens, cost, sessions, streak unified or per-harness |
40
+ | `agentistics_harnesses` | Side-by-side comparison of every tracked harness |
41
+ | `agentistics_projects` | Per-project token and cost breakdown (optionally per-harness) |
42
+ | `agentistics_sessions` | Recent sessions with harness, duration, model, cost |
43
+ | `agentistics_costs` | Model pricing breakdown and cache savings (optionally per-harness) |
43
44
  | `agentistics_component_catalog` | Available dashboard components |
44
45
  | `agentistics_get_layouts` | Current custom page layouts |
45
46
  | `agentistics_build_layout` | Create a full layout from a component list |
@@ -48,6 +49,7 @@ The `AGENTISTICS_API` env var must point to a running agentistics server.
48
49
  | `agentistics_create_layout` | Create a new empty layout |
49
50
  | `agentistics_set_active_layout` | Switch the active /custom layout |
50
51
  | `agentistics_delete_layout` | Delete a layout permanently |
52
+ | `agentistics_export_pdf` | Generate a PDF report download link for a date range |
51
53
 
52
54
  ## Requirements
53
55
 
@@ -9,8 +9,22 @@ import {
9
9
  } from "@modelcontextprotocol/sdk/types.js";
10
10
 
11
11
  // ../core/src/types.ts
12
+ var HARNESS_SORT = {
13
+ claude: 0,
14
+ codex: 1,
15
+ gemini: 2,
16
+ copilot: 3,
17
+ antigravity: 4,
18
+ kimi: 5
19
+ };
20
+ var HARNESS_ORDER = Object.keys(HARNESS_SORT).sort((a, b) => HARNESS_SORT[a] - HARNESS_SORT[b]);
12
21
  var MODEL_PRICING = {
22
+ "claude-fable-5": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
23
+ "claude-mythos-5": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
24
+ "claude-opus-5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
25
+ "claude-opus-4-8": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
13
26
  "claude-opus-4-7": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
27
+ "claude-sonnet-5": { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 },
14
28
  "claude-opus-4-6": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
15
29
  "claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
16
30
  "claude-haiku-4-5-20251001": { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
@@ -20,21 +34,55 @@ var MODEL_PRICING = {
20
34
  "claude-sonnet-4-5-20250929": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
21
35
  "claude-sonnet-4-20250514": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
22
36
  "claude-haiku-3-5-20241022": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
23
- "claude-3-haiku-20240307": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 }
37
+ "claude-3-haiku-20240307": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 },
38
+ "gemini-3.6-flash": { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 1.5 },
39
+ "gemini-3.5-flash-lite": { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0.3 },
40
+ "gemini-3.5-flash": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 1.5 },
41
+ "gemini-3.1-flash-lite": { input: 0.25, output: 1.5, cacheRead: 0.025, cacheWrite: 0.25 },
42
+ "gemini-3.1-pro": { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 2 },
43
+ "gemini-3-flash-preview": { input: 0.5, output: 3, cacheRead: 0.05, cacheWrite: 0.5 },
44
+ "gemini-3-flash": { input: 0.5, output: 3, cacheRead: 0.05, cacheWrite: 0.5 },
45
+ "gemini-2.5-flash": { input: 0.3, output: 2.5, cacheRead: 0.03, cacheWrite: 0.3 },
46
+ "gpt-5.6-sol": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 5 },
47
+ "gpt-5.6-terra": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 2.5 },
48
+ "gpt-5.6-luna": { input: 1, output: 6, cacheRead: 0.1, cacheWrite: 1 },
49
+ "gpt-5.5": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 5 },
50
+ "gpt-5.4-mini": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0.75 },
51
+ "gpt-5.4": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 2.5 },
52
+ "gpt-5-mini": { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0.25 },
53
+ "gpt-5": { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 }
24
54
  };
25
55
  function getModelPrice(modelId) {
26
56
  if (MODEL_PRICING[modelId])
27
57
  return MODEL_PRICING[modelId];
28
- for (const [key, price] of Object.entries(MODEL_PRICING)) {
29
- if (modelId.startsWith(key) || key.startsWith(modelId))
30
- return price;
58
+ const id = String(modelId ?? "");
59
+ let forwardKey = "";
60
+ let reverseKey = "";
61
+ for (const key of Object.keys(MODEL_PRICING)) {
62
+ if (id.startsWith(key)) {
63
+ if (key.length > forwardKey.length)
64
+ forwardKey = key;
65
+ } else if (id && key.startsWith(id) && key[id.length] === "-") {
66
+ if (!reverseKey || key.length < reverseKey.length)
67
+ reverseKey = key;
68
+ }
31
69
  }
70
+ const hit = forwardKey || reverseKey;
71
+ if (hit)
72
+ return MODEL_PRICING[hit];
32
73
  return { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
33
74
  }
34
75
  function calcCost(usage, modelId) {
35
76
  const price = getModelPrice(modelId);
36
77
  return usage.inputTokens / 1e6 * price.input + usage.outputTokens / 1e6 * price.output + usage.cacheReadInputTokens / 1e6 * price.cacheRead + usage.cacheCreationInputTokens / 1e6 * price.cacheWrite;
37
78
  }
79
+ // ../core/src/team.ts
80
+ var DEFAULT_TEAM = Object.freeze({
81
+ schema: 2,
82
+ mode: "solo",
83
+ connections: Object.freeze([])
84
+ });
85
+ var SHARE_SOURCE_TYPES = new Set(["repo", "project", "none"]);
38
86
  // agentistics-mcp.ts
39
87
  var API = process.env.AGENTISTICS_API ?? "http://localhost:47291";
40
88
  function calcCostUSD(input, output, cacheRead, cacheWrite, model) {
@@ -47,6 +95,30 @@ function calcCostUSD(input, output, cacheRead, cacheWrite, model) {
47
95
  costUSD: 0
48
96
  }, model);
49
97
  }
98
+ var HARNESS_IDS = ["claude", "codex", "gemini", "copilot", "antigravity"];
99
+ var HARNESS_PARAM = {
100
+ type: "string",
101
+ enum: ["all", ...HARNESS_IDS],
102
+ description: "Scope to one harness (claude | codex | gemini | copilot | antigravity), or 'all' (default) for the unified view across every harness."
103
+ };
104
+ function sessionHarness(s) {
105
+ return s.harness ?? "claude";
106
+ }
107
+ function filterSessions(sessions, harness) {
108
+ if (!harness || harness === "all")
109
+ return sessions;
110
+ return sessions.filter((s) => sessionHarness(s) === harness);
111
+ }
112
+ function sessionTokens(s) {
113
+ const input = s.input_tokens ?? 0;
114
+ const output = s.output_tokens ?? 0;
115
+ const cacheRead = s.cache_read_input_tokens ?? 0;
116
+ const cacheWrite = s.cache_creation_input_tokens ?? 0;
117
+ return { input, output, cacheRead, cacheWrite, cost: calcCostUSD(input, output, cacheRead, cacheWrite, s.model ?? "") };
118
+ }
119
+ function sessionMessages(s) {
120
+ return (s.user_message_count ?? 0) + (s.assistant_message_count ?? 0);
121
+ }
50
122
  var CATALOG = [
51
123
  { id: "kpi.messages", label: "Messages", category: "kpi", defaultW: 3, defaultH: 3, minW: 2, minH: 2, description: "Total message count in selected period" },
52
124
  { id: "kpi.sessions", label: "Sessions", category: "kpi", defaultW: 3, defaultH: 3, minW: 2, minH: 2, description: "Session count + avg msgs/session" },
@@ -138,31 +210,37 @@ var server = new Server({ name: "agentistics", version: "1.0.0" }, { capabilitie
138
210
  var TOOLS = [
139
211
  {
140
212
  name: "agentistics_summary",
141
- description: "Get an overview of Claude Code usage metrics: total tokens, estimated cost, sessions, streak, most used model, and top project. Good starting point for any metrics question.",
213
+ description: "Get an overview of AI coding usage metrics (across all tracked harnesses — Claude Code, Codex, Gemini, Copilot, Antigravity — or scoped to one): total tokens, estimated cost, sessions, streak, most used model, and top project. Good starting point for any metrics question.",
214
+ inputSchema: { type: "object", properties: { harness: HARNESS_PARAM }, required: [] }
215
+ },
216
+ {
217
+ name: "agentistics_harnesses",
218
+ description: "Compare the tracked AI coding harnesses side by side. Lists every harness present in the data with its sessions, messages, token usage, estimated cost, and last-active date. Use this to answer 'which harness do I use most / costs most' questions.",
142
219
  inputSchema: { type: "object", properties: {}, required: [] }
143
220
  },
144
221
  {
145
222
  name: "agentistics_projects",
146
- description: "List all Claude Code projects with session counts, message counts, token usage, estimated cost, and last active date.",
147
- inputSchema: { type: "object", properties: {}, required: [] }
223
+ description: "List projects with session counts, message counts, token usage, estimated cost, and last active date. Optionally scope to a single harness.",
224
+ inputSchema: { type: "object", properties: { harness: HARNESS_PARAM }, required: [] }
148
225
  },
149
226
  {
150
227
  name: "agentistics_sessions",
151
- description: "Get the most recent Claude Code sessions with project path, duration, message count, token usage, and model.",
228
+ description: "Get the most recent sessions with harness, project path, duration, message count, token usage, and model. Optionally scope to a single harness.",
152
229
  inputSchema: {
153
230
  type: "object",
154
231
  properties: {
155
232
  limit: {
156
233
  type: "number",
157
234
  description: "Max sessions to return (default 20, max 50)"
158
- }
235
+ },
236
+ harness: HARNESS_PARAM
159
237
  }
160
238
  }
161
239
  },
162
240
  {
163
241
  name: "agentistics_costs",
164
- description: "Get cost breakdown by model: token counts (input/output/cache read/write) and estimated USD cost per model.",
165
- inputSchema: { type: "object", properties: {}, required: [] }
242
+ description: "Get cost breakdown by model: token counts (input/output/cache read/write) and estimated USD cost per model. Scope to a single harness, or 'all' (default) for the unified breakdown.",
243
+ inputSchema: { type: "object", properties: { harness: HARNESS_PARAM }, required: [] }
166
244
  },
167
245
  {
168
246
  name: "agentistics_component_catalog",
@@ -308,71 +386,106 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
308
386
  try {
309
387
  switch (name) {
310
388
  case "agentistics_summary": {
389
+ const harness = args?.harness;
390
+ const unified = !harness || harness === "all";
311
391
  const data = await apiGet("/api/data");
312
392
  const sc = data.statsCache ?? {};
313
393
  const totals = sc.allTimeTotals ?? {};
314
- const models = sc.modelUsage ?? {};
315
- const topModel = Object.entries(models).sort(([, a], [, b]) => (b.totalTokens ?? 0) - (a.totalTokens ?? 0))[0]?.[0] ?? "—";
316
- const projects = data.projects ?? [];
317
- const topProject = [...projects].sort((a, b) => (b.sessions?.length ?? 0) - (a.sessions?.length ?? 0))[0]?.name ?? "—";
318
- const allSessions = data.sessions ?? [];
394
+ const allSessions = filterSessions(data.sessions ?? [], harness);
319
395
  let totalCostUSD = 0;
320
396
  let totalInput = 0, totalOutput = 0, totalCacheRead = 0, totalCacheWrite = 0;
397
+ const modelTokens = {};
398
+ const projectSessions = {};
399
+ const activeDates = new Set;
321
400
  for (const s of allSessions) {
322
- const inp = s.input_tokens ?? 0;
323
- const out = s.output_tokens ?? 0;
324
- const cr = s.cache_read_input_tokens ?? 0;
325
- const cw = s.cache_creation_input_tokens ?? 0;
326
- totalInput += inp;
327
- totalOutput += out;
328
- totalCacheRead += cr;
329
- totalCacheWrite += cw;
330
- totalCostUSD += calcCostUSD(inp, out, cr, cw, s.model ?? "");
401
+ const { input, output, cacheRead, cacheWrite, cost } = sessionTokens(s);
402
+ totalInput += input;
403
+ totalOutput += output;
404
+ totalCacheRead += cacheRead;
405
+ totalCacheWrite += cacheWrite;
406
+ totalCostUSD += cost;
407
+ if (s.model)
408
+ modelTokens[s.model] = (modelTokens[s.model] ?? 0) + input + output;
409
+ if (s.project_path)
410
+ projectSessions[s.project_path] = (projectSessions[s.project_path] ?? 0) + 1;
411
+ if (s.start_time)
412
+ activeDates.add(String(s.start_time).slice(0, 10));
331
413
  }
332
- const finalInput = totalInput || (totals.inputTokens ?? 0);
333
- const finalOutput = totalOutput || (totals.outputTokens ?? 0);
334
- const finalCacheRead = totalCacheRead || (totals.cacheReadTokens ?? 0);
335
- const finalCacheWrite = totalCacheWrite || (totals.cacheWriteTokens ?? 0);
414
+ const claudeFallback = unified || harness === "claude";
415
+ const topModel = Object.entries(modelTokens).sort(([, a], [, b]) => b - a)[0]?.[0] ?? (claudeFallback ? Object.entries(sc.modelUsage ?? {}).sort(([, a], [, b]) => (b.totalTokens ?? 0) - (a.totalTokens ?? 0))[0]?.[0] ?? "—" : "—");
416
+ const topProject = Object.entries(projectSessions).sort(([, a], [, b]) => b - a)[0]?.[0] ?? "—";
336
417
  return {
337
418
  content: [{
338
419
  type: "text",
339
420
  text: JSON.stringify({
340
- totalInputTokens: finalInput,
341
- totalOutputTokens: finalOutput,
342
- totalCacheReadTokens: finalCacheRead,
343
- totalCacheWriteTokens: finalCacheWrite,
421
+ harness: unified ? "all" : harness,
422
+ totalInputTokens: totalInput || (claudeFallback ? totals.inputTokens ?? 0 : 0),
423
+ totalOutputTokens: totalOutput || (claudeFallback ? totals.outputTokens ?? 0 : 0),
424
+ totalCacheReadTokens: totalCacheRead || (claudeFallback ? totals.cacheReadTokens ?? 0 : 0),
425
+ totalCacheWriteTokens: totalCacheWrite || (claudeFallback ? totals.cacheWriteTokens ?? 0 : 0),
344
426
  estimatedCostUSD: Math.round(totalCostUSD * 100) / 100,
345
427
  totalSessions: allSessions.length,
346
- totalProjects: projects.length,
347
428
  topModel,
348
429
  topProject,
349
- activeDays: sc.activeDays ?? 0,
350
- currentStreak: sc.currentStreak ?? 0
430
+ activeDays: activeDates.size || (claudeFallback ? sc.activeDays ?? 0 : 0),
431
+ currentStreak: claudeFallback ? sc.currentStreak ?? 0 : null
351
432
  }, null, 2)
352
433
  }]
353
434
  };
354
435
  }
436
+ case "agentistics_harnesses": {
437
+ const data = await apiGet("/api/data");
438
+ const present = data.harnesses ?? ["claude"];
439
+ const sessions = data.sessions ?? [];
440
+ const rows = present.map((h) => {
441
+ const hs = sessions.filter((s) => sessionHarness(s) === h);
442
+ let input = 0, output = 0, cacheRead = 0, cacheWrite = 0, cost = 0, messages = 0, lastActive = "";
443
+ for (const s of hs) {
444
+ const t = sessionTokens(s);
445
+ input += t.input;
446
+ output += t.output;
447
+ cacheRead += t.cacheRead;
448
+ cacheWrite += t.cacheWrite;
449
+ cost += t.cost;
450
+ messages += sessionMessages(s);
451
+ if (s.start_time && String(s.start_time) > lastActive)
452
+ lastActive = String(s.start_time);
453
+ }
454
+ return {
455
+ harness: h,
456
+ sessions: hs.length,
457
+ messages,
458
+ inputTokens: input,
459
+ outputTokens: output,
460
+ cacheReadTokens: cacheRead,
461
+ cacheWriteTokens: cacheWrite,
462
+ totalTokens: input + output + cacheRead + cacheWrite,
463
+ estimatedCostUSD: Math.round(cost * 100) / 100,
464
+ lastActive: lastActive || null
465
+ };
466
+ }).sort((a, b) => b.totalTokens - a.totalTokens);
467
+ return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
468
+ }
355
469
  case "agentistics_projects": {
470
+ const harness = args?.harness;
356
471
  const data = await apiGet("/api/data");
357
- const allSessions = data.sessions ?? [];
472
+ const allSessions = filterSessions(data.sessions ?? [], harness);
358
473
  const byPath = {};
359
474
  for (const s of allSessions) {
360
475
  const key = s.project_path;
361
476
  if (!key)
362
477
  continue;
363
478
  if (!byPath[key])
364
- byPath[key] = { inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
479
+ byPath[key] = { sessions: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
365
480
  const agg = byPath[key];
366
- const inp = s.input_tokens ?? 0;
367
- const out = s.output_tokens ?? 0;
368
- const cr = s.cache_read_input_tokens ?? 0;
369
- const cw = s.cache_creation_input_tokens ?? 0;
370
- agg.inputTokens += inp;
371
- agg.outputTokens += out;
372
- agg.cacheRead += cr;
373
- agg.cacheWrite += cw;
374
- agg.costUSD += calcCostUSD(inp, out, cr, cw, s.model ?? "");
375
- agg.messages += (s.user_message_count ?? 0) + (s.assistant_message_count ?? 0);
481
+ const { input, output, cacheRead, cacheWrite, cost } = sessionTokens(s);
482
+ agg.sessions += 1;
483
+ agg.inputTokens += input;
484
+ agg.outputTokens += output;
485
+ agg.cacheRead += cacheRead;
486
+ agg.cacheWrite += cacheWrite;
487
+ agg.costUSD += cost;
488
+ agg.messages += sessionMessages(s);
376
489
  if (!agg.lastActive || (s.start_time ?? "") > agg.lastActive)
377
490
  agg.lastActive = s.start_time ?? "";
378
491
  for (const lang of s.languages ?? [])
@@ -380,12 +493,12 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
380
493
  agg.languages.push(lang);
381
494
  }
382
495
  const projects = data.projects ?? [];
383
- const summary = projects.map((p) => {
384
- const agg = byPath[p.path] ?? { inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
496
+ const summary = projects.filter((p) => !harness || harness === "all" || byPath[p.path]).map((p) => {
497
+ const agg = byPath[p.path] ?? { sessions: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
385
498
  return {
386
499
  name: p.name,
387
500
  path: p.path,
388
- sessions: (p.sessions ?? []).length,
501
+ sessions: !harness || harness === "all" ? (p.sessions ?? []).length : agg.sessions,
389
502
  messages: agg.messages,
390
503
  inputTokens: agg.inputTokens,
391
504
  outputTokens: agg.outputTokens,
@@ -399,40 +512,65 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
399
512
  }
400
513
  case "agentistics_sessions": {
401
514
  const limit = Math.min(args?.limit ?? 20, 50);
515
+ const harness = args?.harness;
402
516
  const data = await apiGet("/api/data");
403
- const rows = (data.sessions ?? []).slice(0, limit).map((s) => {
404
- const inp = s.input_tokens ?? 0;
405
- const out = s.output_tokens ?? 0;
406
- const cr = s.cache_read_input_tokens ?? 0;
407
- const cw = s.cache_creation_input_tokens ?? 0;
517
+ const rows = filterSessions(data.sessions ?? [], harness).slice(0, limit).map((s) => {
518
+ const { input, output, cacheRead, cacheWrite, cost } = sessionTokens(s);
408
519
  return {
409
520
  id: s.session_id,
521
+ harness: sessionHarness(s),
410
522
  project: s.project_path,
411
523
  startedAt: s.start_time,
412
524
  durationMinutes: s.duration_minutes,
413
- messages: (s.user_message_count ?? 0) + (s.assistant_message_count ?? 0),
414
- inputTokens: inp,
415
- outputTokens: out,
416
- cacheReadTokens: cr,
417
- cacheWriteTokens: cw,
418
- totalTokens: inp + out + cr + cw,
419
- estimatedCostUSD: Math.round(calcCostUSD(inp, out, cr, cw, s.model ?? "") * 1e4) / 1e4,
525
+ messages: sessionMessages(s),
526
+ inputTokens: input,
527
+ outputTokens: output,
528
+ cacheReadTokens: cacheRead,
529
+ cacheWriteTokens: cacheWrite,
530
+ totalTokens: input + output + cacheRead + cacheWrite,
531
+ estimatedCostUSD: Math.round(cost * 1e4) / 1e4,
420
532
  model: s.model ?? null
421
533
  };
422
534
  });
423
535
  return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
424
536
  }
425
537
  case "agentistics_costs": {
538
+ const harness = args?.harness;
426
539
  const data = await apiGet("/api/data");
427
- const usage = data.statsCache?.modelUsage ?? {};
428
- const breakdown = Object.entries(usage).map(([model, u]) => ({
540
+ if (!harness || harness === "all" || harness === "claude") {
541
+ const usage = data.statsCache?.modelUsage ?? {};
542
+ const breakdown2 = Object.entries(usage).map(([model, u]) => ({
543
+ model,
544
+ inputTokens: u.inputTokens ?? 0,
545
+ outputTokens: u.outputTokens ?? 0,
546
+ cacheReadTokens: u.cacheReadTokens ?? 0,
547
+ cacheWriteTokens: u.cacheWriteTokens ?? 0,
548
+ totalTokens: u.totalTokens ?? 0,
549
+ estimatedCostUSD: u.costUSD ?? 0
550
+ })).sort((a, b) => b.totalTokens - a.totalTokens);
551
+ return { content: [{ type: "text", text: JSON.stringify(breakdown2, null, 2) }] };
552
+ }
553
+ const byModel = {};
554
+ for (const s of filterSessions(data.sessions ?? [], harness)) {
555
+ const model = s.model || "unknown";
556
+ if (!byModel[model])
557
+ byModel[model] = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, estimatedCostUSD: 0 };
558
+ const agg = byModel[model];
559
+ const { input, output, cacheRead, cacheWrite, cost } = sessionTokens(s);
560
+ agg.inputTokens += input;
561
+ agg.outputTokens += output;
562
+ agg.cacheReadTokens += cacheRead;
563
+ agg.cacheWriteTokens += cacheWrite;
564
+ agg.estimatedCostUSD += cost;
565
+ }
566
+ const breakdown = Object.entries(byModel).map(([model, u]) => ({
429
567
  model,
430
- inputTokens: u.inputTokens ?? 0,
431
- outputTokens: u.outputTokens ?? 0,
432
- cacheReadTokens: u.cacheReadTokens ?? 0,
433
- cacheWriteTokens: u.cacheWriteTokens ?? 0,
434
- totalTokens: u.totalTokens ?? 0,
435
- estimatedCostUSD: u.costUSD ?? 0
568
+ inputTokens: u.inputTokens,
569
+ outputTokens: u.outputTokens,
570
+ cacheReadTokens: u.cacheReadTokens,
571
+ cacheWriteTokens: u.cacheWriteTokens,
572
+ totalTokens: u.inputTokens + u.outputTokens + u.cacheReadTokens + u.cacheWriteTokens,
573
+ estimatedCostUSD: Math.round(u.estimatedCostUSD * 1e4) / 1e4
436
574
  })).sort((a, b) => b.totalTokens - a.totalTokens);
437
575
  return { content: [{ type: "text", text: JSON.stringify(breakdown, null, 2) }] };
438
576
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentistics/mcp",
3
- "version": "1.3.3",
3
+ "version": "1.7.5",
4
4
  "description": "Agentistics MCP server — Claude Code analytics for Claude Desktop and Claude Code",
5
5
  "type": "module",
6
6
  "main": "./dist/agentistics-mcp.js",