kibi-mcp 0.14.0 → 0.14.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/kibi-mcp CHANGED
@@ -18,6 +18,59 @@
18
18
  */
19
19
  const args = globalThis.Bun?.argv?.slice(2) ?? process.argv.slice(2);
20
20
 
21
+ async function importStartupResolution() {
22
+ try {
23
+ return await import("../dist/startup-resolution.js");
24
+ } catch {
25
+ return await import("../src/startup-resolution.ts");
26
+ }
27
+ }
28
+
29
+ async function serverEntrypointUrl() {
30
+ const { existsSync } = await import("node:fs");
31
+ const distUrl = new URL("../dist/server.js", import.meta.url);
32
+ if (existsSync(distUrl)) {
33
+ return distUrl.href;
34
+ }
35
+ return new URL("../src/server.ts", import.meta.url).href;
36
+ }
37
+
38
+ async function buildResolution() {
39
+ const {
40
+ compareMcpResolution,
41
+ formatResolutionJson,
42
+ readRunningPackageInfo,
43
+ resolveProjectLocalMcp,
44
+ } = await importStartupResolution();
45
+ const running = readRunningPackageInfo(await serverEntrypointUrl());
46
+ const projectLocal = resolveProjectLocalMcp(process.cwd());
47
+ const comparison = compareMcpResolution(running, projectLocal);
48
+ const result = {
49
+ packageName: "kibi-mcp",
50
+ packageVersion: running.version,
51
+ version: running.version,
52
+ resolved: running.entrypoint,
53
+ cwd: process.cwd(),
54
+ running,
55
+ projectLocal,
56
+ ...comparison,
57
+ };
58
+
59
+ return { result, json: formatResolutionJson(result) };
60
+ }
61
+
62
+ async function importServer(entrypoint) {
63
+ if (entrypoint) {
64
+ const { pathToFileURL } = await import("node:url");
65
+ return await import(pathToFileURL(entrypoint).href);
66
+ }
67
+ try {
68
+ return await import("../dist/server.js");
69
+ } catch {
70
+ return await import("../src/server.ts");
71
+ }
72
+ }
73
+
21
74
  function printHelp() {
22
75
  process.stdout.write(`Usage: kibi-mcp [options]
23
76
 
@@ -25,6 +78,7 @@ Starts the Kibi MCP server over stdio.
25
78
 
26
79
  Options:
27
80
  --diagnostic-mode enable diagnostic logging to .kb/usage.log
81
+ --print-resolution print kibi-mcp startup resolution JSON and exit
28
82
  -h, --help show help
29
83
  `);
30
84
  }
@@ -33,6 +87,11 @@ if (args.includes("-h") || args.includes("--help")) {
33
87
  printHelp();
34
88
  process.exitCode = 0;
35
89
  } else {
90
+ if (args.includes("--print-resolution")) {
91
+ const { json } = await buildResolution();
92
+ process.stdout.write(json);
93
+ process.exitCode = 0;
94
+ } else {
36
95
  if (args.includes("--diagnostic-mode") && !process.argv.includes("--diagnostic-mode")) {
37
96
  process.argv.push("--diagnostic-mode");
38
97
  }
@@ -43,7 +102,31 @@ if (args.includes("-h") || args.includes("--help")) {
43
102
  process.env.KIBI_MCP_DIAGNOSTIC_MODE = "1";
44
103
  }
45
104
 
46
- const { startServer } = await import("../dist/server.js");
105
+ const { result, json } = await buildResolution();
106
+ if (process.env.KIBI_MCP_DEBUG === "1" || args.includes("--diagnostic-mode")) {
107
+ process.stderr.write(json);
108
+ }
109
+
110
+ if (result.stale) {
111
+ if (result.projectLocal && process.env.KIBI_MCP_PROJECT_LOCAL_REENTRY !== "1") {
112
+ process.env.KIBI_MCP_PROJECT_LOCAL_REENTRY = "1";
113
+ process.stderr.write(
114
+ `[KIBI-MCP] Re-entering project-local kibi-mcp at ${result.projectLocal.entrypoint}; stale running path was ${result.running.entrypoint}\n`,
115
+ );
116
+ const { startServer } = await importServer(result.projectLocal.entrypoint);
117
+ startServer().catch((error) => {
118
+ console.error("Fatal error:", error);
119
+ process.exit(1);
120
+ });
121
+ } else {
122
+ const localEntrypoint = result.projectLocal?.entrypoint ?? "<not resolved>";
123
+ console.error(
124
+ `[KIBI-MCP] Refusing stale kibi-mcp startup. project-local entrypoint: ${localEntrypoint}; running entrypoint: ${result.running.entrypoint}`,
125
+ );
126
+ process.exit(1);
127
+ }
128
+ } else {
129
+ const { startServer } = await importServer();
47
130
 
48
131
  if (process.env.KIBI_MCP_DEBUG) {
49
132
  const originalStdoutWrite = process.stdout.write.bind(process.stdout);
@@ -84,4 +167,6 @@ if (args.includes("-h") || args.includes("--help")) {
84
167
  console.error("Fatal error:", error);
85
168
  process.exit(1);
86
169
  });
170
+ }
171
+ }
87
172
  }
