kibi-mcp 2.1.0 → 2.2.0

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.
@@ -10,13 +10,32 @@
10
10
  import { createHash } from "node:crypto";
11
11
  import fs from "node:fs";
12
12
  import path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { appendPayloadCountField, normalizeResultPayload } from "kibi-runtime";
13
15
  import { resolveWorkspaceRoot } from "./workspace.js";
14
16
  export { buildDiagnosticToolCall, classifyDiagnosticError, deriveDiagnosticHints, deriveDiagnosticRetryKey, extractToolCallPayload, redactDiagnosticArgs, } from "./diagnostics-helpers.js";
15
17
  const DIAGNOSTIC_MODE_FLAG = "--diagnostic-mode";
16
- export const DIAGNOSTIC_MODE_ENABLED = process.argv.includes(DIAGNOSTIC_MODE_FLAG);
18
+ const DIAGNOSTIC_MODE_ENV_KEY = "KIBI_DIAGNOSTIC_MODE";
19
+ /**
20
+ * Usage telemetry is opt-in and stays off unless the operator asks for it.
21
+ *
22
+ * The launch flag covers hosts whose MCP command line the operator writes by
23
+ * hand. `KIBI_DIAGNOSTIC_MODE=1` covers hosts where a plugin owns the command
24
+ * line, so opting in never requires editing a shipped launcher. Neither
25
+ * signal is set by default, and plugin installation alone never enables it.
26
+ */
27
+ export function diagnosticModeRequested(argv = process.argv, env = process.env) {
28
+ if (argv.includes(DIAGNOSTIC_MODE_FLAG))
29
+ return true;
30
+ const optIn = env[DIAGNOSTIC_MODE_ENV_KEY]?.trim().toLowerCase();
31
+ return optIn === "1" || optIn === "true";
32
+ }
33
+ export const DIAGNOSTIC_MODE_ENABLED = diagnosticModeRequested();
17
34
  let diagnosticUsageLogPath = null;
