@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 +7 -5
- package/dist/agentistics-mcp.js +209 -71
- package/package.json +1 -1
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
|
|
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
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
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
|
|
package/dist/agentistics-mcp.js
CHANGED
|
@@ -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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
|
333
|
-
const
|
|
334
|
-
const
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
agg.
|
|
371
|
-
agg.
|
|
372
|
-
agg.
|
|
373
|
-
agg.
|
|
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
|
|
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
|
|
414
|
-
inputTokens:
|
|
415
|
-
outputTokens:
|
|
416
|
-
cacheReadTokens:
|
|
417
|
-
cacheWriteTokens:
|
|
418
|
-
totalTokens:
|
|
419
|
-
estimatedCostUSD: Math.round(
|
|
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
|
-
|
|
428
|
-
|
|
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
|
|
431
|
-
outputTokens: u.outputTokens
|
|
432
|
-
cacheReadTokens: u.cacheReadTokens
|
|
433
|
-
cacheWriteTokens: u.cacheWriteTokens
|
|
434
|
-
totalTokens: u.
|
|
435
|
-
estimatedCostUSD: u.
|
|
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
|
}
|