@toolbaux/guardian 0.1.9 → 0.1.11
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/dist/commands/mcp-serve.js +90 -1
- package/package.json +1 -1
|
@@ -17,6 +17,65 @@
|
|
|
17
17
|
import fs from "node:fs/promises";
|
|
18
18
|
import path from "node:path";
|
|
19
19
|
import readline from "node:readline";
|
|
20
|
+
const metrics = {
|
|
21
|
+
session_start: Date.now(),
|
|
22
|
+
calls: [],
|
|
23
|
+
intel_reloads: 0,
|
|
24
|
+
cache_hits: 0,
|
|
25
|
+
record(tool, args, responseText, cacheHit) {
|
|
26
|
+
const chars = responseText.length;
|
|
27
|
+
const estimatedTokens = Math.ceil(chars / 3.5); // rough token estimate
|
|
28
|
+
this.calls.push({
|
|
29
|
+
tool,
|
|
30
|
+
args,
|
|
31
|
+
timestamp: Date.now(),
|
|
32
|
+
response_chars: chars,
|
|
33
|
+
estimated_tokens: estimatedTokens,
|
|
34
|
+
cache_hit: cacheHit,
|
|
35
|
+
});
|
|
36
|
+
if (cacheHit)
|
|
37
|
+
this.cache_hits++;
|
|
38
|
+
},
|
|
39
|
+
summary() {
|
|
40
|
+
const duration = Math.round((Date.now() - this.session_start) / 1000);
|
|
41
|
+
const totalCalls = this.calls.length;
|
|
42
|
+
const totalTokensSpent = this.calls.reduce((s, c) => s + c.estimated_tokens, 0);
|
|
43
|
+
// Estimate tokens saved: each guardian call replaces ~3 Read/Grep calls (~400 tokens each)
|
|
44
|
+
const estimatedTokensSaved = totalCalls * 400 - totalTokensSpent;
|
|
45
|
+
const toolBreakdown = {};
|
|
46
|
+
for (const c of this.calls) {
|
|
47
|
+
if (!toolBreakdown[c.tool])
|
|
48
|
+
toolBreakdown[c.tool] = { calls: 0, tokens: 0 };
|
|
49
|
+
toolBreakdown[c.tool].calls++;
|
|
50
|
+
toolBreakdown[c.tool].tokens += c.estimated_tokens;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
session_duration_seconds: duration,
|
|
54
|
+
total_mcp_calls: totalCalls,
|
|
55
|
+
total_tokens_spent: totalTokensSpent,
|
|
56
|
+
estimated_tokens_saved: Math.max(0, estimatedTokensSaved),
|
|
57
|
+
savings_ratio: totalCalls > 0
|
|
58
|
+
? `${Math.round((estimatedTokensSaved / (totalCalls * 400)) * 100)}%`
|
|
59
|
+
: "n/a",
|
|
60
|
+
cache_hits: this.cache_hits,
|
|
61
|
+
intel_reloads: this.intel_reloads,
|
|
62
|
+
tool_breakdown: toolBreakdown,
|
|
63
|
+
avg_tokens_per_call: totalCalls > 0 ? Math.round(totalTokensSpent / totalCalls) : 0,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
// ── Response cache (dedup repeated queries) ──
|
|
68
|
+
const responseCache = new Map();
|
|
69
|
+
const CACHE_TTL = 30_000; // 30s cache
|
|
70
|
+
function getCached(key) {
|
|
71
|
+
const entry = responseCache.get(key);
|
|
72
|
+
if (entry && Date.now() - entry.time < CACHE_TTL)
|
|
73
|
+
return entry.text;
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function setCache(key, text) {
|
|
77
|
+
responseCache.set(key, { text, time: Date.now() });
|
|
78
|
+
}
|
|
20
79
|
// ── Intelligence loader ──
|
|
21
80
|
let intel = null;
|
|
22
81
|
let intelPath = "";
|
|
@@ -225,6 +284,14 @@ const TOOLS = [
|
|
|
225
284
|
properties: {},
|
|
226
285
|
},
|
|
227
286
|
},
|
|
287
|
+
{
|
|
288
|
+
name: "guardian_metrics",
|
|
289
|
+
description: "Get MCP usage metrics for this session: calls made, tokens spent, tokens saved, cache hits. Call at end of session to evaluate guardian's usefulness.",
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: {},
|
|
293
|
+
},
|
|
294
|
+
},
|
|
228
295
|
];
|
|
229
296
|
const TOOL_HANDLERS = {
|
|
230
297
|
guardian_file_context: fileContext,
|
|
@@ -232,6 +299,7 @@ const TOOL_HANDLERS = {
|
|
|
232
299
|
guardian_endpoint_trace: endpointTrace,
|
|
233
300
|
guardian_impact_check: impactCheck,
|
|
234
301
|
guardian_overview: overview,
|
|
302
|
+
guardian_metrics: async () => JSON.stringify(metrics.summary(), null, 2),
|
|
235
303
|
};
|
|
236
304
|
function respond(id, result) {
|
|
237
305
|
const msg = JSON.stringify({ jsonrpc: "2.0", id, result });
|
|
@@ -268,7 +336,17 @@ async function handleRequest(req) {
|
|
|
268
336
|
break;
|
|
269
337
|
}
|
|
270
338
|
try {
|
|
339
|
+
// Check cache first
|
|
340
|
+
const cacheKey = `${toolName}:${JSON.stringify(toolArgs)}`;
|
|
341
|
+
const cached = getCached(cacheKey);
|
|
342
|
+
if (cached) {
|
|
343
|
+
metrics.record(toolName, toolArgs, cached, true);
|
|
344
|
+
respond(req.id, { content: [{ type: "text", text: cached }] });
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
271
347
|
const result = await handler(toolArgs);
|
|
348
|
+
setCache(cacheKey, result);
|
|
349
|
+
metrics.record(toolName, toolArgs, result, false);
|
|
272
350
|
respond(req.id, {
|
|
273
351
|
content: [{ type: "text", text: result }],
|
|
274
352
|
});
|
|
@@ -307,7 +385,18 @@ export async function runMcpServe(options) {
|
|
|
307
385
|
respondError(null, -32700, `Parse error: ${err.message}`);
|
|
308
386
|
}
|
|
309
387
|
});
|
|
310
|
-
rl.on("close", () => {
|
|
388
|
+
rl.on("close", async () => {
|
|
389
|
+
// Persist session metrics to .specs/machine/mcp-metrics.jsonl
|
|
390
|
+
const metricsPath = path.join(specsDir, "machine", "mcp-metrics.jsonl");
|
|
391
|
+
try {
|
|
392
|
+
const entry = JSON.stringify({
|
|
393
|
+
...metrics.summary(),
|
|
394
|
+
session_end: new Date().toISOString(),
|
|
395
|
+
});
|
|
396
|
+
await fs.appendFile(metricsPath, entry + "\n", "utf8");
|
|
397
|
+
process.stderr.write(`Guardian metrics saved to ${metricsPath}\n`);
|
|
398
|
+
}
|
|
399
|
+
catch { }
|
|
311
400
|
process.stderr.write("Guardian MCP server stopped.\n");
|
|
312
401
|
process.exit(0);
|
|
313
402
|
});
|
package/package.json
CHANGED