35
+ let diagnosticOriginFields = {};
18
36
  export function initializeDiagnosticMode(enabled = DIAGNOSTIC_MODE_ENABLED) {
19
37
  diagnosticUsageLogPath = null;
38
+ diagnosticOriginFields = {};
20
39
  if (!enabled) {
21
40
  process.env.KIBI_MCP_DIAGNOSTIC_MODE = "0";
22
41
  return;
@@ -30,13 +49,50 @@ export function initializeDiagnosticMode(enabled = DIAGNOSTIC_MODE_ENABLED) {
30
49
  diagnosticUsageLogPath =
31
50
  process.env.KIBI_MCP_DIAGNOSTIC_USAGE_LOG_PATH ??
32
51
  path.join(workspaceRoot, ".kb", "usage.log");
52
+ diagnosticOriginFields = resolveOriginFields(workspaceRoot);
33
53
  process.env.KIBI_MCP_DIAGNOSTIC_MODE = "1";
34
54
  }
55
+ /**
56
+ * Identify which host, package build, and checkout produced a usage row.
57
+ *
58
+ * Without these a multi-worktree or multi-host setup cannot tell whether a
59
+ * behavioral difference came from the agent, the editor integration, or the
60
+ * installed Kibi version, because nothing else in a row names its origin.
61
+ */
62
+ function resolveOriginFields(workspaceRoot) {
63
+ return {
64
+ interface: "mcp",
65
+ host: process.env.KIBI_MCP_HOST?.trim() || "unknown",
66
+ package_version: readMcpPackageVersion(),
67
+ workspace_root: workspaceRoot,
68
+ };
69
+ }
70
+ function readMcpPackageVersion() {
71
+ let current = path.dirname(fileURLToPath(import.meta.url));
72
+ while (true) {
73
+ const candidate = path.join(current, "package.json");
74
+ if (fs.existsSync(candidate)) {
75
+ try {
76
+ const parsed = JSON.parse(fs.readFileSync(candidate, "utf8"));
77
+ const version = isRecord(parsed) ? parsed.version : undefined;
78
+ return typeof version === "string" ? version : null;
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ const parent = path.dirname(current);
85
+ if (parent === current)
86
+ return null;
87
+ current = parent;
88
+ }
89
+ }
35
90
  export function appendUsageLogLine(entry) {
36
91
  if (!diagnosticUsageLogPath)
37
92
  return;
38
93
  fs.mkdirSync(path.dirname(diagnosticUsageLogPath), { recursive: true });
39
- fs.appendFileSync(diagnosticUsageLogPath, `${JSON.stringify(entry)}\n`, {
94
+ const row = { ...diagnosticOriginFields, ...entry };
95
+ fs.appendFileSync(diagnosticUsageLogPath, `${JSON.stringify(row)}\n`, {
40
96
  encoding: "utf8",
41
97
  });
42
98
  }
@@ -77,12 +133,6 @@ export const DIAGNOSTIC_TELEMETRY_SCHEMA = {
77
133
  function isRecord(value) {
78
134
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
79
135
  }
80
- function structuredContentFrom(result) {
81
- if (!isRecord(result))
82
- return undefined;
83
- const structuredContent = result.structuredContent;
84
- return isRecord(structuredContent) ? structuredContent : undefined;
85
- }
86
136
  function stringArray(value) {
87
137
  if (!Array.isArray(value))
88
138
  return [];
@@ -276,11 +326,8 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
276
326
  fields.actor_id = telemetry.actor_id;
277
327
  }
278
328
  }
279
- const envelope = structuredContentFrom(result);
280
- const structuredContent = envelope?.kibiProtocol === 1 && isRecord(envelope.data)
281
- ? envelope.data
282
- : envelope;
283
- if (envelope?.kibiProtocol === 1) {
329
+ const { envelope, data: structuredContent } = normalizeResultPayload(result);
330
+ if (envelope) {
284
331
  fields.protocol_version = envelope.kibiProtocol;
285
332
  fields.result_version = envelope.resultVersion ?? null;
286
333
  fields.result_status = envelope.status ?? null;
@@ -296,18 +343,14 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
296
343
  fields.unsafe_original_retry = telemetry?.unsafe_original_retry === true;
297
344
  }
298
345
  if (toolName === "kb_query" || toolName === "kb_search") {
299
- const resultCount = Number(structuredContent?.count ?? 0);
300
- fields.result_count = resultCount;
301
- fields.zero_results = resultCount === 0;
302
- fields.result_summary =
303
- resultCount === 0 ? "0 results" : `${resultCount} results`;
346
+ appendPayloadCountField(fields, "result_count", "results", structuredContent);
347
+ if (typeof fields.result_count === "number") {
348
+ fields.zero_results = fields.result_count === 0;
349
+ }
304
350
  }
305
351
  if (toolName === "kb_check") {
306
- const violationCount = Number(structuredContent?.count ?? 0);
307
- fields.violation_count = violationCount;
352
+ appendPayloadCountField(fields, "violation_count", "violations", structuredContent);
308
353
  fields.requested_rules = Array.isArray(args.rules) ? args.rules : [];
309
- fields.result_summary =
310
- violationCount === 0 ? "0 violations" : `${violationCount} violations`;
311
354
  }
312
355
  if (toolName === "kb_coverage" && structuredContent) {
313
356
  appendCoverageFields(fields, args, structuredContent);
@@ -199,7 +199,7 @@ function registerDocResources() {
199
199
  '2. `kb_coverage` with `{ "by": "req", "includePassing": false }` to review proof rows and `repairPlan`; require `repairPlan.scope.complete` before treating it as a project-wide inventory',
200
200
  "3. Apply only a `repairPlan` batch whose state is `ready`; batches are non-auto-applicable guidance, so query current endpoints, validate every write, and keep `kb_upsert` sequential",
201
201
  "4. Re-run `kb_coverage` after each batch instead of continuing from a stale plan",
202
- '5. `kb_graph` with `{ "seedIds": ["REQ-001"], "direction": "both", "depth": 2 }` to inspect neighboring entities',
202
+ '5. `kb_graph` with `{ "seedIds": ["REQ-cli-gc"], "direction": "both", "depth": 2 }` to inspect neighboring entities',
203
203
  "",
204
204
  "## Add a requirement and link it to a test",
205
205
  '1. `kb_query` with `{ "type": "test" }` to check for existing test IDs',
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "kibi-mcp",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
+ "mcpName": "io.github.Looted/kibi-mcp",
4
5
  "dependencies": {
5
6
  "@modelcontextprotocol/sdk": "^1.30.0",
6
7
  "ajv": "^8.18.0",
@@ -9,8 +10,8 @@
9
10
  "fast-glob": "^3.2.12",
10
11
  "gray-matter": "^4.0.3",
11
12
  "js-yaml": "^4.1.0",
12
- "kibi-runtime": "^2.0.1",
13
- "kibi-core": "^0.13.0",
13
+ "kibi-runtime": "^2.0.2",
14
+ "kibi-core": "^0.14.0",
14
15
  "mcpcat": "^0.1.12",
15
16
  "zod": "^4.3.6"
16
17
  },