kibi-mcp 0.15.1 → 0.15.3

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.
@@ -89,6 +89,57 @@ export const DIAGNOSTIC_TELEMETRY_SCHEMA = {
89
89
  },
90
90
  };
91
91
  // implements REQ-002
92
+ export function classifyDiagnosticError(error) {
93
+ const err = error instanceof Error ? error : new Error(String(error));
94
+ const message = err.message;
95
+ const lower = message.toLowerCase();
96
+ if (lower.includes("stale_snapshot")) {
97
+ return buildErrorFields(err, "stale_snapshot", "persistence", "KB snapshot is stale; refresh/retry after the latest KB state is attached.");
98
+ }
99
+ if (lower.includes("unknown option") && lower.includes("h for help")) {
100
+ return buildErrorFields(err, "prolog_unknown_option", "prolog_runtime", "Prolog rejected startup/module/query options; inspect MCP package wiring and Prolog invocation.");
101
+ }
102
+ if (lower.includes("prolog process not started")) {
103
+ return buildErrorFields(err, "prolog_process_not_started", "prolog_lifecycle", "Prolog process is unavailable; restart the MCP server before retrying.");
104
+ }
105
+ if (lower.includes("resetting prolog worker") ||
106
+ lower.includes("prolog worker reset")) {
107
+ return buildErrorFields(err, "prolog_worker_reset", "prolog_lifecycle", "Prolog worker was reset so the next MCP call can start from a fresh worker.");
108
+ }
109
+ if (/timed out after \d+ms/i.test(message) ||
110
+ lower.includes("tool timeout")) {
111
+ return buildErrorFields(err, "tool_timeout", "tool_timeout", "MCP tool execution exceeded its bounded timeout.");
112
+ }
113
+ if (lower.includes("coarsely while granular symbols are available")) {
114
+ return buildErrorFields(err, "coarse_symbol_linkage", "validation", "Symbol traceability targeted a coarse file/module while narrower exported symbols exist.");
115
+ }
116
+ if (message.startsWith("Entity validation failed:")) {
117
+ return buildErrorFields(err, "entity_validation_failed", "validation", "Entity payload failed schema validation.");
118
+ }
119
+ if (message.startsWith("Relationship validation failed")) {
120
+ return buildErrorFields(err, "relationship_validation_failed", "validation", "Relationship payload failed schema validation.");
121
+ }
122
+ if (message.startsWith("Relationship source must match the upserted entity")) {
123
+ return buildErrorFields(err, "relationship_source_mismatch", "validation", "Relationship source did not match the entity being upserted.");
124
+ }
125
+ if (lower.includes("module load failed")) {
126
+ return buildErrorFields(err, "prolog_module_load_failed", "prolog_runtime", "Prolog failed to load an execution module.");
127
+ }
128
+ if (lower.includes("query failed")) {
129
+ return buildErrorFields(err, "prolog_query_failed", "prolog_runtime", "Prolog query execution failed.");
130
+ }
131
+ return buildErrorFields(err, "handler_error", "handler", "Unhandled MCP handler error.");
132
+ }
133
+ function buildErrorFields(error, category, stage, summary) {
134
+ return {
135
+ error_name: error.name,
136
+ error_message: error.message,
137
+ error_category: category,
138
+ error_stage: stage,
139
+ error_summary: summary,
140
+ };
141
+ }
142
+ // implements REQ-002
92
143
  export function deriveDiagnosticFields(toolName, args, telemetry, result) {
93
144
  const fields = {
94
145
  telemetry_status: telemetry ? "provided" : "missing",
@@ -48,7 +48,7 @@ export const PROMPTS = [
48
48
  text: [
49
49
  "# Kibi Interactive Activation Workflow",
50
50
  "",
51
- "Use this workflow to onboard a new or empty repository into Kibi through interactive discovery.",
51
+ "Use this post-hoc workflow to onboard a new or empty repository into Kibi through interactive discovery.",
52
52
  "",
53
53
  "## Step 1: Gather Declared Context",
54
54
  "",
@@ -63,27 +63,28 @@ export const PROMPTS = [
63
63
  "Call `kb_autopilot_generate` with the gathered context to synthesize candidate entities.",
64
64
  "",
65
65
  "This tool is **read-only**. It returns additive `structuredContent` with:",
66
- "- `promptBlock`: review text that can be surfaced for optional human review",
66
+ "- `promptBlock`: review text that must be surfaced before writes",
67
67
  "- `recommendedActions`: agent-facing next steps, including any REQ/SCEN/TEST authoring routed for manual handling",
68
68
  "- `declaredContext`: the user-provided bootstrap context",
69
69
  "- `confidence`: confidence summary for the generated output",
70
70
  "- `bootstrapMode`: current KB state (e.g., `root_uninitialized`)",
71
71
  "- `candidates`: synthesized entities grounded in declared context and source evidence",
72
+ "- `applyPlan`: exact sequential `kb_upsert` payloads for approved candidates",
72
73
  "- `discoverySummary`: source-backed discovery notes",
73
74
  "",
74
- "## Step 3: Optional Human Review",
75
+ "## Step 3: Preview and Approval",
75
76
  "",
76
- "Surface the `promptBlock` and a summary of `candidates` when optional human review is useful. Human review is post-hoc/optional and must not block writes.",
77
+ "Surface the `promptBlock`, a summary of `candidates`, and the exact `structuredContent.applyPlan` payloads. Wait for explicit user approval before proceeding to writes.",
77
78
  "",
78
79
  "## Step 4: Apply Candidates",
79
80
  "",
80
81
  "Apply candidates sequentially using `kb_upsert`.",
81
- "1. Execute each candidate's `applyPlan` in ascending phase order.",
82
+ "1. Execute `structuredContent.applyPlan` sequentially in listed order.",
82
83
  "2. Confirm success of each `kb_upsert` before moving to the next.",
83
84
  "3. Run `kb_check` after the batch to verify KB integrity.",
84
85
  "",
85
86
  "## Rules",
86
- "- Human review is optional and post-hoc; do not gate writes on synchronous sign-off.",
87
+ "- Never apply bootstrap writes without user-facing preview and explicit approval.",
87
88
  "- `kb_autopilot_generate` is strictly read-only; synthesis is the backend, not the actor.",
88
89
  "- Guidance must stay MCP-only; do not suggest `kibi` CLI commands.",
89
90
  ].join("\n"),
@@ -110,7 +111,7 @@ export const PROMPTS = [
110
111
  "Core modeling principles:",
111
112
  "- Kibi has eight entity types: common authoring (req, scenario, test, fact) and supporting/system (adr, flag, event, symbol).",
112
113
  "- Encode requirements as linked facts: `req --constrains--> fact` plus `req --requires_property--> fact`.",
113
- "- High-confidence modeling (>= 0.7) is fully automated; optional human review can happen post-hoc.",
114
+ "- High-confidence `kb_model_requirement` output is deterministic; `/init-kibi` bootstrap writes still require preview and explicit approval.",
114
115
  "- Low-confidence claims (< 0.7) are downgraded to `observation` facts to prevent false-positive contradictions.",
115
116
  "- Only strict domain facts participate in contradiction inference; observation and meta facts are non-blocking notes.",
116
117
  "- v1 contradictions are limited to exact-value, boolean/enum, numeric range, and polarity conflicts.",
@@ -38,6 +38,7 @@ export let prologProcess = null;
38
38
  let isInitialized = false;
39
39
  export let activeBranchName = "develop";
40
40
  let ensurePrologTail = Promise.resolve();
41
+ let prologResetGeneration = 0;
41
42
  export let isShuttingDown = false;
42
43
  let shutdownTimeout = null;
43
44
  export const inFlightRequests = new Map();
@@ -47,6 +48,7 @@ export function resetSessionStateForTests() {
47
48
  isInitialized = false;
48
49
  activeBranchName = "develop";
49
50
  ensurePrologTail = Promise.resolve();
51
+ prologResetGeneration = 0;
50
52
  isShuttingDown = false;
51
53
  inFlightRequests.clear();
52
54
  if (shutdownTimeout) {
@@ -138,8 +140,38 @@ export async function initiateGracefulShutdown(exitCode = 0) {
138
140
  process.exit(exitCode);
139
141
  }
140
142
  // implements REQ-008
143
+ export async function resetProlog(reason) {
144
+ debugLog(`[KIBI-MCP] Resetting Prolog worker: ${reason}`);
145
+ prologResetGeneration += 1;
146
+ const current = prologProcess;
147
+ prologProcess = null;
148
+ isInitialized = false;
149
+ if (current) {
150
+ try {
151
+ await current.terminate();
152
+ }
153
+ catch (error) {
154
+ console.error("[KIBI-MCP] Error resetting Prolog worker:", error);
155
+ }
156
+ }
157
+ }
158
+ // implements REQ-008
141
159
  async function ensurePrologUnsafe() {
160
+ const generationAtStart = prologResetGeneration;
142
161
  const workspaceRoot = sessionDeps.resolveWorkspaceRoot();
162
+ const assertGeneration = async () => {
163
+ if (generationAtStart !== prologResetGeneration) {
164
+ const current = prologProcess;
165
+ prologProcess = null;
166
+ isInitialized = false;
167
+ if (current) {
168
+ await current.terminate().catch((error) => {
169
+ console.error("[KIBI-MCP] Error terminating stale Prolog after reset generation change:", error);
170
+ });
171
+ }
172
+ throw new Error("Prolog worker reset while initialization was in progress");
173
+ }
174
+ };
143
175
  // Determine target branch: respect KIBI_BRANCH override or resolve from git
144
176
  const envBranch = getBranchOverride();
145
177
  let targetBranch;
@@ -170,10 +202,12 @@ async function ensurePrologUnsafe() {
170
202
  debugLog(`[KIBI-MCP] Branch changed: ${activeBranchName} -> ${targetBranch}`);
171
203
  // Persist and detach from old KB
172
204
  const saveResult = await prologProcess.query("kb_save");
205
+ await assertGeneration();
173
206
  if (!saveResult.success) {
174
207
  throw new Error(`Failed to save old KB before detach: ${saveResult.error || "Unknown error"}`);
175
208
  }
176
209
  const detachResult = await prologProcess.query("kb_detach");
210
+ await assertGeneration();
177
211
  if (!detachResult.success) {
178
212
  debugLog(`[KIBI-MCP] Warning: failed to detach from old KB: ${detachResult.error || "Unknown error"}`);
179
213
  // Continue anyway - we'll try to attach to the new KB
@@ -183,6 +217,7 @@ async function ensurePrologUnsafe() {
183
217
  const newKbPath = sessionDeps.resolveKbPath(workspaceRoot, targetBranch);
184
218
  // Attach to new branch KB
185
219
  const attachResult = await prologProcess.query(`kb_attach('${newKbPath}')`);
220
+ await assertGeneration();
186
221
  if (!attachResult.success) {
187
222
  throw new Error(`Failed to attach to new branch KB: ${attachResult.error || "Unknown error"}`);
188
223
  }
@@ -195,6 +230,7 @@ async function ensurePrologUnsafe() {
195
230
  debugLog("[KIBI-MCP] Initializing Prolog process...");
196
231
  prologProcess = new sessionDeps.PrologProcess({ timeout: 120000 });
197
232
  await prologProcess.start();
233
+ await assertGeneration();
198
234
  // Startup debug: resolve which kibi-cli is being used and its version (best-effort).
199
235
  // Gate all output under KIBI_MCP_DEBUG and write only to stderr via debugLog.
200
236
  if (isMcpDebugEnabled()) {
@@ -235,6 +271,7 @@ async function ensurePrologUnsafe() {
235
271
  ensureBranchKbExists(workspaceRoot, targetBranch);
236
272
  const kbPath = sessionDeps.resolveKbPath(workspaceRoot, targetBranch);
237
273
  const attachResult = await prologProcess.query(`kb_attach('${kbPath}')`);
274
+ await assertGeneration();
238
275
  if (!attachResult.success) {
239
276
  throw new Error(`Failed to attach KB: ${attachResult.error || "Unknown error"}`);
240
277
  }
@@ -1,5 +1,23 @@
1
+ /*
2
+ Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
+ Copyright (C) 2026 Piotr Franczyk
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Affero General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Affero General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Affero General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ import process from "node:process";
1
19
  import { z } from "zod";
2
- import { DIAGNOSTIC_MODE_ENABLED, appendUsageLogLine, deriveDiagnosticFields, extractToolCallPayload, } from "../diagnostics.js";
20
+ import { DIAGNOSTIC_MODE_ENABLED, appendUsageLogLine, classifyDiagnosticError, deriveDiagnosticFields, extractToolCallPayload, } from "../diagnostics.js";
3
21
  import { isMcpDebugEnabled } from "../env.js";
4
22
  import { TOOLS } from "../tools-config.js";
5
23
  import { handleKbAutopilotGenerate, } from "../tools/autopilot-generate.js";
@@ -14,6 +32,8 @@ import { handleKbSearch } from "../tools/search.js";
14
32
  import { handleKbSkillsList, handleKbSkillsLoad, handleKbSkillsRead, } from "../tools/skills.js";
15
33
  import { handleKbStatus } from "../tools/status.js";
16
34
  import { handleKbUpsert } from "../tools/upsert.js";
35
+ const DEFAULT_TOOL_TIMEOUT_MS = 90_000;
36
+ const TOOL_TIMEOUT_ENV = "KIBI_MCP_TOOL_TIMEOUT_MS";
17
37
  const defaultToolsServerDeps = {
18
38
  getSessionModule: () => import("./session.js"),
19
39
  };
@@ -40,6 +60,7 @@ async function getSessionModule() {
40
60
  const DEFAULT_TOOLS_RUNTIME = {
41
61
  diagnosticModeEnabled: () => DIAGNOSTIC_MODE_ENABLED,
42
62
  appendUsageLogLine,
63
+ classifyDiagnosticError,
43
64
  deriveDiagnosticFields,
44
65
  extractToolCallPayload,
45
66
  // INTENTIONAL: TOOLS is imported as a Zod-inferred schema type; ToolConfig is the
@@ -48,6 +69,7 @@ const DEFAULT_TOOLS_RUNTIME = {
48
69
  tools: TOOLS,
49
70
  activeBranchName: async () => (await getSessionModule()).activeBranchName,
50
71
  ensureProlog: async () => (await getSessionModule()).ensureProlog(),
72
+ resetProlog: async (reason) => (await getSessionModule()).resetProlog(reason),
51
73
  inFlightRequests: async () => (await getSessionModule()).inFlightRequests,
52
74
  isShuttingDown: async () => (await getSessionModule()).isShuttingDown,
53
75
  prologProcess: async () => (await getSessionModule()).prologProcess,
@@ -73,6 +95,43 @@ function debugLog(...args) {
73
95
  }
74
96
  }
75
97
  // implements REQ-002
98
+ function getToolTimeoutMs() {
99
+ const raw = process.env[TOOL_TIMEOUT_ENV]?.trim();
100
+ if (!raw) {
101
+ return DEFAULT_TOOL_TIMEOUT_MS;
102
+ }
103
+ const parsed = Number(raw);
104
+ return Number.isSafeInteger(parsed) && parsed > 0
105
+ ? parsed
106
+ : DEFAULT_TOOL_TIMEOUT_MS;
107
+ }
108
+ // implements REQ-002
109
+ function createToolTimeoutError(toolName, timeoutMs) {
110
+ return new Error(`Tool ${toolName} timed out after ${timeoutMs}ms`);
111
+ }
112
+ // implements REQ-002
113
+ async function withToolTimeout(toolName, operation, onTimeout) {
114
+ const timeoutMs = getToolTimeoutMs();
115
+ let timeout;
116
+ try {
117
+ return await Promise.race([
118
+ operation,
119
+ new Promise((_, reject) => {
120
+ timeout = setTimeout(() => {
121
+ const error = createToolTimeoutError(toolName, timeoutMs);
122
+ reject(error);
123
+ void onTimeout(error, timeoutMs);
124
+ }, timeoutMs);
125
+ }),
126
+ ]);
127
+ }
128
+ finally {
129
+ if (timeout) {
130
+ clearTimeout(timeout);
131
+ }
132
+ }
133
+ }
134
+ // implements REQ-002
76
135
  export function jsonSchemaToZod(schema) {
77
136
  if (!schema || typeof schema !== "object") {
78
137
  return z.any();
@@ -209,9 +268,21 @@ runtime = DEFAULT_TOOLS_RUNTIME) {
209
268
  const trackedRequests = await runtime.inFlightRequests();
210
269
  const handlerPromise = handler(businessArgs);
211
270
  trackedRequests.set(requestId, handlerPromise);
271
+ let resetAttempted = false;
272
+ let resetSucceeded = false;
273
+ let resetError = null;
212
274
  try {
213
275
  // Execute handler
214
- const result = await handlerPromise;
276
+ const result = await withToolTimeout(name, handlerPromise, async () => {
277
+ resetAttempted = true;
278
+ try {
279
+ await runtime.resetProlog(`tool timeout: ${name}`);
280
+ resetSucceeded = true;
281
+ }
282
+ catch (error) {
283
+ resetError = error instanceof Error ? error.message : String(error);
284
+ }
285
+ });
215
286
  // Log usage in diagnostic mode
216
287
  if (diagnosticModeEnabled) {
217
288
  const finishedAt = new Date();
@@ -240,6 +311,7 @@ runtime = DEFAULT_TOOLS_RUNTIME) {
240
311
  if (diagnosticModeEnabled) {
241
312
  const finishedAt = new Date();
242
313
  const err = error instanceof Error ? error : new Error(String(error));
314
+ const diagnosticErrorFields = runtime.classifyDiagnosticError(err);
243
315
  const processHandle = await runtime.prologProcess();
244
316
  const branchName = await runtime.activeBranchName();
245
317
  runtime.appendUsageLogLine({
@@ -254,7 +326,10 @@ runtime = DEFAULT_TOOLS_RUNTIME) {
254
326
  duration_ms: finishedAt.getTime() - startedAt.getTime(),
255
327
  prolog_pid: processHandle?.getPid() ?? null,
256
328
  active_branch: branchName,
257
- error_message: err.message,
329
+ reset_attempted: resetAttempted,
330
+ reset_succeeded: resetSucceeded,
331
+ reset_error: resetError,
332
+ ...diagnosticErrorFields,
258
333
  });
259
334
  }
260
335
  throw error;
@@ -60,6 +60,13 @@ function countCandidatesByType(candidateRecords) {
60
60
  }
61
61
  return counts;
62
62
  }
63
+ function buildApplyPlan(candidateRecords) {
64
+ return candidateRecords.flatMap((candidate) => {
65
+ if (!Array.isArray(candidate.applyPlan))
66
+ return [];
67
+ return candidate.applyPlan.filter((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry));
68
+ });
69
+ }
63
70
  function formatCandidateTypeCounts(candidateRecords) {
64
71
  const counts = countCandidatesByType(candidateRecords);
65
72
  return Object.keys(counts)
@@ -573,6 +580,7 @@ _prolog, args) {
573
580
  // best-effort only; ignore failures here so generation can continue
574
581
  }
575
582
  const candidateRecords = Array.from(seenByKey.values());
583
+ const applyPlan = buildApplyPlan(candidateRecords);
576
584
  const payoffSummary = buildPayoffSummary(candidateRecords);
577
585
  const promptBlock = buildPromptBlock(workspaceRoot, activationState, activation.activationMode, activation.reason, activation.applyBlocked, declaredContext, candidateRecords, sourceOnlySignals, activationDiscovery.summary.scanWarnings);
578
586
  const confidence = buildConfidence(activation.activationMode, activation.applyBlocked, declaredContext, candidateRecords, sourceOnlySignals, promptBlock);
@@ -585,6 +593,9 @@ _prolog, args) {
585
593
  const effectiveTldr = confidence.level === "low" && !activation.applyBlocked
586
594
  ? `Low-confidence bootstrap (${confidence.score}): review diagnostics before proceeding. ${tldr}`
587
595
  : tldr;
596
+ const effectiveText = applyPlan.length > 0
597
+ ? `${effectiveTldr} Review structuredContent.applyPlan for exact sequential kb_upsert payloads before requesting approval.`
598
+ : effectiveTldr;
588
599
  const structuredContent = {
589
600
  activationState,
590
601
  activationMode: activation.activationMode,
@@ -602,6 +613,7 @@ _prolog, args) {
602
613
  declaredContext,
603
614
  discoverySummary: activationDiscovery.summary,
604
615
  candidates: candidateRecords,
616
+ applyPlan,
605
617
  suppressedCandidates: suppressed,
606
618
  payoffSummary,
607
619
  };
@@ -609,12 +621,13 @@ _prolog, args) {
609
621
  content: [
610
622
  {
611
623
  type: "text",
612
- text: effectiveTldr,
624
+ text: effectiveText,
613
625
  },
614
626
  ],
615
627
  structuredContent,
616
628
  migrationWarning,
617
629
  candidates: candidateRecords,
630
+ applyPlan,
618
631
  suppressedCandidates: suppressed,
619
632
  payoffSummary,
620
633
  };
@@ -1,3 +1,5 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
1
3
  /*
2
4
  Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
5
  Copyright (C) 2026 Piotr Franczyk
@@ -16,12 +18,10 @@
16
18
  along with this program. If not, see <https://www.gnu.org/licenses/>.
17
19
  */
18
20
  import Ajv from "ajv";
19
- import { existsSync, readFileSync } from "node:fs";
20
- import path from "node:path";
21
- import { Project, ScriptKind } from "ts-morph";
22
21
  import { escapeAtom, toPrologAtom, toPrologString, } from "kibi-cli/prolog/codec";
23
22
  import entitySchema from "kibi-cli/schemas/entity";
24
23
  import relationshipSchema from "kibi-cli/schemas/relationship";
24
+ import { Project, ScriptKind } from "ts-morph";
25
25
  import { isMcpDebugEnabled } from "../env.js";
26
26
  import { refreshCoordinatesForSymbolId } from "./symbols.js";
27
27
  let refreshCoordinatesForSymbolIdImpl = refreshCoordinatesForSymbolId;
@@ -225,7 +225,9 @@ function chooseScriptKind(filePath) {
225
225
  const lower = filePath.toLowerCase();
226
226
  if (lower.endsWith(".tsx"))
227
227
  return ScriptKind.TSX;
228
- if (lower.endsWith(".ts") || lower.endsWith(".mts") || lower.endsWith(".cts")) {
228
+ if (lower.endsWith(".ts") ||
229
+ lower.endsWith(".mts") ||
230
+ lower.endsWith(".cts")) {
229
231
  return ScriptKind.TS;
230
232
  }
231
233
  if (lower.endsWith(".jsx"))
@@ -238,7 +240,7 @@ function hasTraceabilityRelationship(relationships) {
238
240
  }
239
241
  function hasAllowedGranularityReason(entity) {
240
242
  const reason = entity.granularity_reason;
241
- return (typeof reason === "string" && ALLOWED_GRANULARITY_REASONS.has(reason));
243
+ return typeof reason === "string" && ALLOWED_GRANULARITY_REASONS.has(reason);
242
244
  }
243
245
  function collectNarrowExportNames(filePath, content) {
244
246
  const project = new Project({ skipAddingFilesFromTsConfig: true });
@@ -351,6 +351,82 @@ const BASE_TOOLS = [
351
351
  ],
352
352
  description: "Optional justification for a coarse file/module-level symbol traceability relationship when narrower function/class/type symbols exist.",
353
353
  },
354
+ fact_kind: {
355
+ type: "string",
356
+ enum: [
357
+ "subject",
358
+ "property_value",
359
+ "observation",
360
+ "meta",
361
+ "predicate_schema",
362
+ "predicate",
363
+ ],
364
+ description: "Optional fact lane kind for fact entities. Strict lane uses 'subject' and 'property_value'; context lane uses 'observation' or 'meta'.",
365
+ },
366
+ subject_key: {
367
+ type: "string",
368
+ description: "Optional canonical subject key for strict fact entities. Example: 'user.session'.",
369
+ },
370
+ property_key: {
371
+ type: "string",
372
+ description: "Optional canonical property key for property_value facts. Example: 'session.timeout_minutes'.",
373
+ },
374
+ operator: {
375
+ type: "string",
376
+ enum: ["eq", "neq", "lt", "lte", "gt", "gte"],
377
+ description: "Optional comparison operator for property_value facts. Example: 'eq'.",
378
+ },
379
+ value_type: {
380
+ type: "string",
381
+ enum: ["string", "int", "number", "bool"],
382
+ description: "Optional typed value discriminator for property_value facts.",
383
+ },
384
+ value_string: {
385
+ type: "string",
386
+ description: "Optional string value for property_value facts.",
387
+ },
388
+ value_int: {
389
+ type: "integer",
390
+ description: "Optional integer value for property_value facts.",
391
+ },
392
+ value_number: {
393
+ type: "number",
394
+ description: "Optional number value for property_value facts.",
395
+ },
396
+ value_bool: {
397
+ type: "boolean",
398
+ description: "Optional boolean value for property_value facts.",
399
+ },
400
+ unit: {
401
+ type: "string",
402
+ description: "Optional unit for numeric property_value facts.",
403
+ },
404
+ scope: {
405
+ type: "string",
406
+ description: "Optional scope qualifier for fact entities.",
407
+ },
408
+ polarity: {
409
+ type: "string",
410
+ enum: ["require", "forbid", "assert", "deny"],
411
+ description: "Optional polarity for property_value or predicate facts.",
412
+ },
413
+ closed_world: {
414
+ type: "boolean",
415
+ description: "Optional closed-world marker for strict fact interpretation.",
416
+ },
417
+ canonical_key: {
418
+ type: "string",
419
+ description: "Optional canonical identity key for predicate or strict fact claims.",
420
+ },
421
+ predicate_name: {
422
+ type: "string",
423
+ description: "Optional predicate name for ontology predicate facts.",
424
+ },
425
+ predicate_args: {
426
+ type: "array",
427
+ items: { type: "string" },
428
+ description: "Optional ordered predicate arguments for ontology predicate facts.",
429
+ },
354
430
  },
355
431
  required: ["title", "status"],
356
432
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-mcp",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "dependencies": {
5
5
  "@modelcontextprotocol/sdk": "^1.26.0",
6
6
  "ajv": "^8.18.0",
@@ -9,7 +9,7 @@
9
9
  "fast-glob": "^3.2.12",
10
10
  "gray-matter": "^4.0.3",
11
11
  "js-yaml": "^4.1.0",
12
- "kibi-cli": "^0.12.1",
12
+ "kibi-cli": "^0.12.3",
13
13
  "kibi-core": "^0.6.0",
14
14
  "mcpcat": "^0.1.12",
15
15
  "ts-morph": "^23.0.0",
@@ -27,10 +27,7 @@
27
27
  "build": "tsc -p tsconfig.json",
28
28
  "prepack": "npm run build"
29
29
  },
30
- "files": [
31
- "dist",
32
- "bin"
33
- ],
30
+ "files": ["dist", "bin"],
34
31
  "engines": {
35
32
  "node": ">=18",
36
33
  "bun": ">=1.0"