@@ -0,0 +1,110 @@
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 { existsSync, readFileSync, realpathSync } from "node:fs";
19
+ import { createRequire } from "node:module";
20
+ import path from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+ const PACKAGE_NAME = "kibi-mcp";
23
+ const FORBIDDEN_VERSION = "0.13.0";
24
+ function toEntrypointPath(entrypointUrl) {
25
+ if (entrypointUrl.startsWith("file:")) {
26
+ return fileURLToPath(entrypointUrl);
27
+ }
28
+ return path.resolve(entrypointUrl);
29
+ }
30
+ function readJson(filePath) {
31
+ return JSON.parse(readFileSync(filePath, "utf8"));
32
+ }
33
+ function nearestPackageJson(startPath) {
34
+ let current = path.dirname(startPath);
35
+ while (true) {
36
+ const candidate = path.join(current, "package.json");
37
+ if (existsSync(candidate)) {
38
+ return candidate;
39
+ }
40
+ const parent = path.dirname(current);
41
+ if (parent === current) {
42
+ throw new Error(`Unable to find package.json for ${startPath}`);
43
+ }
44
+ current = parent;
45
+ }
46
+ }
47
+ function packageInfoFromEntrypoint(entrypoint) {
48
+ const normalizedEntrypoint = path.resolve(entrypoint);
49
+ const packageJsonPath = nearestPackageJson(normalizedEntrypoint);
50
+ const packageJson = readJson(packageJsonPath);
51
+ if (packageJson.name && packageJson.name !== PACKAGE_NAME) {
52
+ throw new Error(`Resolved package ${packageJson.name} for ${normalizedEntrypoint}; expected ${PACKAGE_NAME}`);
53
+ }
54
+ return {
55
+ packageRoot: path.dirname(packageJsonPath),
56
+ version: packageJson.version ?? "unknown",
57
+ entrypoint: normalizedEntrypoint,
58
+ };
59
+ }
60
+ export function readRunningPackageInfo(entrypointUrl) {
61
+ return packageInfoFromEntrypoint(toEntrypointPath(entrypointUrl));
62
+ }
63
+ export function resolveProjectLocalMcp(cwd) {
64
+ try {
65
+ const projectRequire = createRequire(path.join(path.resolve(cwd), "package.json"));
66
+ const resolved = projectRequire.resolve(PACKAGE_NAME);
67
+ const entrypoint = realpathSync.native?.(resolved) ?? realpathSync(resolved);
68
+ return packageInfoFromEntrypoint(entrypoint);
69
+ }
70
+ catch (error) {
71
+ const code = error.code;
72
+ if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
73
+ return null;
74
+ }
75
+ return null;
76
+ }
77
+ }
78
+ export function compareMcpResolution(running, projectLocal) {
79
+ const diagnosticText = JSON.stringify({ running, projectLocal });
80
+ const forbiddenVersionObserved = diagnosticText.includes(FORBIDDEN_VERSION);
81
+ if (!projectLocal) {
82
+ return {
83
+ stale: false,
84
+ reason: "no project-local kibi-mcp resolved",
85
+ forbiddenVersionObserved,
86
+ };
87
+ }
88
+ if (running.version !== projectLocal.version) {
89
+ return {
90
+ stale: true,
91
+ reason: `version mismatch: running ${running.version}, project-local ${projectLocal.version}`,
92
+ forbiddenVersionObserved,
93
+ };
94
+ }
95
+ if (path.resolve(running.packageRoot) !== path.resolve(projectLocal.packageRoot)) {
96
+ return {
97
+ stale: true,
98
+ reason: `package root mismatch: running ${running.packageRoot}, project-local ${projectLocal.packageRoot}`,
99
+ forbiddenVersionObserved,
100
+ };
101
+ }
102
+ return {
103
+ stale: false,
104
+ reason: "running kibi-mcp matches project-local kibi-mcp",
105
+ forbiddenVersionObserved,
106
+ };
107
+ }
108
+ export function formatResolutionJson(result) {
109
+ return `${JSON.stringify(result, null, 2)}\n`;
110
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-mcp",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "dependencies": {
5
5
  "@modelcontextprotocol/sdk": "^1.26.0",
6
6
  "ajv": "^8.18.0",