kibi-mcp 0.22.0 → 1.0.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.
- package/bin/kibi-mcp +12 -11
- package/dist/diagnostics.js +61 -1
- package/dist/env.js +5 -1
- package/dist/runtime/mcp-runtime.js +36 -3
- package/dist/server/docs.js +31 -42
- package/dist/server/icons.js +27 -0
- package/dist/server/json-schema-to-zod.js +18 -1
- package/dist/server/session.js +25 -51
- package/dist/server/tool-registration.js +35 -8
- package/dist/server/tools-runtime.js +4 -4
- package/dist/server/tools.js +86 -7
- package/dist/server.js +16 -1
- package/dist/tools/check-impact.js +1 -1
- package/dist/tools/check.js +1 -1
- package/dist/tools/core-module.js +11 -3
- package/dist/tools/coverage.js +1 -1
- package/dist/tools/delete.js +1 -1
- package/dist/tools/discovery-adapter.js +5 -0
- package/dist/tools/entity-query.js +1 -1
- package/dist/tools/find-gaps.js +9 -1
- package/dist/tools/graph.js +9 -1
- package/dist/tools/model-requirement.js +2 -2
- package/dist/tools/mutation-context.js +15 -9
- package/dist/tools/plan-bootstrap.js +21 -0
- package/dist/tools/prolog-list.js +1 -1
- package/dist/tools/query.js +1 -1
- package/dist/tools/relationship-validation.js +1 -1
- package/dist/tools/search.js +1 -1
- package/dist/tools/semantic-advisor.js +1 -1
- package/dist/tools/skills.js +2 -2
- package/dist/tools/sparql.js +1 -1
- package/dist/tools/status.js +1 -1
- package/dist/tools/suggest-predicates.js +1 -1
- package/dist/tools/symbols.js +310 -68
- package/dist/tools/upsert.js +3 -3
- package/dist/tools/validate-upsert.js +1 -1
- package/dist/tools-config.js +77 -3
- package/dist/workspace.js +3 -2
- package/package.json +13 -8
- package/dist/tools/autopilot-candidates.js +0 -554
- package/dist/tools/autopilot-discovery.js +0 -898
- package/dist/tools/autopilot-generate.js +0 -47
- package/dist/tools/autopilot-test-compat.js +0 -88
- package/dist/tools/autopilot-test-parsers.js +0 -120
- package/dist/tools/check-config.js +0 -67
package/bin/kibi-mcp
CHANGED
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
const args = globalThis.Bun?.argv?.slice(2) ?? process.argv.slice(2);
|
|
20
20
|
|
|
21
21
|
async function importStartupResolution() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
// Published packages contain the compiled runtime only. Do not replace a
|
|
23
|
+
// real dependency/import failure with a source-tree fallback: doing so
|
|
24
|
+
// hides package contract errors and makes startup depend on an unpackaged
|
|
25
|
+
// TypeScript tree.
|
|
26
|
+
return await import("../dist/startup-resolution.js");
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async function serverEntrypointUrl() {
|
|
@@ -32,7 +32,10 @@ async function serverEntrypointUrl() {
|
|
|
32
32
|
if (existsSync(distUrl)) {
|
|
33
33
|
return distUrl.href;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
throw new Error(
|
|
36
|
+
`[KIBI-MCP] Missing compiled server entrypoint: ${distUrl.pathname}. ` +
|
|
37
|
+
"Run the package build before launching kibi-mcp.",
|
|
38
|
+
);
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
async function buildResolution() {
|
|
@@ -64,11 +67,9 @@ async function importServer(entrypoint) {
|
|
|
64
67
|
const { pathToFileURL } = await import("node:url");
|
|
65
68
|
return await import(pathToFileURL(entrypoint).href);
|
|
66
69
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return await import("../src/server.ts");
|
|
71
|
-
}
|
|
70
|
+
// Preserve the original import error. In particular, an incompatible
|
|
71
|
+
// kibi-cli export must not be replaced with a misleading source fallback.
|
|
72
|
+
return await import("../dist/server.js");
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
function printHelp() {
|
package/dist/diagnostics.js
CHANGED
|
@@ -144,8 +144,46 @@ const RECEIPT_GAP_CODES = new Set([
|
|
|
144
144
|
"stale_verification_receipt",
|
|
145
145
|
"failed_verification_receipt",
|
|
146
146
|
"invalid_verification_receipt",
|
|
147
|
+
"verification_contract_mismatch",
|
|
147
148
|
"verification_snapshot_unavailable",
|
|
148
149
|
]);
|
|
150
|
+
const MAX_COVERAGE_RECEIPT_GAPS = 50;
|
|
151
|
+
function coverageReceiptGapDetails(rows) {
|
|
152
|
+
const details = rows
|
|
153
|
+
.flatMap((row) => {
|
|
154
|
+
const codes = [...new Set(stringArray(row.proofGaps))]
|
|
155
|
+
.filter((code) => RECEIPT_GAP_CODES.has(code))
|
|
156
|
+
.sort();
|
|
157
|
+
if (codes.length === 0)
|
|
158
|
+
return [];
|
|
159
|
+
const stages = isRecord(row.proofStages) ? row.proofStages : undefined;
|
|
160
|
+
const passingE2e = isRecord(stages?.passingE2e)
|
|
161
|
+
? stages.passingE2e
|
|
162
|
+
: undefined;
|
|
163
|
+
const testIds = [
|
|
164
|
+
"missingReceiptTests",
|
|
165
|
+
"staleReceiptTests",
|
|
166
|
+
"failedReceiptTests",
|
|
167
|
+
"invalidReceiptTests",
|
|
168
|
+
"contractMismatchReceiptTests",
|
|
169
|
+
"snapshotUnavailableTests",
|
|
170
|
+
].flatMap((key) => stringArray(passingE2e?.[key]));
|
|
171
|
+
const requirementId = typeof row.id === "string" ? row.id : "unknown";
|
|
172
|
+
return [
|
|
173
|
+
{
|
|
174
|
+
requirementId,
|
|
175
|
+
testIds: [...new Set(testIds)].sort(),
|
|
176
|
+
codes,
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
})
|
|
180
|
+
.sort((left, right) => left.requirementId.localeCompare(right.requirementId));
|
|
181
|
+
return {
|
|
182
|
+
details: details.slice(0, MAX_COVERAGE_RECEIPT_GAPS),
|
|
183
|
+
total: details.length,
|
|
184
|
+
truncated: details.length > MAX_COVERAGE_RECEIPT_GAPS,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
149
187
|
function appendCoverageFields(fields, args, structuredContent) {
|
|
150
188
|
const rows = Array.isArray(structuredContent.rows)
|
|
151
189
|
? structuredContent.rows.filter(isRecord)
|
|
@@ -166,6 +204,10 @@ function appendCoverageFields(fields, args, structuredContent) {
|
|
|
166
204
|
fields.coverage_proof_gap_count = gapCodes.length;
|
|
167
205
|
fields.coverage_gap_codes = uniqueGapCodes;
|
|
168
206
|
fields.coverage_receipt_gap_count = gapCodes.filter((gap) => RECEIPT_GAP_CODES.has(gap)).length;
|
|
207
|
+
const receiptGaps = coverageReceiptGapDetails(rows);
|
|
208
|
+
fields.coverage_receipt_gaps = receiptGaps.details;
|
|
209
|
+
fields.coverage_receipt_gap_total = receiptGaps.total;
|
|
210
|
+
fields.coverage_receipt_gaps_truncated = receiptGaps.truncated;
|
|
169
211
|
if (typeof scope?.complete === "boolean") {
|
|
170
212
|
fields.coverage_scope_complete = scope.complete;
|
|
171
213
|
}
|
|
@@ -234,7 +276,25 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
234
276
|
fields.actor_id = telemetry.actor_id;
|
|
235
277
|
}
|
|
236
278
|
}
|
|
237
|
-
const
|
|
279
|
+
const envelope = structuredContentFrom(result);
|
|
280
|
+
const structuredContent = envelope?.kibiProtocol === 1 && isRecord(envelope.data)
|
|
281
|
+
? envelope.data
|
|
282
|
+
: envelope;
|
|
283
|
+
if (envelope?.kibiProtocol === 1) {
|
|
284
|
+
fields.protocol_version = envelope.kibiProtocol;
|
|
285
|
+
fields.result_version = envelope.resultVersion ?? null;
|
|
286
|
+
fields.result_status = envelope.status ?? null;
|
|
287
|
+
fields.effect_failures = Array.isArray(envelope.effects)
|
|
288
|
+
? envelope.effects.filter((effect) => effect !== null &&
|
|
289
|
+
typeof effect === "object" &&
|
|
290
|
+
!Array.isArray(effect) &&
|
|
291
|
+
effect.status === "failed")
|
|
292
|
+
: [];
|
|
293
|
+
fields.followed_next_actions = Array.isArray(telemetry?.followed_next_actions)
|
|
294
|
+
? telemetry.followed_next_actions
|
|
295
|
+
: [];
|
|
296
|
+
fields.unsafe_original_retry = telemetry?.unsafe_original_retry === true;
|
|
297
|
+
}
|
|
238
298
|
if (toolName === "kb_query" || toolName === "kb_search") {
|
|
239
299
|
const resultCount = Number(structuredContent?.count ?? 0);
|
|
240
300
|
fields.result_count = resultCount;
|
package/dist/env.js
CHANGED
|
@@ -36,7 +36,11 @@ export function isMcpDebugEnabled() {
|
|
|
36
36
|
}
|
|
37
37
|
export function getBranchOverride() {
|
|
38
38
|
// implements REQ-002
|
|
39
|
-
|
|
39
|
+
// Branch names are identities, not display labels. Preserve the value
|
|
40
|
+
// exactly so the Git-ref validator can reject whitespace or other invalid
|
|
41
|
+
// input instead of silently attaching a different branch.
|
|
42
|
+
const value = getEnvValue("KIBI_BRANCH");
|
|
43
|
+
return value === undefined || value.length === 0 ? undefined : value;
|
|
40
44
|
}
|
|
41
45
|
export function getKbPlPathOverride() {
|
|
42
46
|
// implements REQ-002
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveBranchAttachment } from "kibi-runtime";
|
|
1
2
|
// implements REQ-kibi-operation-interface-parity
|
|
2
3
|
export function createMcpRuntime(session) {
|
|
3
4
|
const sessionPrologs = new WeakMap();
|
|
@@ -11,18 +12,50 @@ export function createMcpRuntime(session) {
|
|
|
11
12
|
signal: options.signal ?? session.signal ?? new AbortController().signal,
|
|
12
13
|
clock: options.clock ?? session.clock ?? (() => new Date()),
|
|
13
14
|
...(fs ? { fs } : {}),
|
|
15
|
+
...(fs ? { sourceFirst: true } : {}),
|
|
14
16
|
...(git ? { git } : {}),
|
|
15
17
|
...(net ? { net } : {}),
|
|
16
18
|
};
|
|
19
|
+
const attachment = resolveBranchAttachment(context.workspaceRoot);
|
|
20
|
+
if (!("error" in attachment) && attachment.migrationRequired) {
|
|
21
|
+
console.warn(`[KIBI-MCP] Legacy branch attachment: Git '${attachment.gitBranch}' is reading KB '${attachment.kbBranch}'. Run 'kibi branch migrate --from ${attachment.kbBranch} --to ${attachment.gitBranch} --apply'; writes are blocked until then.`);
|
|
22
|
+
}
|
|
23
|
+
const withAttachment = "error" in attachment
|
|
24
|
+
? context
|
|
25
|
+
: { ...context, branchAttachment: attachment };
|
|
17
26
|
if (!spec.requiresProlog) {
|
|
18
|
-
|
|
27
|
+
// Preserve explicit host/test injection without asking the shared MCP
|
|
28
|
+
// session to initialise a store merely to report its condition.
|
|
29
|
+
const providedProlog = options.prolog;
|
|
30
|
+
let lazyProlog;
|
|
31
|
+
const lazyContext = {};
|
|
32
|
+
const ensureProlog = async () => {
|
|
33
|
+
if (providedProlog !== undefined)
|
|
34
|
+
return providedProlog;
|
|
35
|
+
if (lazyProlog !== undefined)
|
|
36
|
+
return session.adaptProlog(lazyProlog);
|
|
37
|
+
lazyProlog = await session.ensureProlog();
|
|
38
|
+
if (lazyContext.current !== undefined)
|
|
39
|
+
sessionPrologs.set(lazyContext.current, lazyProlog);
|
|
40
|
+
return session.adaptProlog(lazyProlog);
|
|
41
|
+
};
|
|
42
|
+
const operationContext = {
|
|
43
|
+
...withAttachment,
|
|
44
|
+
...(options.prolog ? { prolog: options.prolog } : {}),
|
|
45
|
+
ensureProlog,
|
|
46
|
+
};
|
|
47
|
+
lazyContext.current = operationContext;
|
|
48
|
+
return operationContext;
|
|
49
|
+
}
|
|
50
|
+
if (!("error" in attachment) && attachment.migrationRequired) {
|
|
51
|
+
throw new Error(`Legacy branch attachment requires explicit migration before '${spec.name}'; run kibi branch migrate --from ${attachment.kbBranch} --to ${attachment.gitBranch} --apply`);
|
|
19
52
|
}
|
|
20
53
|
if (options.prolog) {
|
|
21
|
-
return { ...
|
|
54
|
+
return { ...withAttachment, prolog: options.prolog };
|
|
22
55
|
}
|
|
23
56
|
const sessionProlog = await session.ensureProlog();
|
|
24
57
|
const operationContext = {
|
|
25
|
-
...
|
|
58
|
+
...withAttachment,
|
|
26
59
|
prolog: session.adaptProlog(sessionProlog),
|
|
27
60
|
};
|
|
28
61
|
sessionPrologs.set(operationContext, sessionProlog);
|
package/dist/server/docs.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
import { TOOLS } from "../tools-config.js";
|
|
19
|
+
import { KIBI_ICONS } from "./icons.js";
|
|
19
20
|
const ACTIVE_TOOLS = TOOLS;
|
|
20
21
|
function renderToolsDoc() {
|
|
21
22
|
const lines = [
|
|
@@ -41,58 +42,32 @@ function renderToolsDoc() {
|
|
|
41
42
|
export const PROMPTS = [
|
|
42
43
|
// implements REQ-002, REQ-013, REQ-mcp-search-discovery
|
|
43
44
|
{
|
|
44
|
-
name: "
|
|
45
|
-
|
|
45
|
+
name: "kibi-bootstrap",
|
|
46
|
+
title: "Bootstrap Kibi knowledge",
|
|
47
|
+
description: "Bootstrap Kibi knowledge for an existing repository through the canonical planner and apply operation.",
|
|
46
48
|
text: [
|
|
47
|
-
"# Kibi
|
|
49
|
+
"# Kibi Bootstrap",
|
|
48
50
|
"",
|
|
49
|
-
"
|
|
51
|
+
"Route an explicit bootstrap request to the canonical `kibi-bootstrap` skill and planner.",
|
|
50
52
|
"",
|
|
51
|
-
"##
|
|
53
|
+
"## Route",
|
|
52
54
|
"",
|
|
53
|
-
"
|
|
54
|
-
"1. **Project Summary**: What is the core purpose of this project?",
|
|
55
|
-
"2. **Source of Truth**: Where is the primary documentation (canonical requirements, ADRs)?",
|
|
56
|
-
"3. **Priority Root**: In a monorepo, which package should be prioritized?",
|
|
57
|
-
"4. **Verification Anchors**: Where are the primary tests or verification configs located?",
|
|
55
|
+
"Inspect `kb_status.bootstrap` and follow its typed `nextAction`. If infrastructure is missing, run `kibi init` before planning.",
|
|
58
56
|
"",
|
|
59
|
-
"##
|
|
57
|
+
"## Preview and Approval",
|
|
60
58
|
"",
|
|
61
|
-
"Call `
|
|
59
|
+
"Call `kb_plan_bootstrap` read-only. Ask only the bounded questions returned when its status is `needs_context`; do not invent a questionnaire.",
|
|
62
60
|
"",
|
|
63
|
-
"
|
|
64
|
-
"- `promptBlock`: review text that must be surfaced before writes",
|
|
65
|
-
"- `recommendedActions`: agent-facing next steps, including any REQ/SCEN/TEST authoring routed for manual handling",
|
|
66
|
-
"- `declaredContext`: the user-provided bootstrap context",
|
|
67
|
-
"- `confidence`: confidence summary for the generated output",
|
|
68
|
-
"- `bootstrapMode`: current KB state (e.g., `root_uninitialized`)",
|
|
69
|
-
"- `candidates`: synthesized entities grounded in declared context and source evidence",
|
|
70
|
-
"- `applyPlan`: exact sequential `kb_upsert` payloads for approved candidates",
|
|
71
|
-
"- `discoverySummary`: source-backed discovery notes",
|
|
61
|
+
"Show the exact returned `structuredContent.plan`, its actions, and canonical hash. Get explicit user approval, then pass that plan object unchanged to `kb_apply_plan`.",
|
|
72
62
|
"",
|
|
73
|
-
"##
|
|
63
|
+
"## Verify and repair",
|
|
74
64
|
"",
|
|
75
|
-
"
|
|
76
|
-
"",
|
|
77
|
-
"## Step 4: Apply Candidates",
|
|
78
|
-
"",
|
|
79
|
-
"Apply candidates sequentially using `kb_upsert`.",
|
|
80
|
-
"1. Execute `structuredContent.applyPlan` sequentially in listed order.",
|
|
81
|
-
"2. Confirm success of each `kb_upsert` before moving to the next.",
|
|
82
|
-
"3. Run `kb_check` after the batch to verify KB integrity.",
|
|
83
|
-
"",
|
|
84
|
-
"## Rules",
|
|
85
|
-
"- Never apply bootstrap writes without user-facing preview and explicit approval.",
|
|
86
|
-
"- `kb_autopilot_generate` is strictly read-only; synthesis is the backend, not the actor.",
|
|
87
|
-
"- MCP tools and the trusted project-local CLI are peer surfaces; choose by what is visible and approved here.",
|
|
88
|
-
"- If Kibi MCP tools are visible and approved, use MCP.",
|
|
89
|
-
"- Otherwise, in a trusted workspace, use the project-local CLI's dedicated JSON routes with `--input <file|->`.",
|
|
90
|
-
"- If neither interface is available, Kibi operation is blocked; tell the operator to enable MCP or the trusted project-local CLI. Do not infer MCP availability from config file existence.",
|
|
91
|
-
"- Do not read or edit `.kb/` files directly. Query before mutate. Run `kb_upsert` sequentially. Run `kb_check` before completion.",
|
|
65
|
+
"Inspect the typed `kb_apply_plan` result, follow any `nextActions` for recovery, and finish with `kb_check` and `kb_status`. Never read or edit `.kb` directly or reconstruct a plan.",
|
|
92
66
|
].join("\n"),
|
|
93
67
|
},
|
|
94
68
|
{
|
|
95
69
|
name: "kibi_overview",
|
|
70
|
+
title: "Kibi usage overview",
|
|
96
71
|
description: "High-level model for using kibi-mcp safely and effectively.",
|
|
97
72
|
text: [
|
|
98
73
|
"# kibi-mcp Overview",
|
|
@@ -113,7 +88,7 @@ export const PROMPTS = [
|
|
|
113
88
|
"Core modeling principles:",
|
|
114
89
|
"- Kibi has eight entity types: common authoring (req, scenario, test, fact) and supporting/system (adr, flag, event, symbol).",
|
|
115
90
|
"- Encode requirements as linked facts: `req --constrains--> fact` plus `req --requires_property--> fact`.",
|
|
116
|
-
"- High-confidence `kb_model_requirement` output is deterministic; `/
|
|
91
|
+
"- High-confidence `kb_model_requirement` output is deterministic; `/kibi-bootstrap` bootstrap writes still require preview and explicit approval.",
|
|
117
92
|
"- Low-confidence claims (< 0.7) are downgraded to `observation` facts to prevent false-positive contradictions.",
|
|
118
93
|
"- Only strict domain facts participate in contradiction inference; observation and meta facts are non-blocking notes.",
|
|
119
94
|
"- v1 contradictions are limited to exact-value, boolean/enum, numeric range, and polarity conflicts.",
|
|
@@ -127,6 +102,7 @@ export const PROMPTS = [
|
|
|
127
102
|
},
|
|
128
103
|
{
|
|
129
104
|
name: "kibi_workflow",
|
|
105
|
+
title: "Kibi workflow steps",
|
|
130
106
|
description: "Step-by-step call order for discovery, mutation, and verification.",
|
|
131
107
|
text: [
|
|
132
108
|
"# kibi-mcp Workflow",
|
|
@@ -149,6 +125,7 @@ export const PROMPTS = [
|
|
|
149
125
|
},
|
|
150
126
|
{
|
|
151
127
|
name: "kibi_constraints",
|
|
128
|
+
title: "Kibi constraints",
|
|
152
129
|
description: "Operational limits, validation rules, and mutation gotchas.",
|
|
153
130
|
text: [
|
|
154
131
|
"# kibi-mcp Constraints",
|
|
@@ -236,6 +213,7 @@ function registerDocResources() {
|
|
|
236
213
|
{
|
|
237
214
|
uri: "kibi://docs/overview",
|
|
238
215
|
name: "kibi docs overview",
|
|
216
|
+
title: "Kibi server overview",
|
|
239
217
|
description: "Full server description, purpose, and scope.",
|
|
240
218
|
mimeType: "text/markdown",
|
|
241
219
|
text: overview,
|
|
@@ -243,6 +221,7 @@ function registerDocResources() {
|
|
|
243
221
|
{
|
|
244
222
|
uri: "kibi://docs/tools",
|
|
245
223
|
name: "kibi docs tools",
|
|
224
|
+
title: "Kibi tools reference",
|
|
246
225
|
description: "Available tools with summaries and required parameters.",
|
|
247
226
|
mimeType: "text/markdown",
|
|
248
227
|
text: renderToolsDoc(),
|
|
@@ -250,6 +229,7 @@ function registerDocResources() {
|
|
|
250
229
|
{
|
|
251
230
|
uri: "kibi://docs/errors",
|
|
252
231
|
name: "kibi docs errors",
|
|
232
|
+
title: "Kibi error guide",
|
|
253
233
|
description: "Common error modes and suggested recovery actions.",
|
|
254
234
|
mimeType: "text/markdown",
|
|
255
235
|
text: errors,
|
|
@@ -257,6 +237,7 @@ function registerDocResources() {
|
|
|
257
237
|
{
|
|
258
238
|
uri: "kibi://docs/examples",
|
|
259
239
|
name: "kibi docs examples",
|
|
240
|
+
title: "Kibi usage examples",
|
|
260
241
|
description: "Concrete tool call sequences for common tasks.",
|
|
261
242
|
mimeType: "text/markdown",
|
|
262
243
|
text: examples,
|
|
@@ -266,7 +247,10 @@ function registerDocResources() {
|
|
|
266
247
|
export const DOC_RESOURCES = registerDocResources();
|
|
267
248
|
export function setupDocsAndPrompts(server) {
|
|
268
249
|
for (const prompt of PROMPTS) {
|
|
269
|
-
server.
|
|
250
|
+
server.registerPrompt(prompt.name, {
|
|
251
|
+
title: prompt.title,
|
|
252
|
+
description: prompt.description,
|
|
253
|
+
}, async () => ({
|
|
270
254
|
messages: [
|
|
271
255
|
{
|
|
272
256
|
role: "user",
|
|
@@ -276,7 +260,12 @@ export function setupDocsAndPrompts(server) {
|
|
|
276
260
|
}));
|
|
277
261
|
}
|
|
278
262
|
for (const resource of DOC_RESOURCES) {
|
|
279
|
-
server.
|
|
263
|
+
server.registerResource(resource.name, resource.uri, {
|
|
264
|
+
title: resource.title,
|
|
265
|
+
description: resource.description,
|
|
266
|
+
mimeType: resource.mimeType,
|
|
267
|
+
icons: KIBI_ICONS,
|
|
268
|
+
}, async () => ({
|
|
280
269
|
contents: [
|
|
281
270
|
{
|
|
282
271
|
uri: resource.uri,
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
const KIBI_LOGO_MARK_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 308 309"><rect width="308" height="309" rx="64" fill="#1D1E23"/><ellipse cx="113.3" cy="150.9" rx="51.3" ry="51.5" fill="#A2D3F4"/><path d="M228.1 197.8L227.6 165.5H156.5L95.4 165.3V135.1H156.5L227.5 135.1V101.2H252.2V197.8Z" fill="#A2D3F4"/></svg>`;
|
|
19
|
+
const KIBI_ICON_SRC = `data:image/svg+xml;base64,${Buffer.from(KIBI_LOGO_MARK_SVG, "utf8").toString("base64")}`;
|
|
20
|
+
export const KIBI_ICONS = [
|
|
21
|
+
{
|
|
22
|
+
src: KIBI_ICON_SRC,
|
|
23
|
+
mimeType: "image/svg+xml",
|
|
24
|
+
sizes: ["any"],
|
|
25
|
+
theme: "dark",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
@@ -73,7 +73,19 @@ export function jsonSchemaToZod(schema) {
|
|
|
73
73
|
const union = z.union(literalSchemas);
|
|
74
74
|
return description ? union.describe(description) : union;
|
|
75
75
|
}
|
|
76
|
-
const
|
|
76
|
+
const schemaTypes = Array.isArray(obj.type)
|
|
77
|
+
? obj.type.filter((value) => typeof value === "string")
|
|
78
|
+
: typeof obj.type === "string"
|
|
79
|
+
? [obj.type]
|
|
80
|
+
: [];
|
|
81
|
+
// JSON Schema nullable fields are represented as a type union in the
|
|
82
|
+
// catalog. Build a real Zod union so MCP publishes and validates the same
|
|
83
|
+
// nullability instead of silently degrading the field to z.any().
|
|
84
|
+
if (schemaTypes.length > 1) {
|
|
85
|
+
const variants = schemaTypes.map((type) => jsonSchemaToZod({ ...obj, type }));
|
|
86
|
+
return z.union(variants);
|
|
87
|
+
}
|
|
88
|
+
const schemaType = schemaTypes[0];
|
|
77
89
|
switch (schemaType) {
|
|
78
90
|
case "object": {
|
|
79
91
|
const properties = obj.properties && typeof obj.properties === "object"
|
|
@@ -178,6 +190,11 @@ export function jsonSchemaToZod(schema) {
|
|
|
178
190
|
const description = typeof obj.description === "string" ? obj.description : undefined;
|
|
179
191
|
return description ? b.describe(description) : b;
|
|
180
192
|
}
|
|
193
|
+
case "null": {
|
|
194
|
+
const n = z.null();
|
|
195
|
+
const description = typeof obj.description === "string" ? obj.description : undefined;
|
|
196
|
+
return description ? n.describe(description) : n;
|
|
197
|
+
}
|
|
181
198
|
default: {
|
|
182
199
|
const anySchema = z.any();
|
|
183
200
|
const description = typeof obj.description === "string" ? obj.description : undefined;
|
package/dist/server/session.js
CHANGED
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
import fs from "node:fs";
|
|
19
19
|
import { createRequire } from "node:module";
|
|
20
20
|
import process from "node:process";
|
|
21
|
-
import { EngineClient } from "kibi-
|
|
22
|
-
import { PrologProcess } from "kibi-
|
|
23
|
-
import { copyCleanSnapshot, getBranchDiagnostic, isValidBranchName, resolveActiveBranch, } from "kibi-
|
|
21
|
+
import { EngineClient } from "kibi-runtime";
|
|
22
|
+
import { PrologProcess } from "kibi-runtime";
|
|
23
|
+
import { copyCleanSnapshot, ensureBranchStoreManifest, getBranchDiagnostic, isValidBranchName, resolveActiveBranch, resolveBranchAttachment, } from "kibi-runtime";
|
|
24
24
|
import { getBranchOverride, isMcpDebugEnabled } from "../env.js";
|
|
25
25
|
import { resolveKbPath, resolveWorkspaceRoot } from "../workspace.js";
|
|
26
26
|
import { KbRefreshError, describeBranchKbStamp, readBranchKbStamp, sameBranchKbStamp, } from "./kb-freshness.js";
|
|
@@ -32,6 +32,7 @@ const defaultSessionDeps = {
|
|
|
32
32
|
getBranchDiagnostic,
|
|
33
33
|
isValidBranchName,
|
|
34
34
|
resolveActiveBranch,
|
|
35
|
+
resolveBranchAttachment,
|
|
35
36
|
resolveKbPath,
|
|
36
37
|
resolveWorkspaceRoot,
|
|
37
38
|
};
|
|
@@ -90,19 +91,19 @@ export function ensureBranchKbExists(workspaceRoot, branch) {
|
|
|
90
91
|
if (sessionDeps.fs.existsSync(branchPath)) {
|
|
91
92
|
return false;
|
|
92
93
|
}
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
// Branch initialization is intentionally empty. The compiled store is
|
|
95
|
+
// materialized from the current checkout's authored sources by sync; it is
|
|
96
|
+
// never copied from another branch.
|
|
97
|
+
// Unit/integration hosts can provide a virtual filesystem through the
|
|
98
|
+
// existing session dependency seam. Only the real Node filesystem needs
|
|
99
|
+
// the identity manifest writer; virtual hosts already model the resolved
|
|
100
|
+
// branch path and should not be forced to create that path on the host.
|
|
101
|
+
if (sessionDeps.fs === fs) {
|
|
102
|
+
ensureBranchStoreManifest(workspaceRoot, branch);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
sessionDeps.fs.mkdirSync(branchPath, { recursive: true });
|
|
103
106
|
}
|
|
104
|
-
// No previous branch available - create empty KB
|
|
105
|
-
sessionDeps.fs.mkdirSync(branchPath, { recursive: true });
|
|
106
107
|
debugLog(`[KIBI-MCP] Created empty branch KB for '${branch}'`);
|
|
107
108
|
return true;
|
|
108
109
|
}
|
|
@@ -228,14 +229,15 @@ async function ensurePrologUnsafe() {
|
|
|
228
229
|
targetBranch = envBranch;
|
|
229
230
|
}
|
|
230
231
|
else {
|
|
231
|
-
// No override - resolve
|
|
232
|
-
|
|
232
|
+
// No override - resolve the exact Git branch, with read-compatible legacy
|
|
233
|
+
// literal-path storage detection for bridge-release repositories.
|
|
234
|
+
const branchResult = sessionDeps.resolveBranchAttachment(workspaceRoot);
|
|
233
235
|
if ("error" in branchResult) {
|
|
234
236
|
const diagnostic = sessionDeps.getBranchDiagnostic(undefined, branchResult.error);
|
|
235
237
|
console.error(`[KIBI-MCP] ${diagnostic}`);
|
|
236
238
|
throw new Error(`Failed to resolve active branch: ${branchResult.error}`);
|
|
237
239
|
}
|
|
238
|
-
targetBranch = branchResult.
|
|
240
|
+
targetBranch = branchResult.kbBranch;
|
|
239
241
|
}
|
|
240
242
|
// Check if we need to switch branches
|
|
241
243
|
if (isInitialized && useEngine && prologProcess?.isRunning()) {
|
|
@@ -322,39 +324,11 @@ async function ensurePrologUnsafe() {
|
|
|
322
324
|
: new sessionDeps.PrologProcess({ timeout: 120000 });
|
|
323
325
|
await prologProcess.start();
|
|
324
326
|
await assertGeneration();
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
try {
|
|
331
|
-
const resolved = req.resolve("kibi-cli/prolog");
|
|
332
|
-
debugLog(`[KIBI-MCP] require.resolve('kibi-cli/prolog') -> ${resolved}`);
|
|
333
|
-
}
|
|
334
|
-
catch (resolveErr) {
|
|
335
|
-
debugLog("[KIBI-MCP] require.resolve('kibi-cli/prolog') failed:", resolveErr.message);
|
|
336
|
-
}
|
|
337
|
-
// Try to read package.json for kibi-cli to get version. This may fail if
|
|
338
|
-
// the package uses exports blocking package.json access — log explicit failure.
|
|
339
|
-
try {
|
|
340
|
-
// prefer direct package.json require; createRequire makes this ESM-friendly
|
|
341
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
342
|
-
const pkg = req("kibi-cli/package.json");
|
|
343
|
-
if (pkg && typeof pkg.version === "string") {
|
|
344
|
-
debugLog(`[KIBI-MCP] kibi-cli version: ${pkg.version}`);
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
debugLog("[KIBI-MCP] kibi-cli package.json read but no version field");
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
catch (pkgErr) {
|
|
351
|
-
debugLog("[KIBI-MCP] Failed to read kibi-cli package.json (exports may restrict access):", pkgErr.message);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
catch (err) {
|
|
355
|
-
debugLog("[KIBI-MCP] Failed to create require() for debug lookup:", err.message);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
327
|
+
// Adapters report the package that owns runtime execution. Do not resolve
|
|
328
|
+
// CLI implementation paths from the MCP process; the runtime package is
|
|
329
|
+
// the sole first-party execution boundary.
|
|
330
|
+
if (isMcpDebugEnabled())
|
|
331
|
+
debugLog("[KIBI-MCP] Runtime boundary: kibi-runtime");
|
|
358
332
|
debugLog("[KIBI-MCP] Branch selection:");
|
|
359
333
|
debugLog(`[KIBI-MCP] KIBI_BRANCH env: ${envBranch || "not set"}`);
|
|
360
334
|
debugLog(`[KIBI-MCP] Resolved branch: ${targetBranch}`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getSpec } from "kibi-
|
|
1
|
+
import { getSpec, statusSpec } from "kibi-runtime";
|
|
2
|
+
import { executeApplyPlan, executeCompileIntent, executeDelete, executeIngestVerification, executeUpsert, validateUpsertSpec, } from "kibi-runtime";
|
|
2
3
|
// implements REQ-002, REQ-013
|
|
3
4
|
export function registerConfiguredTools(server, runtime, registerTool) {
|
|
4
5
|
const toolDef = (name) => {
|
|
@@ -14,6 +15,10 @@ export function registerConfiguredTools(server, runtime, registerTool) {
|
|
|
14
15
|
}
|
|
15
16
|
return prolog;
|
|
16
17
|
};
|
|
18
|
+
const withSessionProlog = (context) => ({
|
|
19
|
+
...context,
|
|
20
|
+
prolog: prologFor(context),
|
|
21
|
+
});
|
|
17
22
|
const register = ({ name, execute }) => {
|
|
18
23
|
const definition = toolDef(name);
|
|
19
24
|
const publicSpec = getSpec(name);
|
|
@@ -23,7 +28,7 @@ export function registerConfiguredTools(server, runtime, registerTool) {
|
|
|
23
28
|
requiresProlog: publicSpec.requiresProlog,
|
|
24
29
|
execute: (args, context) => execute(context, args),
|
|
25
30
|
};
|
|
26
|
-
registerTool(server, name, definition.description, definition.inputSchema, async () => undefined, runtime, spec, definition.annotations);
|
|
31
|
+
registerTool(server, name, definition.description, definition.inputSchema, async () => undefined, runtime, spec, definition.annotations, definition.outputSchema);
|
|
27
32
|
};
|
|
28
33
|
// INTENTIONAL ARGUMENT CASTS: The `args as (unknown as)? XyzArgs` casts below
|
|
29
34
|
// bridge the generic ToolHandler (which receives Record<string, unknown>) to the
|
|
@@ -39,7 +44,11 @@ export function registerConfiguredTools(server, runtime, registerTool) {
|
|
|
39
44
|
});
|
|
40
45
|
register({
|
|
41
46
|
name: "kb_status",
|
|
42
|
-
execute: async (context, args) =>
|
|
47
|
+
execute: async (context, args) =>
|
|
48
|
+
// Status owns its non-mutating fallback. Do not require the session engine
|
|
49
|
+
// here: an absent or unreadable branch store is exactly the condition it
|
|
50
|
+
// must be able to describe.
|
|
51
|
+
statusSpec.execute(args, context),
|
|
43
52
|
});
|
|
44
53
|
register({
|
|
45
54
|
name: "kb_skills_list",
|
|
@@ -75,15 +84,15 @@ export function registerConfiguredTools(server, runtime, registerTool) {
|
|
|
75
84
|
});
|
|
76
85
|
register({
|
|
77
86
|
name: "kb_upsert",
|
|
78
|
-
execute: async (context, args) =>
|
|
87
|
+
execute: async (context, args) => executeUpsert(args, withSessionProlog(context)),
|
|
79
88
|
});
|
|
80
89
|
register({
|
|
81
90
|
name: "kb_validate_upsert",
|
|
82
|
-
execute: async (context, args) =>
|
|
91
|
+
execute: async (context, args) => validateUpsertSpec.execute(args, withSessionProlog(context)),
|
|
83
92
|
});
|
|
84
93
|
register({
|
|
85
94
|
name: "kb_delete",
|
|
86
|
-
execute: async (context, args) =>
|
|
95
|
+
execute: async (context, args) => executeDelete(args, withSessionProlog(context)),
|
|
87
96
|
});
|
|
88
97
|
register({
|
|
89
98
|
name: "kb_check",
|
|
@@ -98,7 +107,25 @@ export function registerConfiguredTools(server, runtime, registerTool) {
|
|
|
98
107
|
execute: async (context, args) => runtime.handleKbSuggestPredicates(prologFor(context), args),
|
|
99
108
|
});
|
|
100
109
|
register({
|
|
101
|
-
name: "
|
|
102
|
-
execute: async (context, args) => runtime.
|
|
110
|
+
name: "kb_plan_bootstrap",
|
|
111
|
+
execute: async (context, args) => runtime.handleKbPlanBootstrap(args, context),
|
|
112
|
+
});
|
|
113
|
+
register({
|
|
114
|
+
name: "kb_compile_intent",
|
|
115
|
+
execute: async (context, args) => runtime.handleKbCompileIntent
|
|
116
|
+
? runtime.handleKbCompileIntent(args, context)
|
|
117
|
+
: executeCompileIntent(args, context),
|
|
118
|
+
});
|
|
119
|
+
register({
|
|
120
|
+
name: "kb_apply_plan",
|
|
121
|
+
execute: async (context, args) => runtime.handleKbApplyPlan
|
|
122
|
+
? runtime.handleKbApplyPlan(args, context)
|
|
123
|
+
: executeApplyPlan(args, context),
|
|
124
|
+
});
|
|
125
|
+
register({
|
|
126
|
+
name: "kb_ingest_verification",
|
|
127
|
+
execute: async (context, args) => runtime.handleKbIngestVerification
|
|
128
|
+
? runtime.handleKbIngestVerification(args, context)
|
|
129
|
+
: executeIngestVerification(args, context),
|
|
103
130
|
});
|
|
104
131
|
}
|