memorix 1.8.2 → 1.8.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.
- package/CHANGELOG.md +16 -0
- package/dist/cli/index.js +277 -32
- package/dist/index.js +269 -24
- package/dist/memcode-runtime/CHANGELOG.md +16 -0
- package/dist/sdk.js +25 -14
- package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +5 -2
- package/docs/API_REFERENCE.md +9 -8
- package/docs/DEVELOPMENT.md +3 -2
- package/docs/README.md +12 -9
- package/package.json +2 -1
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/serve.ts +26 -7
- package/src/server/mcp-compat.ts +18 -37
- package/src/server/mcp-discovery.ts +61 -0
- package/src/server/stdio-startup-gate.ts +263 -0
- package/src/server.ts +2 -5
package/dist/index.js
CHANGED
|
@@ -158117,11 +158117,14 @@ var init_request_context = __esm({
|
|
|
158117
158117
|
}
|
|
158118
158118
|
});
|
|
158119
158119
|
|
|
158120
|
-
// src/server/mcp-
|
|
158121
|
-
|
|
158122
|
-
|
|
158123
|
-
|
|
158124
|
-
|
|
158120
|
+
// src/server/mcp-discovery.ts
|
|
158121
|
+
function getMcpServerInfo() {
|
|
158122
|
+
return {
|
|
158123
|
+
name: "memorix",
|
|
158124
|
+
version: true ? "1.8.3" : "1.0.1"
|
|
158125
|
+
};
|
|
158126
|
+
}
|
|
158127
|
+
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
158125
158128
|
return {
|
|
158126
158129
|
resultType: "complete",
|
|
158127
158130
|
supportedVersions: [
|
|
@@ -158129,31 +158132,51 @@ function buildMcpDiscoverResult(server, serverInfo) {
|
|
|
158129
158132
|
MCP_LEGACY_PROTOCOL_VERSION,
|
|
158130
158133
|
MCP_OLDEST_LEGACY_PROTOCOL_VERSION
|
|
158131
158134
|
],
|
|
158132
|
-
capabilities
|
|
158135
|
+
capabilities,
|
|
158133
158136
|
_meta: {
|
|
158134
158137
|
[MCP_SERVER_INFO_META_KEY]: { ...serverInfo }
|
|
158135
158138
|
},
|
|
158136
|
-
// v1 has no response-cache negotiation. A zero private TTL
|
|
158137
|
-
//
|
|
158139
|
+
// v1 has no response-cache negotiation. A zero private TTL prevents clients
|
|
158140
|
+
// from inventing a shared cache for an implementation that cannot invalidate it.
|
|
158138
158141
|
ttlMs: 0,
|
|
158139
158142
|
cacheScope: "private"
|
|
158140
158143
|
};
|
|
158141
158144
|
}
|
|
158145
|
+
function createMcpStartupDiscoverResult(serverInfo) {
|
|
158146
|
+
return createMcpDiscoverResult({ tools: { listChanged: true } }, serverInfo);
|
|
158147
|
+
}
|
|
158148
|
+
var MCP_MODERN_PROTOCOL_VERSION, MCP_LEGACY_PROTOCOL_VERSION, MCP_OLDEST_LEGACY_PROTOCOL_VERSION, MCP_SERVER_DISCOVER_METHOD, MCP_SERVER_INFO_META_KEY;
|
|
158149
|
+
var init_mcp_discovery = __esm({
|
|
158150
|
+
"src/server/mcp-discovery.ts"() {
|
|
158151
|
+
"use strict";
|
|
158152
|
+
init_esm_shims();
|
|
158153
|
+
MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
158154
|
+
MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
|
|
158155
|
+
MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
|
|
158156
|
+
MCP_SERVER_DISCOVER_METHOD = "server/discover";
|
|
158157
|
+
MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
|
|
158158
|
+
}
|
|
158159
|
+
});
|
|
158160
|
+
|
|
158161
|
+
// src/server/mcp-compat.ts
|
|
158162
|
+
import { RequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
158163
|
+
import { z as z625 } from "zod/v4";
|
|
158164
|
+
function buildMcpDiscoverResult(server, serverInfo) {
|
|
158165
|
+
const getCapabilities = server.server.getCapabilities.bind(server.server);
|
|
158166
|
+
return createMcpDiscoverResult(getCapabilities(), serverInfo);
|
|
158167
|
+
}
|
|
158142
158168
|
function registerMcpDiscovery(server, serverInfo) {
|
|
158143
158169
|
if (registeredServers.has(server)) return;
|
|
158144
158170
|
server.server.setRequestHandler(ServerDiscoverRequestSchema, async () => buildMcpDiscoverResult(server, serverInfo));
|
|
158145
158171
|
registeredServers.add(server);
|
|
158146
158172
|
}
|
|
158147
|
-
var
|
|
158173
|
+
var ServerDiscoverRequestSchema, registeredServers;
|
|
158148
158174
|
var init_mcp_compat = __esm({
|
|
158149
158175
|
"src/server/mcp-compat.ts"() {
|
|
158150
158176
|
"use strict";
|
|
158151
158177
|
init_esm_shims();
|
|
158152
|
-
|
|
158153
|
-
|
|
158154
|
-
MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
|
|
158155
|
-
MCP_SERVER_DISCOVER_METHOD = "server/discover";
|
|
158156
|
-
MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
|
|
158178
|
+
init_mcp_discovery();
|
|
158179
|
+
init_mcp_discovery();
|
|
158157
158180
|
ServerDiscoverRequestSchema = RequestSchema.extend({
|
|
158158
158181
|
method: z625.literal(MCP_SERVER_DISCOVER_METHOD),
|
|
158159
158182
|
params: z625.looseObject({}).optional()
|
|
@@ -168447,10 +168470,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
168447
168470
|
isError: true
|
|
168448
168471
|
};
|
|
168449
168472
|
};
|
|
168450
|
-
const serverInfo =
|
|
168451
|
-
name: "memorix",
|
|
168452
|
-
version: true ? "1.8.2" : "1.0.1"
|
|
168453
|
-
};
|
|
168473
|
+
const serverInfo = getMcpServerInfo();
|
|
168454
168474
|
const server = existingServer ?? new McpServer(serverInfo);
|
|
168455
168475
|
registerMcpDiscovery(server, serverInfo);
|
|
168456
168476
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
@@ -172857,6 +172877,218 @@ var init_server2 = __esm({
|
|
|
172857
172877
|
}
|
|
172858
172878
|
});
|
|
172859
172879
|
|
|
172880
|
+
// src/server/stdio-startup-gate.ts
|
|
172881
|
+
import { PassThrough as PassThrough3 } from "stream";
|
|
172882
|
+
var STDIO_STARTUP_GATE_MAX_BUFFER_SIZE, StdioStartupGate;
|
|
172883
|
+
var init_stdio_startup_gate = __esm({
|
|
172884
|
+
"src/server/stdio-startup-gate.ts"() {
|
|
172885
|
+
"use strict";
|
|
172886
|
+
init_esm_shims();
|
|
172887
|
+
init_mcp_discovery();
|
|
172888
|
+
STDIO_STARTUP_GATE_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
|
|
172889
|
+
StdioStartupGate = class {
|
|
172890
|
+
input = new PassThrough3();
|
|
172891
|
+
stdin;
|
|
172892
|
+
stdout;
|
|
172893
|
+
serverInfo;
|
|
172894
|
+
maxBufferSize;
|
|
172895
|
+
onError;
|
|
172896
|
+
onEnd;
|
|
172897
|
+
queuedMessages = [];
|
|
172898
|
+
pendingInput = "";
|
|
172899
|
+
queuedBytes = 0;
|
|
172900
|
+
started = false;
|
|
172901
|
+
ready = false;
|
|
172902
|
+
stopped = false;
|
|
172903
|
+
stdinEnded = false;
|
|
172904
|
+
endNotified = false;
|
|
172905
|
+
onData = (chunk) => {
|
|
172906
|
+
if (this.stopped) return;
|
|
172907
|
+
if (this.ready) {
|
|
172908
|
+
if (Buffer.byteLength(chunk, "utf8") > this.maxBufferSize) {
|
|
172909
|
+
this.fail(new Error(`stdio input chunk exceeded ${this.maxBufferSize} bytes`));
|
|
172910
|
+
return;
|
|
172911
|
+
}
|
|
172912
|
+
this.input.write(chunk);
|
|
172913
|
+
return;
|
|
172914
|
+
}
|
|
172915
|
+
const text2 = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
172916
|
+
if (this.queuedBytes + Buffer.byteLength(this.pendingInput, "utf8") + Buffer.byteLength(text2, "utf8") > this.maxBufferSize) {
|
|
172917
|
+
this.fail(new Error(`stdio startup input exceeded ${this.maxBufferSize} bytes before a complete JSON-RPC line`));
|
|
172918
|
+
return;
|
|
172919
|
+
}
|
|
172920
|
+
this.pendingInput += text2;
|
|
172921
|
+
let newline = this.pendingInput.indexOf("\n");
|
|
172922
|
+
while (newline >= 0 && !this.stopped) {
|
|
172923
|
+
const line = this.pendingInput.slice(0, newline + 1);
|
|
172924
|
+
this.pendingInput = this.pendingInput.slice(newline + 1);
|
|
172925
|
+
this.handleLine(line);
|
|
172926
|
+
newline = this.pendingInput.indexOf("\n");
|
|
172927
|
+
}
|
|
172928
|
+
if (!this.stopped && Buffer.byteLength(this.pendingInput, "utf8") > this.maxBufferSize) {
|
|
172929
|
+
this.fail(new Error(`stdio startup input exceeded ${this.maxBufferSize} bytes`));
|
|
172930
|
+
}
|
|
172931
|
+
};
|
|
172932
|
+
onStdinError = (error) => {
|
|
172933
|
+
this.fail(error);
|
|
172934
|
+
};
|
|
172935
|
+
onStdinEnd = () => {
|
|
172936
|
+
this.stdinEnded = true;
|
|
172937
|
+
if (this.ready) this.finishInput();
|
|
172938
|
+
};
|
|
172939
|
+
constructor(options) {
|
|
172940
|
+
this.stdin = options.stdin;
|
|
172941
|
+
this.stdout = options.stdout;
|
|
172942
|
+
this.serverInfo = { ...options.serverInfo };
|
|
172943
|
+
this.maxBufferSize = options.maxBufferSize ?? STDIO_STARTUP_GATE_MAX_BUFFER_SIZE;
|
|
172944
|
+
this.onError = options.onError;
|
|
172945
|
+
this.onEnd = options.onEnd;
|
|
172946
|
+
}
|
|
172947
|
+
start() {
|
|
172948
|
+
if (this.started || this.stopped) return;
|
|
172949
|
+
this.started = true;
|
|
172950
|
+
this.stdin.on("data", this.onData);
|
|
172951
|
+
this.stdin.on("error", this.onStdinError);
|
|
172952
|
+
this.stdin.on("end", this.onStdinEnd);
|
|
172953
|
+
}
|
|
172954
|
+
/** Release queued messages only after StdioServerTransport has installed its listener. */
|
|
172955
|
+
markReady() {
|
|
172956
|
+
if (this.stopped || this.ready) return;
|
|
172957
|
+
this.ready = true;
|
|
172958
|
+
for (const message of this.queuedMessages) this.input.write(message);
|
|
172959
|
+
this.queuedMessages.length = 0;
|
|
172960
|
+
this.queuedBytes = 0;
|
|
172961
|
+
if (this.pendingInput) {
|
|
172962
|
+
this.input.write(this.pendingInput);
|
|
172963
|
+
this.pendingInput = "";
|
|
172964
|
+
}
|
|
172965
|
+
if (this.stdinEnded) this.finishInput();
|
|
172966
|
+
}
|
|
172967
|
+
stop() {
|
|
172968
|
+
if (this.stopped) return;
|
|
172969
|
+
this.stopped = true;
|
|
172970
|
+
this.stdin.off("data", this.onData);
|
|
172971
|
+
this.stdin.off("error", this.onStdinError);
|
|
172972
|
+
this.stdin.off("end", this.onStdinEnd);
|
|
172973
|
+
this.queuedMessages.length = 0;
|
|
172974
|
+
this.pendingInput = "";
|
|
172975
|
+
this.queuedBytes = 0;
|
|
172976
|
+
if (!this.input.destroyed) this.input.end();
|
|
172977
|
+
}
|
|
172978
|
+
handleLine(line) {
|
|
172979
|
+
const parsed = this.parseLine(line);
|
|
172980
|
+
if (!parsed) return;
|
|
172981
|
+
if ("error" in parsed) {
|
|
172982
|
+
this.writeJsonRpcError(null, parsed.error.code, parsed.error.message);
|
|
172983
|
+
return;
|
|
172984
|
+
}
|
|
172985
|
+
if (!this.ready && this.isDiscoveryRequest(parsed.message)) {
|
|
172986
|
+
const response = {
|
|
172987
|
+
jsonrpc: "2.0",
|
|
172988
|
+
id: parsed.message.id,
|
|
172989
|
+
result: createMcpStartupDiscoverResult(this.serverInfo)
|
|
172990
|
+
};
|
|
172991
|
+
try {
|
|
172992
|
+
this.stdout.write(`${JSON.stringify(response)}
|
|
172993
|
+
`);
|
|
172994
|
+
} catch (error) {
|
|
172995
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
172996
|
+
}
|
|
172997
|
+
return;
|
|
172998
|
+
}
|
|
172999
|
+
if (this.ready) {
|
|
173000
|
+
this.input.write(line);
|
|
173001
|
+
return;
|
|
173002
|
+
}
|
|
173003
|
+
const bytes = Buffer.byteLength(line, "utf8");
|
|
173004
|
+
if (this.queuedBytes + bytes > this.maxBufferSize) {
|
|
173005
|
+
this.fail(new Error(`stdio startup queue exceeded ${this.maxBufferSize} bytes`));
|
|
173006
|
+
return;
|
|
173007
|
+
}
|
|
173008
|
+
this.queuedMessages.push(line);
|
|
173009
|
+
this.queuedBytes += bytes;
|
|
173010
|
+
}
|
|
173011
|
+
parseLine(line) {
|
|
173012
|
+
const trimmed = line.endsWith("\n") ? line.slice(0, -1).replace(/\r$/, "") : line;
|
|
173013
|
+
if (!trimmed.trim()) return null;
|
|
173014
|
+
let value;
|
|
173015
|
+
try {
|
|
173016
|
+
value = JSON.parse(trimmed);
|
|
173017
|
+
} catch {
|
|
173018
|
+
return { error: { code: -32700, message: "Parse error" } };
|
|
173019
|
+
}
|
|
173020
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
173021
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173022
|
+
}
|
|
173023
|
+
const message = value;
|
|
173024
|
+
if (message.jsonrpc !== "2.0") {
|
|
173025
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173026
|
+
}
|
|
173027
|
+
const hasId = Object.prototype.hasOwnProperty.call(message, "id");
|
|
173028
|
+
const hasMethod = typeof message.method === "string";
|
|
173029
|
+
const hasResult = Object.prototype.hasOwnProperty.call(message, "result");
|
|
173030
|
+
const hasError = Object.prototype.hasOwnProperty.call(message, "error");
|
|
173031
|
+
if (hasMethod && (hasResult || hasError)) {
|
|
173032
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173033
|
+
}
|
|
173034
|
+
if (!hasMethod && !hasResult && !hasError) {
|
|
173035
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173036
|
+
}
|
|
173037
|
+
if (hasId && !this.isValidRequestId(message.id)) {
|
|
173038
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173039
|
+
}
|
|
173040
|
+
if (!hasMethod && !hasId) {
|
|
173041
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173042
|
+
}
|
|
173043
|
+
return { message };
|
|
173044
|
+
}
|
|
173045
|
+
isDiscoveryRequest(message) {
|
|
173046
|
+
if (message.method !== MCP_SERVER_DISCOVER_METHOD) return false;
|
|
173047
|
+
if (!Object.prototype.hasOwnProperty.call(message, "id")) return false;
|
|
173048
|
+
const params = message.params;
|
|
173049
|
+
return params === void 0 || typeof params === "object" && params !== null && !Array.isArray(params);
|
|
173050
|
+
}
|
|
173051
|
+
isValidRequestId(id) {
|
|
173052
|
+
return id === null || typeof id === "string" || typeof id === "number" && Number.isFinite(id);
|
|
173053
|
+
}
|
|
173054
|
+
writeJsonRpcError(id, code, message) {
|
|
173055
|
+
try {
|
|
173056
|
+
this.stdout.write(`${JSON.stringify({
|
|
173057
|
+
jsonrpc: "2.0",
|
|
173058
|
+
id,
|
|
173059
|
+
error: { code, message }
|
|
173060
|
+
})}
|
|
173061
|
+
`);
|
|
173062
|
+
} catch (error) {
|
|
173063
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
173064
|
+
}
|
|
173065
|
+
}
|
|
173066
|
+
finishInput() {
|
|
173067
|
+
if (!this.ready || this.input.destroyed || this.input.writableEnded) return;
|
|
173068
|
+
this.input.end();
|
|
173069
|
+
this.input.once("end", () => this.notifyEnd());
|
|
173070
|
+
}
|
|
173071
|
+
notifyEnd() {
|
|
173072
|
+
if (this.endNotified) return;
|
|
173073
|
+
this.endNotified = true;
|
|
173074
|
+
this.onEnd?.();
|
|
173075
|
+
}
|
|
173076
|
+
fail(error) {
|
|
173077
|
+
if (this.stopped) return;
|
|
173078
|
+
this.stopped = true;
|
|
173079
|
+
this.stdin.off("data", this.onData);
|
|
173080
|
+
this.stdin.off("error", this.onStdinError);
|
|
173081
|
+
this.stdin.off("end", this.onStdinEnd);
|
|
173082
|
+
this.queuedMessages.length = 0;
|
|
173083
|
+
this.pendingInput = "";
|
|
173084
|
+
this.queuedBytes = 0;
|
|
173085
|
+
if (!this.input.destroyed) this.input.destroy();
|
|
173086
|
+
this.onError?.(error);
|
|
173087
|
+
}
|
|
173088
|
+
};
|
|
173089
|
+
}
|
|
173090
|
+
});
|
|
173091
|
+
|
|
172860
173092
|
// src/cli/mcp-root-path.ts
|
|
172861
173093
|
import path35 from "path";
|
|
172862
173094
|
function mcpFileUriToPath(uri, platform = process.platform) {
|
|
@@ -173001,7 +173233,7 @@ var init_serve_shared = __esm({
|
|
|
173001
173233
|
|
|
173002
173234
|
// src/cli/version.ts
|
|
173003
173235
|
function getCliVersion() {
|
|
173004
|
-
return true ? "1.8.
|
|
173236
|
+
return true ? "1.8.3" : pkg.version;
|
|
173005
173237
|
}
|
|
173006
173238
|
var init_version3 = __esm({
|
|
173007
173239
|
"src/cli/version.ts"() {
|
|
@@ -173231,6 +173463,8 @@ var init_serve = __esm({
|
|
|
173231
173463
|
"use strict";
|
|
173232
173464
|
init_esm_shims();
|
|
173233
173465
|
init_tool_profile();
|
|
173466
|
+
init_mcp_discovery();
|
|
173467
|
+
init_stdio_startup_gate();
|
|
173234
173468
|
init_mcp_root_path();
|
|
173235
173469
|
serve_default = defineCommand({
|
|
173236
173470
|
meta: {
|
|
@@ -173255,15 +173489,25 @@ var init_serve = __esm({
|
|
|
173255
173489
|
}
|
|
173256
173490
|
},
|
|
173257
173491
|
run: async ({ args }) => {
|
|
173492
|
+
const startupGate = new StdioStartupGate({
|
|
173493
|
+
stdin: process.stdin,
|
|
173494
|
+
stdout: process.stdout,
|
|
173495
|
+
serverInfo: getMcpServerInfo(),
|
|
173496
|
+
onError: (error) => {
|
|
173497
|
+
console.error(`[memorix] stdio startup gate error: ${error.message}`);
|
|
173498
|
+
process.exit(1);
|
|
173499
|
+
},
|
|
173500
|
+
onEnd: () => {
|
|
173501
|
+
console.error("[memorix] stdin closed \u2014 exiting");
|
|
173502
|
+
process.exitCode = 0;
|
|
173503
|
+
}
|
|
173504
|
+
});
|
|
173505
|
+
startupGate.start();
|
|
173258
173506
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
173259
173507
|
const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
|
|
173260
173508
|
const { detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
173261
173509
|
const { homedir: homedir27 } = await import("os");
|
|
173262
173510
|
const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
|
|
173263
|
-
process.stdin.on("end", () => {
|
|
173264
|
-
console.error("[memorix] stdin closed \u2014 exiting");
|
|
173265
|
-
process.exit(0);
|
|
173266
|
-
});
|
|
173267
173511
|
let safeCwd;
|
|
173268
173512
|
try {
|
|
173269
173513
|
safeCwd = process.cwd();
|
|
@@ -173298,8 +173542,9 @@ var init_serve = __esm({
|
|
|
173298
173542
|
const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: "micro" });
|
|
173299
173543
|
const serverOptions = detected ? { toolProfile, deferProjectRuntimeInit: true } : { allowUntrackedFallback: allowUntracked, deferProjectInitUntilBound: !allowUntracked, deferProjectRuntimeInit: true, toolProfile };
|
|
173300
173544
|
const { server, projectId, deferredInit, switchProject } = await createMemorixServer2(projectRoot, void 0, void 0, serverOptions);
|
|
173301
|
-
const transport = new StdioServerTransport();
|
|
173545
|
+
const transport = new StdioServerTransport(startupGate.input, process.stdout);
|
|
173302
173546
|
await server.connect(transport);
|
|
173547
|
+
startupGate.markReady();
|
|
173303
173548
|
console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
|
|
173304
173549
|
console.error(`[memorix] Project root: ${detected?.rootPath ?? projectRoot}`);
|
|
173305
173550
|
const tryRootsSwitch = async () => {
|
|
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.8.3] - 2026-08-25
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Stdio MCP startup gate** -- `memorix serve` now reads and bounds
|
|
11
|
+
newline-delimited JSON-RPC input before project initialization completes.
|
|
12
|
+
Early `server/discover` requests receive the complete discovery result, while
|
|
13
|
+
initialize, notifications, responses, and versionless tool requests replay
|
|
14
|
+
through the pinned SDK transport in input order without duplicate handling.
|
|
15
|
+
|
|
16
|
+
### MCP Compatibility
|
|
17
|
+
- **Stable cold-start discovery** -- real stdio CLI startup now supports
|
|
18
|
+
pre-initialize discovery within the startup window and preserves legacy
|
|
19
|
+
initialize plus versionless `tools/list`/`tools/call` behavior. Tasks,
|
|
20
|
+
subscriptions, list-response caching, and modern non-discovery result
|
|
21
|
+
metadata remain unsupported or partial.
|
|
22
|
+
|
|
7
23
|
## [1.8.2] - 2026-08-25
|
|
8
24
|
|
|
9
25
|
### Fixed
|
package/dist/sdk.js
CHANGED
|
@@ -167806,18 +167806,21 @@ function createProjectBindingController(projectRoot) {
|
|
|
167806
167806
|
init_esm_shims();
|
|
167807
167807
|
import { RequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
167808
167808
|
import { z as z625 } from "zod/v4";
|
|
167809
|
+
|
|
167810
|
+
// src/server/mcp-discovery.ts
|
|
167811
|
+
init_esm_shims();
|
|
167809
167812
|
var MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
167810
167813
|
var MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
|
|
167811
167814
|
var MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
|
|
167812
167815
|
var MCP_SERVER_DISCOVER_METHOD = "server/discover";
|
|
167813
167816
|
var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
|
|
167814
|
-
|
|
167815
|
-
|
|
167816
|
-
|
|
167817
|
-
|
|
167818
|
-
|
|
167819
|
-
|
|
167820
|
-
|
|
167817
|
+
function getMcpServerInfo() {
|
|
167818
|
+
return {
|
|
167819
|
+
name: "memorix",
|
|
167820
|
+
version: true ? "1.8.3" : "1.0.1"
|
|
167821
|
+
};
|
|
167822
|
+
}
|
|
167823
|
+
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
167821
167824
|
return {
|
|
167822
167825
|
resultType: "complete",
|
|
167823
167826
|
supportedVersions: [
|
|
@@ -167825,16 +167828,27 @@ function buildMcpDiscoverResult(server, serverInfo) {
|
|
|
167825
167828
|
MCP_LEGACY_PROTOCOL_VERSION,
|
|
167826
167829
|
MCP_OLDEST_LEGACY_PROTOCOL_VERSION
|
|
167827
167830
|
],
|
|
167828
|
-
capabilities
|
|
167831
|
+
capabilities,
|
|
167829
167832
|
_meta: {
|
|
167830
167833
|
[MCP_SERVER_INFO_META_KEY]: { ...serverInfo }
|
|
167831
167834
|
},
|
|
167832
|
-
// v1 has no response-cache negotiation. A zero private TTL
|
|
167833
|
-
//
|
|
167835
|
+
// v1 has no response-cache negotiation. A zero private TTL prevents clients
|
|
167836
|
+
// from inventing a shared cache for an implementation that cannot invalidate it.
|
|
167834
167837
|
ttlMs: 0,
|
|
167835
167838
|
cacheScope: "private"
|
|
167836
167839
|
};
|
|
167837
167840
|
}
|
|
167841
|
+
|
|
167842
|
+
// src/server/mcp-compat.ts
|
|
167843
|
+
var ServerDiscoverRequestSchema = RequestSchema.extend({
|
|
167844
|
+
method: z625.literal(MCP_SERVER_DISCOVER_METHOD),
|
|
167845
|
+
params: z625.looseObject({}).optional()
|
|
167846
|
+
});
|
|
167847
|
+
var registeredServers = /* @__PURE__ */ new WeakSet();
|
|
167848
|
+
function buildMcpDiscoverResult(server, serverInfo) {
|
|
167849
|
+
const getCapabilities = server.server.getCapabilities.bind(server.server);
|
|
167850
|
+
return createMcpDiscoverResult(getCapabilities(), serverInfo);
|
|
167851
|
+
}
|
|
167838
167852
|
function registerMcpDiscovery(server, serverInfo) {
|
|
167839
167853
|
if (registeredServers.has(server)) return;
|
|
167840
167854
|
server.server.setRequestHandler(ServerDiscoverRequestSchema, async () => buildMcpDiscoverResult(server, serverInfo));
|
|
@@ -168238,10 +168252,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
168238
168252
|
isError: true
|
|
168239
168253
|
};
|
|
168240
168254
|
};
|
|
168241
|
-
const serverInfo =
|
|
168242
|
-
name: "memorix",
|
|
168243
|
-
version: true ? "1.8.2" : "1.0.1"
|
|
168244
|
-
};
|
|
168255
|
+
const serverInfo = getMcpServerInfo();
|
|
168245
168256
|
const server = existingServer ?? new McpServer(serverInfo);
|
|
168246
168257
|
registerMcpDiscovery(server, serverInfo);
|
|
168247
168258
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
@@ -18,12 +18,15 @@ Status: accepted implementation contract for Memorix 1.8.0.
|
|
|
18
18
|
## Compatibility Contract
|
|
19
19
|
|
|
20
20
|
Memorix preserves the legacy 2025-era initialize contract, including stdio and
|
|
21
|
-
stateful Streamable HTTP with `Mcp-Session-Id`. Memorix 1.8.
|
|
21
|
+
stateful Streamable HTTP with `Mcp-Session-Id`. Memorix 1.8.3 also registers a
|
|
22
22
|
low-level `server/discover` compatibility adapter for the 2026-07-28 request.
|
|
23
23
|
Its `DiscoverResult` is complete for the required identity, versions,
|
|
24
24
|
capabilities, and private zero-TTL cache hint. Versionless `tools/list` and
|
|
25
25
|
`tools/call` are accepted by the pinned v1 SDK and retain its legacy result
|
|
26
|
-
envelopes.
|
|
26
|
+
envelopes. The stdio startup gate reads bounded newline-delimited input before
|
|
27
|
+
project initialization, answers discovery immediately, and replays all other
|
|
28
|
+
messages in order; EOF is deferred until the queued input is released. HTTP
|
|
29
|
+
stateless project handles remain a compatibility adapter: they
|
|
27
30
|
own project routing, but do not turn the v1 SDK into a complete 2026 runtime.
|
|
28
31
|
Tasks, subscriptions, and list-response caching are unsupported and are not
|
|
29
32
|
advertised or simulated.
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -147,14 +147,15 @@ preserved. A memory without explicit feedback keeps the legacy relevance and
|
|
|
147
147
|
retention score. The first feedback event opts that candidate into the
|
|
148
148
|
feedback projection.
|
|
149
149
|
|
|
150
|
-
###
|
|
151
|
-
|
|
152
|
-
Legacy clients use stateful Streamable HTTP with `Mcp-Session-Id
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
`server/discover` request before `initialize
|
|
156
|
-
|
|
157
|
-
SDK's legacy result envelopes.
|
|
150
|
+
### MCP Compatibility
|
|
151
|
+
|
|
152
|
+
Legacy clients use stateful Streamable HTTP with `Mcp-Session-Id` and the
|
|
153
|
+
2025-era `initialize` handshake. The 1.8.3 stdio server starts a bounded
|
|
154
|
+
newline-delimited JSON-RPC gate before project initialization: it answers the
|
|
155
|
+
2026-07-28 `server/discover` request before `initialize`, then replays other
|
|
156
|
+
requests, notifications, and responses in order into the pinned SDK transport.
|
|
157
|
+
Versionless `tools/list` and `tools/call` use the SDK's legacy result envelopes.
|
|
158
|
+
HTTP stateless clients may use a durable
|
|
158
159
|
`Mcp-Project-Handle`; `/protocol-diagnostics` labels this as compatibility-only.
|
|
159
160
|
Modern result metadata is discovery-only with a private zero-TTL hint. Tasks,
|
|
160
161
|
subscriptions, and list-response caching are unsupported. When MCP discovery
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -13,8 +13,8 @@ Memorix is a TypeScript project built around:
|
|
|
13
13
|
|
|
14
14
|
## Current Development Baseline
|
|
15
15
|
|
|
16
|
-
The current release work targets the **1.8.
|
|
17
|
-
is kept at `1.8.
|
|
16
|
+
The current release work targets the **1.8.3 release line** and package metadata
|
|
17
|
+
is kept at `1.8.3` for this release commit.
|
|
18
18
|
|
|
19
19
|
Contributors should assume the following areas are part of the current release line:
|
|
20
20
|
|
|
@@ -96,6 +96,7 @@ npm run test:watch
|
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
98
|
memorix serve
|
|
99
|
+
node scripts/mcp-stdio-smoke.mjs
|
|
99
100
|
memorix background start
|
|
100
101
|
memorix status
|
|
101
102
|
```
|
package/docs/README.md
CHANGED
|
@@ -94,7 +94,7 @@ The public docs are organized by user intent:
|
|
|
94
94
|
| 1.2 honest Lite and optional semantic CodeGraph provider contract | [1.2 Provider Quality](1.2.0-PROVIDER-QUALITY.md) |
|
|
95
95
|
| 1.3 long-term memory model, source boundary, and lifecycle | [1.3 Memory Architecture](1.3-MEMORY-ARCHITECTURE.md) |
|
|
96
96
|
| Active context-control work | [1.2.2 Memory Control Plane](1.2.2-MEMORY-CONTROL-PLANE.md) |
|
|
97
|
-
| 1.8.
|
|
97
|
+
| 1.8.3 stdio discovery startup and compatibility acceptance contract | [1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md) |
|
|
98
98
|
| Historical cloud sync and multi-agent research | [CLOUD_SYNC_AND_MULTI_AGENT_RESEARCH.md](CLOUD_SYNC_AND_MULTI_AGENT_RESEARCH.md) |
|
|
99
99
|
| Known issues and old roadmap notes | [KNOWN_ISSUES_AND_ROADMAP.md](KNOWN_ISSUES_AND_ROADMAP.md) |
|
|
100
100
|
|
|
@@ -122,7 +122,7 @@ Historical/deep-reference documents may describe older designs. If they conflict
|
|
|
122
122
|
|
|
123
123
|
## Current Product Line
|
|
124
124
|
|
|
125
|
-
The current product line is **1.8.
|
|
125
|
+
The current product line is **1.8.3**. The authoritative acceptance contract is
|
|
126
126
|
[1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md).
|
|
127
127
|
The current baseline has:
|
|
128
128
|
|
|
@@ -141,10 +141,13 @@ The current baseline has:
|
|
|
141
141
|
- `~/.memorix/config.toml` and project `memorix.toml` are the user-facing configuration model
|
|
142
142
|
- legacy `memorix.yml`, `.env`, and `config.json` files are still read for compatibility, but new setups should use TOML
|
|
143
143
|
|
|
144
|
-
For MCP compatibility, 1.8.
|
|
145
|
-
stateful Streamable HTTP behavior
|
|
146
|
-
`server/discover`
|
|
147
|
-
|
|
148
|
-
the
|
|
149
|
-
|
|
150
|
-
|
|
144
|
+
For MCP compatibility, 1.8.3 preserves legacy 2025-era initialize and
|
|
145
|
+
stateful Streamable HTTP behavior. `memorix serve` starts a bounded stdio
|
|
146
|
+
startup gate: pre-initialize 2026-07-28 `server/discover` is answered with a
|
|
147
|
+
complete discovery result, and other early JSON-RPC messages replay in order
|
|
148
|
+
through the pinned SDK transport. Versionless `tools/list` and `tools/call`
|
|
149
|
+
continue through the SDK's legacy result envelopes. Modern result metadata is
|
|
150
|
+
limited to the discovery response (`ttlMs: 0`, private cache scope); Tasks,
|
|
151
|
+
subscriptions, and list-response caching are unsupported. Stateless HTTP
|
|
152
|
+
project handles are a compatibility adapter, not a claim of complete 2026
|
|
153
|
+
conformance.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.3",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"update-models": "npm --workspace @memorix/ai run update-models",
|
|
63
63
|
"start": "node dist/index.js",
|
|
64
64
|
"test": "vitest run --no-file-parallelism --maxWorkers=1 --minWorkers=1",
|
|
65
|
+
"test:mcp-stdio-smoke": "node scripts/mcp-stdio-smoke.mjs",
|
|
65
66
|
"test:watch": "vitest",
|
|
66
67
|
"test:llm-live": "MEMORIX_RUN_LIVE_LLM_TESTS=1 vitest run tests/integration/formation-llm-quality.test.ts",
|
|
67
68
|
"test:e2e": "vitest run tests/e2e/",
|