Skip to content

Commit bc74447

Browse files
Klowclaude
andcommitted
fix: review cleanups — esc() usage counts in dashboard, garbage-name guard, privacy text includes timestamps, single-process note
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 69ecf31 commit bc74447

2 files changed

Lines changed: 10 additions & 4 deletions

File tree

mcp-server/src/lib/usage.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
* - Counts live in ONE local file: ~/.chaingpt-mcp/usage.json.
99
* - Nothing is ever transmitted anywhere. There is no remote endpoint,
1010
* no analytics SDK, no phone-home. Grep this file to verify.
11-
* - Only tool NAMES and counts are stored — never arguments, addresses,
12-
* amounts, or results.
11+
* - Only tool NAMES, counts and last-called timestamps are stored —
12+
* never arguments, addresses, amounts, or results.
1313
* - Disable entirely with CHAINGPT_USAGE=off.
1414
*
1515
* Write strategy: in-memory increment + debounced flush (500ms, unref'd so
@@ -38,6 +38,9 @@ function enabled(): boolean {
3838
let cache: UsageFile | null = null;
3939
let flushTimer: NodeJS.Timeout | null = null;
4040

41+
// Single-process assumption: the cache is read once and flushes overwrite the
42+
// file. Two server processes sharing one file = last-writer-wins (acceptable
43+
// for best-effort stats; do not build billing on this).
4144
function load(): UsageFile {
4245
if (cache) return cache;
4346
try {
@@ -66,6 +69,9 @@ function scheduleFlush(): void {
6669
/** Record one tool invocation. Synchronous, allocation-light, never throws. */
6770
export function recordToolUse(tool: string): void {
6871
if (!enabled()) return;
72+
// A non-compliant client can send arbitrary tool names; don't let garbage
73+
// grow the file or pollute the dashboard table.
74+
if (!tool || tool.length > 128 || !tool.startsWith('chaingpt_')) return;
6975
try {
7076
const u = load();
7177
u.counts[tool] = (u.counts[tool] ?? 0) + 1;

mcp-server/src/tools/dashboard.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,9 +637,9 @@ function renderHealth(d) {
637637
}).join('');
638638
const dirRows = d.dirs.map(x => \`<div class="row"><div style="flex:1"><code>\${esc(x.path)}</code></div>\${x.exists ? '<span class="tag ok">present</span>' : '<span class="tag miss">missing</span>'}</div>\`).join('');
639639
const usage = d.usage && d.usage.top && d.usage.top.length
640-
? d.usage.top.slice(0, 15).map(u => \`<div class="row"><div style="flex:1"><code>\${esc(u.tool)}</code></div><span class="subtle" style="margin-right:10px">\${esc((u.lastUsed||'').slice(0,16).replace('T',' '))}</span><span class="tag ok">\${u.count}</span></div>\`).join('')
640+
? d.usage.top.slice(0, 15).map(u => \`<div class="row"><div style="flex:1"><code>\${esc(u.tool)}</code></div><span class="subtle" style="margin-right:10px">\${esc((u.lastUsed||'').slice(0,16).replace('T',' '))}</span><span class="tag ok">\${esc(String(u.count))}</span></div>\`).join('')
641641
: '<div class="subtle">No tool calls recorded yet (or CHAINGPT_USAGE=off).</div>';
642-
const usageNote = d.usage ? \`<p class="subtle" style="margin-top:8px">\${d.usage.total} calls since \${esc((d.usage.since||'').slice(0,10))}. Local-only — stored in ~/.chaingpt-mcp/usage.json, never transmitted. Tool names + counts only.</p>\` : '';
642+
const usageNote = d.usage ? \`<p class="subtle" style="margin-top:8px">\${esc(String(d.usage.total))} calls since \${esc((d.usage.since||'').slice(0,10))}. Local-only — stored in ~/.chaingpt-mcp/usage.json, never transmitted. Tool names, counts + last-called timestamps only.</p>\` : '';
643643
return \`
644644
<div class="card"><h3>Node runtime</h3><p>\${esc(d.node)}</p></div>
645645
<div class="card" style="margin-top:12px"><h3>Tool usage (top 15, local-only)</h3>\${usage}\${usageNote}</div>

0 commit comments

Comments
 (0)