zopkit-bug-resolution-mcp 1.8.0 → 1.9.1
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/bin/zopkit-statusline.js +8 -1
- package/index.js +21 -11
- package/lib/zopkit-config.js +66 -1
- package/package.json +1 -1
package/bin/zopkit-statusline.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
36
36
|
import { join } from 'node:path';
|
|
37
37
|
import { homedir, tmpdir } from 'node:os';
|
|
38
|
-
import { loadConfig, post, readStdinJson } from '../lib/zopkit-config.js';
|
|
38
|
+
import { loadConfig, post, readStdinJson, readClaudeAccount, reporterVersion } from '../lib/zopkit-config.js';
|
|
39
39
|
|
|
40
40
|
// Per-session throttle. The status line fires on every assistant message; a
|
|
41
41
|
// 40-turn session would otherwise be 40 POSTs. 15s keeps the panel's 10s
|
|
@@ -94,6 +94,9 @@ function buildHeartbeat(p, cfg, fresh) {
|
|
|
94
94
|
sessionName: cfg.shareSessionName ? (p.session_name ?? null) : null,
|
|
95
95
|
model: p.model?.id ?? null,
|
|
96
96
|
version: p.version ?? null,
|
|
97
|
+
// This reporter's version, so the server can tell "old client" apart
|
|
98
|
+
// from "nothing to report" — they produce identical rows otherwise.
|
|
99
|
+
reporterVersion: reporterVersion(),
|
|
97
100
|
|
|
98
101
|
contextTokens: p.context_window?.total_input_tokens ?? null,
|
|
99
102
|
contextLimit: p.context_window?.context_window_size ?? null,
|
|
@@ -112,6 +115,10 @@ function buildHeartbeat(p, cfg, fresh) {
|
|
|
112
115
|
|
|
113
116
|
rateLimits: p.rate_limits ?? null,
|
|
114
117
|
|
|
118
|
+
// Which Anthropic account this session is signed in to. Read from
|
|
119
|
+
// ~/.claude.json rather than the payload, which does not carry it.
|
|
120
|
+
account: readClaudeAccount(),
|
|
121
|
+
|
|
115
122
|
// Whether a NEW API response landed since the last heartbeat for this
|
|
116
123
|
// session, detected by the context token count moving. Only a fresh
|
|
117
124
|
// reading may advance the account-level percentages: an idle session's
|
package/index.js
CHANGED
|
@@ -91,7 +91,7 @@ if (!API_KEY) {
|
|
|
91
91
|
'Generate one from the company-admin panel (Bug Reports → API Keys) and set it when registering this server.');
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
const server = new McpServer({ name: 'zopkit-bug-resolution', version: '1.
|
|
94
|
+
const server = new McpServer({ name: 'zopkit-bug-resolution', version: '1.9.1' });
|
|
95
95
|
|
|
96
96
|
const BUG_SEVERITIES = ['low', 'medium', 'high', 'critical'];
|
|
97
97
|
// Keep in sync with BUG_STATUSES in backend/src/db/schema/platform/bug-reports.ts.
|
|
@@ -139,18 +139,28 @@ const SUBAGENT_TOOL_NAMES = new Set(['Agent', 'Task']);
|
|
|
139
139
|
// weeklySessionsWithWebSearch; missing this half undercounted it before.
|
|
140
140
|
const WEB_TOOL_NAMES = new Set(['WebSearch', 'WebFetch']);
|
|
141
141
|
|
|
142
|
-
// Context-window ceiling per model
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
142
|
+
// Context-window ceiling per model, used only to turn currentContextTokens
|
|
143
|
+
// into a percentage. A GUESS, and unavoidably so: the transcript records no
|
|
144
|
+
// window size (only context_management), so unlike the status-line path —
|
|
145
|
+
// which is handed context_window.context_window_size by Claude Code itself
|
|
146
|
+
// — this has to infer it.
|
|
147
|
+
//
|
|
148
|
+
// The previous rule was "sonnet => 1M, everything else => 200k", which put
|
|
149
|
+
// every Opus 5 session at a fifth of its real ceiling: a session the status
|
|
150
|
+
// line measured at 626k/1.0M (63%) showed here as 626k/200k, pinned red at
|
|
151
|
+
// 100%. The 5-series ships the 1M window; 4.x is 200k.
|
|
152
|
+
//
|
|
153
|
+
// Note the version is parsed positionally, not by substring: the family
|
|
154
|
+
// version is the segment right after the name, so claude-haiku-4-5-20251001
|
|
155
|
+
// is a 4-series despite containing "-5-".
|
|
150
156
|
function contextLimitForModel(model) {
|
|
151
157
|
if (!model) return 200_000;
|
|
152
|
-
|
|
153
|
-
|
|
158
|
+
const [, version] = model.replace(/^claude-/, '').split('-');
|
|
159
|
+
const major = Number.parseInt(version, 10);
|
|
160
|
+
// Unrecognised => 200k. Undercounting the ceiling shows a session as
|
|
161
|
+
// fuller than it is, which is visibly wrong and gets reported; guessing
|
|
162
|
+
// high would quietly hide a session that is genuinely about to run out.
|
|
163
|
+
return Number.isFinite(major) && major >= 5 ? 1_000_000 : 200_000;
|
|
154
164
|
}
|
|
155
165
|
|
|
156
166
|
/**
|
package/lib/zopkit-config.js
CHANGED
|
@@ -16,11 +16,76 @@
|
|
|
16
16
|
// Create it with: npx zopkit-bug-resolution-mcp --init-usage-reporting
|
|
17
17
|
|
|
18
18
|
import { readFileSync } from 'node:fs';
|
|
19
|
-
import { join } from 'node:path';
|
|
19
|
+
import { join, dirname } from 'node:path';
|
|
20
20
|
import { homedir } from 'node:os';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
21
22
|
|
|
22
23
|
export const CONFIG_PATH = join(homedir(), '.claude', '.zopkit-usage.json');
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* This reporter's own version, sent with every heartbeat.
|
|
27
|
+
*
|
|
28
|
+
* The server cannot otherwise tell an OLD reporter apart from a machine
|
|
29
|
+
* with nothing to report: a pre-1.9.0 client sends perfect session rows
|
|
30
|
+
* and silently omits the account, which looks identical to a machine that
|
|
31
|
+
* has no signed-in Claude account. Both show up as "not reporting" and
|
|
32
|
+
* need completely different fixes.
|
|
33
|
+
*
|
|
34
|
+
* Read from package.json rather than hardcoded, so it cannot drift from
|
|
35
|
+
* the published version the way a duplicated constant would.
|
|
36
|
+
*/
|
|
37
|
+
let versionCache;
|
|
38
|
+
export function reporterVersion() {
|
|
39
|
+
if (versionCache !== undefined) return versionCache;
|
|
40
|
+
try {
|
|
41
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
42
|
+
versionCache = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8')).version ?? null;
|
|
43
|
+
} catch {
|
|
44
|
+
versionCache = null;
|
|
45
|
+
}
|
|
46
|
+
return versionCache;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Which Anthropic account this machine is signed in to.
|
|
51
|
+
*
|
|
52
|
+
* Read from ~/.claude.json's oauthAccount block — the same file Claude
|
|
53
|
+
* Code keeps its own settings in. Nothing here is a credential: the uuid
|
|
54
|
+
* is an identifier, and the email/org are what the account calls itself.
|
|
55
|
+
*
|
|
56
|
+
* It matters because RATE LIMITS ARE PER ACCOUNT. The wrapper staff member
|
|
57
|
+
* reading the panel and the Claude account being billed need not be the
|
|
58
|
+
* same identity (on the machine this was written, they are not), and if
|
|
59
|
+
* two developers share one account then "this developer is at 70%" is the
|
|
60
|
+
* wrong sentence — the account is, and the token split says who drove it.
|
|
61
|
+
*
|
|
62
|
+
* Cached for the process: a status line runs on every assistant message
|
|
63
|
+
* and this file is ~MBs, so re-reading it per render would be the most
|
|
64
|
+
* expensive thing the reporter does.
|
|
65
|
+
*/
|
|
66
|
+
let accountCache;
|
|
67
|
+
export function readClaudeAccount() {
|
|
68
|
+
if (accountCache !== undefined) return accountCache;
|
|
69
|
+
try {
|
|
70
|
+
const raw = JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf8'));
|
|
71
|
+
const a = raw?.oauthAccount;
|
|
72
|
+
accountCache = a?.accountUuid
|
|
73
|
+
? {
|
|
74
|
+
uuid: a.accountUuid,
|
|
75
|
+
email: a.emailAddress ?? null,
|
|
76
|
+
orgName: a.organizationName ?? null,
|
|
77
|
+
// The rate-limit TIER, not the seat: it is what explains why two
|
|
78
|
+
// accounts have different ceilings, so a high percentage on a
|
|
79
|
+
// small plan is not read as the same signal as one on a 20x.
|
|
80
|
+
plan: a.organizationRateLimitTier ?? a.organizationType ?? null,
|
|
81
|
+
}
|
|
82
|
+
: null;
|
|
83
|
+
} catch {
|
|
84
|
+
accountCache = null; // never signed in, or an unreadable file
|
|
85
|
+
}
|
|
86
|
+
return accountCache;
|
|
87
|
+
}
|
|
88
|
+
|
|
24
89
|
let cached;
|
|
25
90
|
|
|
26
91
|
export function loadConfig() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zopkit-bug-resolution-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server + status-line/hook reporters for Zopkit wrapper bug reports: fetch full bug context (with screenshots), submit how a bug was fixed, list bugs assigned to you, file new bug reports, and report REAL Claude Code usage \u2014 live per-session context and account rate limits via the status line, plus per-bug token attribution via PostToolUse hooks. Requires a BUG_REPORT_API_KEY (generated in the company-admin panel) \u2014 see README.md.",
|
|
6
6
|
"license": "UNLICENSED",
|