memorix 1.8.2 → 1.8.4
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 +35 -0
- package/dist/cli/index.js +277 -39
- package/dist/index.js +268 -25
- package/dist/memcode-runtime/CHANGELOG.md +35 -0
- package/dist/sdk.js +25 -16
- 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-http.ts +2 -7
- package/src/cli/commands/serve.ts +26 -7
- package/src/server/mcp-compat.ts +18 -37
- package/src/server/mcp-discovery.ts +60 -0
- package/src/server/stdio-startup-gate.ts +263 -0
- package/src/server.ts +2 -5
package/dist/index.js
CHANGED
|
@@ -158117,43 +158117,64 @@ 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.4" : "1.0.1"
|
|
158125
|
+
};
|
|
158126
|
+
}
|
|
158127
|
+
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
158125
158128
|
return {
|
|
158126
158129
|
resultType: "complete",
|
|
158127
158130
|
supportedVersions: [
|
|
158128
|
-
MCP_MODERN_PROTOCOL_VERSION,
|
|
158129
158131
|
MCP_LEGACY_PROTOCOL_VERSION,
|
|
158130
158132
|
MCP_OLDEST_LEGACY_PROTOCOL_VERSION
|
|
158131
158133
|
],
|
|
158132
|
-
capabilities
|
|
158134
|
+
capabilities,
|
|
158133
158135
|
_meta: {
|
|
158134
158136
|
[MCP_SERVER_INFO_META_KEY]: { ...serverInfo }
|
|
158135
158137
|
},
|
|
158136
|
-
// v1 has no response-cache negotiation. A zero private TTL
|
|
158137
|
-
//
|
|
158138
|
+
// v1 has no response-cache negotiation. A zero private TTL prevents clients
|
|
158139
|
+
// from inventing a shared cache for an implementation that cannot invalidate it.
|
|
158138
158140
|
ttlMs: 0,
|
|
158139
158141
|
cacheScope: "private"
|
|
158140
158142
|
};
|
|
158141
158143
|
}
|
|
158144
|
+
function createMcpStartupDiscoverResult(serverInfo) {
|
|
158145
|
+
return createMcpDiscoverResult({ tools: { listChanged: true } }, serverInfo);
|
|
158146
|
+
}
|
|
158147
|
+
var MCP_LEGACY_PROTOCOL_VERSION, MCP_OLDEST_LEGACY_PROTOCOL_VERSION, MCP_SERVER_DISCOVER_METHOD, MCP_SERVER_INFO_META_KEY;
|
|
158148
|
+
var init_mcp_discovery = __esm({
|
|
158149
|
+
"src/server/mcp-discovery.ts"() {
|
|
158150
|
+
"use strict";
|
|
158151
|
+
init_esm_shims();
|
|
158152
|
+
MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
|
|
158153
|
+
MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
|
|
158154
|
+
MCP_SERVER_DISCOVER_METHOD = "server/discover";
|
|
158155
|
+
MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
|
|
158156
|
+
}
|
|
158157
|
+
});
|
|
158158
|
+
|
|
158159
|
+
// src/server/mcp-compat.ts
|
|
158160
|
+
import { RequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
158161
|
+
import { z as z625 } from "zod/v4";
|
|
158162
|
+
function buildMcpDiscoverResult(server, serverInfo) {
|
|
158163
|
+
const getCapabilities = server.server.getCapabilities.bind(server.server);
|
|
158164
|
+
return createMcpDiscoverResult(getCapabilities(), serverInfo);
|
|
158165
|
+
}
|
|
158142
158166
|
function registerMcpDiscovery(server, serverInfo) {
|
|
158143
158167
|
if (registeredServers.has(server)) return;
|
|
158144
158168
|
server.server.setRequestHandler(ServerDiscoverRequestSchema, async () => buildMcpDiscoverResult(server, serverInfo));
|
|
158145
158169
|
registeredServers.add(server);
|
|
158146
158170
|
}
|
|
158147
|
-
var
|
|
158171
|
+
var ServerDiscoverRequestSchema, registeredServers;
|
|
158148
158172
|
var init_mcp_compat = __esm({
|
|
158149
158173
|
"src/server/mcp-compat.ts"() {
|
|
158150
158174
|
"use strict";
|
|
158151
158175
|
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";
|
|
158176
|
+
init_mcp_discovery();
|
|
158177
|
+
init_mcp_discovery();
|
|
158157
158178
|
ServerDiscoverRequestSchema = RequestSchema.extend({
|
|
158158
158179
|
method: z625.literal(MCP_SERVER_DISCOVER_METHOD),
|
|
158159
158180
|
params: z625.looseObject({}).optional()
|
|
@@ -168447,10 +168468,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
168447
168468
|
isError: true
|
|
168448
168469
|
};
|
|
168449
168470
|
};
|
|
168450
|
-
const serverInfo =
|
|
168451
|
-
name: "memorix",
|
|
168452
|
-
version: true ? "1.8.2" : "1.0.1"
|
|
168453
|
-
};
|
|
168471
|
+
const serverInfo = getMcpServerInfo();
|
|
168454
168472
|
const server = existingServer ?? new McpServer(serverInfo);
|
|
168455
168473
|
registerMcpDiscovery(server, serverInfo);
|
|
168456
168474
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
@@ -172857,6 +172875,218 @@ var init_server2 = __esm({
|
|
|
172857
172875
|
}
|
|
172858
172876
|
});
|
|
172859
172877
|
|
|
172878
|
+
// src/server/stdio-startup-gate.ts
|
|
172879
|
+
import { PassThrough as PassThrough3 } from "stream";
|
|
172880
|
+
var STDIO_STARTUP_GATE_MAX_BUFFER_SIZE, StdioStartupGate;
|
|
172881
|
+
var init_stdio_startup_gate = __esm({
|
|
172882
|
+
"src/server/stdio-startup-gate.ts"() {
|
|
172883
|
+
"use strict";
|
|
172884
|
+
init_esm_shims();
|
|
172885
|
+
init_mcp_discovery();
|
|
172886
|
+
STDIO_STARTUP_GATE_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
|
|
172887
|
+
StdioStartupGate = class {
|
|
172888
|
+
input = new PassThrough3();
|
|
172889
|
+
stdin;
|
|
172890
|
+
stdout;
|
|
172891
|
+
serverInfo;
|
|
172892
|
+
maxBufferSize;
|
|
172893
|
+
onError;
|
|
172894
|
+
onEnd;
|
|
172895
|
+
queuedMessages = [];
|
|
172896
|
+
pendingInput = "";
|
|
172897
|
+
queuedBytes = 0;
|
|
172898
|
+
started = false;
|
|
172899
|
+
ready = false;
|
|
172900
|
+
stopped = false;
|
|
172901
|
+
stdinEnded = false;
|
|
172902
|
+
endNotified = false;
|
|
172903
|
+
onData = (chunk) => {
|
|
172904
|
+
if (this.stopped) return;
|
|
172905
|
+
if (this.ready) {
|
|
172906
|
+
if (Buffer.byteLength(chunk, "utf8") > this.maxBufferSize) {
|
|
172907
|
+
this.fail(new Error(`stdio input chunk exceeded ${this.maxBufferSize} bytes`));
|
|
172908
|
+
return;
|
|
172909
|
+
}
|
|
172910
|
+
this.input.write(chunk);
|
|
172911
|
+
return;
|
|
172912
|
+
}
|
|
172913
|
+
const text2 = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
172914
|
+
if (this.queuedBytes + Buffer.byteLength(this.pendingInput, "utf8") + Buffer.byteLength(text2, "utf8") > this.maxBufferSize) {
|
|
172915
|
+
this.fail(new Error(`stdio startup input exceeded ${this.maxBufferSize} bytes before a complete JSON-RPC line`));
|
|
172916
|
+
return;
|
|
172917
|
+
}
|
|
172918
|
+
this.pendingInput += text2;
|
|
172919
|
+
let newline = this.pendingInput.indexOf("\n");
|
|
172920
|
+
while (newline >= 0 && !this.stopped) {
|
|
172921
|
+
const line = this.pendingInput.slice(0, newline + 1);
|
|
172922
|
+
this.pendingInput = this.pendingInput.slice(newline + 1);
|
|
172923
|
+
this.handleLine(line);
|
|
172924
|
+
newline = this.pendingInput.indexOf("\n");
|
|
172925
|
+
}
|
|
172926
|
+
if (!this.stopped && Buffer.byteLength(this.pendingInput, "utf8") > this.maxBufferSize) {
|
|
172927
|
+
this.fail(new Error(`stdio startup input exceeded ${this.maxBufferSize} bytes`));
|
|
172928
|
+
}
|
|
172929
|
+
};
|
|
172930
|
+
onStdinError = (error) => {
|
|
172931
|
+
this.fail(error);
|
|
172932
|
+
};
|
|
172933
|
+
onStdinEnd = () => {
|
|
172934
|
+
this.stdinEnded = true;
|
|
172935
|
+
if (this.ready) this.finishInput();
|
|
172936
|
+
};
|
|
172937
|
+
constructor(options) {
|
|
172938
|
+
this.stdin = options.stdin;
|
|
172939
|
+
this.stdout = options.stdout;
|
|
172940
|
+
this.serverInfo = { ...options.serverInfo };
|
|
172941
|
+
this.maxBufferSize = options.maxBufferSize ?? STDIO_STARTUP_GATE_MAX_BUFFER_SIZE;
|
|
172942
|
+
this.onError = options.onError;
|
|
172943
|
+
this.onEnd = options.onEnd;
|
|
172944
|
+
}
|
|
172945
|
+
start() {
|
|
172946
|
+
if (this.started || this.stopped) return;
|
|
172947
|
+
this.started = true;
|
|
172948
|
+
this.stdin.on("data", this.onData);
|
|
172949
|
+
this.stdin.on("error", this.onStdinError);
|
|
172950
|
+
this.stdin.on("end", this.onStdinEnd);
|
|
172951
|
+
}
|
|
172952
|
+
/** Release queued messages only after StdioServerTransport has installed its listener. */
|
|
172953
|
+
markReady() {
|
|
172954
|
+
if (this.stopped || this.ready) return;
|
|
172955
|
+
this.ready = true;
|
|
172956
|
+
for (const message of this.queuedMessages) this.input.write(message);
|
|
172957
|
+
this.queuedMessages.length = 0;
|
|
172958
|
+
this.queuedBytes = 0;
|
|
172959
|
+
if (this.pendingInput) {
|
|
172960
|
+
this.input.write(this.pendingInput);
|
|
172961
|
+
this.pendingInput = "";
|
|
172962
|
+
}
|
|
172963
|
+
if (this.stdinEnded) this.finishInput();
|
|
172964
|
+
}
|
|
172965
|
+
stop() {
|
|
172966
|
+
if (this.stopped) return;
|
|
172967
|
+
this.stopped = true;
|
|
172968
|
+
this.stdin.off("data", this.onData);
|
|
172969
|
+
this.stdin.off("error", this.onStdinError);
|
|
172970
|
+
this.stdin.off("end", this.onStdinEnd);
|
|
172971
|
+
this.queuedMessages.length = 0;
|
|
172972
|
+
this.pendingInput = "";
|
|
172973
|
+
this.queuedBytes = 0;
|
|
172974
|
+
if (!this.input.destroyed) this.input.end();
|
|
172975
|
+
}
|
|
172976
|
+
handleLine(line) {
|
|
172977
|
+
const parsed = this.parseLine(line);
|
|
172978
|
+
if (!parsed) return;
|
|
172979
|
+
if ("error" in parsed) {
|
|
172980
|
+
this.writeJsonRpcError(null, parsed.error.code, parsed.error.message);
|
|
172981
|
+
return;
|
|
172982
|
+
}
|
|
172983
|
+
if (!this.ready && this.isDiscoveryRequest(parsed.message)) {
|
|
172984
|
+
const response = {
|
|
172985
|
+
jsonrpc: "2.0",
|
|
172986
|
+
id: parsed.message.id,
|
|
172987
|
+
result: createMcpStartupDiscoverResult(this.serverInfo)
|
|
172988
|
+
};
|
|
172989
|
+
try {
|
|
172990
|
+
this.stdout.write(`${JSON.stringify(response)}
|
|
172991
|
+
`);
|
|
172992
|
+
} catch (error) {
|
|
172993
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
172994
|
+
}
|
|
172995
|
+
return;
|
|
172996
|
+
}
|
|
172997
|
+
if (this.ready) {
|
|
172998
|
+
this.input.write(line);
|
|
172999
|
+
return;
|
|
173000
|
+
}
|
|
173001
|
+
const bytes = Buffer.byteLength(line, "utf8");
|
|
173002
|
+
if (this.queuedBytes + bytes > this.maxBufferSize) {
|
|
173003
|
+
this.fail(new Error(`stdio startup queue exceeded ${this.maxBufferSize} bytes`));
|
|
173004
|
+
return;
|
|
173005
|
+
}
|
|
173006
|
+
this.queuedMessages.push(line);
|
|
173007
|
+
this.queuedBytes += bytes;
|
|
173008
|
+
}
|
|
173009
|
+
parseLine(line) {
|
|
173010
|
+
const trimmed = line.endsWith("\n") ? line.slice(0, -1).replace(/\r$/, "") : line;
|
|
173011
|
+
if (!trimmed.trim()) return null;
|
|
173012
|
+
let value;
|
|
173013
|
+
try {
|
|
173014
|
+
value = JSON.parse(trimmed);
|
|
173015
|
+
} catch {
|
|
173016
|
+
return { error: { code: -32700, message: "Parse error" } };
|
|
173017
|
+
}
|
|
173018
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
173019
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173020
|
+
}
|
|
173021
|
+
const message = value;
|
|
173022
|
+
if (message.jsonrpc !== "2.0") {
|
|
173023
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173024
|
+
}
|
|
173025
|
+
const hasId = Object.prototype.hasOwnProperty.call(message, "id");
|
|
173026
|
+
const hasMethod = typeof message.method === "string";
|
|
173027
|
+
const hasResult = Object.prototype.hasOwnProperty.call(message, "result");
|
|
173028
|
+
const hasError = Object.prototype.hasOwnProperty.call(message, "error");
|
|
173029
|
+
if (hasMethod && (hasResult || hasError)) {
|
|
173030
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173031
|
+
}
|
|
173032
|
+
if (!hasMethod && !hasResult && !hasError) {
|
|
173033
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173034
|
+
}
|
|
173035
|
+
if (hasId && !this.isValidRequestId(message.id)) {
|
|
173036
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173037
|
+
}
|
|
173038
|
+
if (!hasMethod && !hasId) {
|
|
173039
|
+
return { error: { code: -32600, message: "Invalid Request" } };
|
|
173040
|
+
}
|
|
173041
|
+
return { message };
|
|
173042
|
+
}
|
|
173043
|
+
isDiscoveryRequest(message) {
|
|
173044
|
+
if (message.method !== MCP_SERVER_DISCOVER_METHOD) return false;
|
|
173045
|
+
if (!Object.prototype.hasOwnProperty.call(message, "id")) return false;
|
|
173046
|
+
const params = message.params;
|
|
173047
|
+
return params === void 0 || typeof params === "object" && params !== null && !Array.isArray(params);
|
|
173048
|
+
}
|
|
173049
|
+
isValidRequestId(id) {
|
|
173050
|
+
return id === null || typeof id === "string" || typeof id === "number" && Number.isFinite(id);
|
|
173051
|
+
}
|
|
173052
|
+
writeJsonRpcError(id, code, message) {
|
|
173053
|
+
try {
|
|
173054
|
+
this.stdout.write(`${JSON.stringify({
|
|
173055
|
+
jsonrpc: "2.0",
|
|
173056
|
+
id,
|
|
173057
|
+
error: { code, message }
|
|
173058
|
+
})}
|
|
173059
|
+
`);
|
|
173060
|
+
} catch (error) {
|
|
173061
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
173062
|
+
}
|
|
173063
|
+
}
|
|
173064
|
+
finishInput() {
|
|
173065
|
+
if (!this.ready || this.input.destroyed || this.input.writableEnded) return;
|
|
173066
|
+
this.input.end();
|
|
173067
|
+
this.input.once("end", () => this.notifyEnd());
|
|
173068
|
+
}
|
|
173069
|
+
notifyEnd() {
|
|
173070
|
+
if (this.endNotified) return;
|
|
173071
|
+
this.endNotified = true;
|
|
173072
|
+
this.onEnd?.();
|
|
173073
|
+
}
|
|
173074
|
+
fail(error) {
|
|
173075
|
+
if (this.stopped) return;
|
|
173076
|
+
this.stopped = true;
|
|
173077
|
+
this.stdin.off("data", this.onData);
|
|
173078
|
+
this.stdin.off("error", this.onStdinError);
|
|
173079
|
+
this.stdin.off("end", this.onStdinEnd);
|
|
173080
|
+
this.queuedMessages.length = 0;
|
|
173081
|
+
this.pendingInput = "";
|
|
173082
|
+
this.queuedBytes = 0;
|
|
173083
|
+
if (!this.input.destroyed) this.input.destroy();
|
|
173084
|
+
this.onError?.(error);
|
|
173085
|
+
}
|
|
173086
|
+
};
|
|
173087
|
+
}
|
|
173088
|
+
});
|
|
173089
|
+
|
|
172860
173090
|
// src/cli/mcp-root-path.ts
|
|
172861
173091
|
import path35 from "path";
|
|
172862
173092
|
function mcpFileUriToPath(uri, platform = process.platform) {
|
|
@@ -173001,7 +173231,7 @@ var init_serve_shared = __esm({
|
|
|
173001
173231
|
|
|
173002
173232
|
// src/cli/version.ts
|
|
173003
173233
|
function getCliVersion() {
|
|
173004
|
-
return true ? "1.8.
|
|
173234
|
+
return true ? "1.8.4" : pkg.version;
|
|
173005
173235
|
}
|
|
173006
173236
|
var init_version3 = __esm({
|
|
173007
173237
|
"src/cli/version.ts"() {
|
|
@@ -173231,6 +173461,8 @@ var init_serve = __esm({
|
|
|
173231
173461
|
"use strict";
|
|
173232
173462
|
init_esm_shims();
|
|
173233
173463
|
init_tool_profile();
|
|
173464
|
+
init_mcp_discovery();
|
|
173465
|
+
init_stdio_startup_gate();
|
|
173234
173466
|
init_mcp_root_path();
|
|
173235
173467
|
serve_default = defineCommand({
|
|
173236
173468
|
meta: {
|
|
@@ -173255,15 +173487,25 @@ var init_serve = __esm({
|
|
|
173255
173487
|
}
|
|
173256
173488
|
},
|
|
173257
173489
|
run: async ({ args }) => {
|
|
173490
|
+
const startupGate = new StdioStartupGate({
|
|
173491
|
+
stdin: process.stdin,
|
|
173492
|
+
stdout: process.stdout,
|
|
173493
|
+
serverInfo: getMcpServerInfo(),
|
|
173494
|
+
onError: (error) => {
|
|
173495
|
+
console.error(`[memorix] stdio startup gate error: ${error.message}`);
|
|
173496
|
+
process.exit(1);
|
|
173497
|
+
},
|
|
173498
|
+
onEnd: () => {
|
|
173499
|
+
console.error("[memorix] stdin closed \u2014 exiting");
|
|
173500
|
+
process.exitCode = 0;
|
|
173501
|
+
}
|
|
173502
|
+
});
|
|
173503
|
+
startupGate.start();
|
|
173258
173504
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
173259
173505
|
const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
|
|
173260
173506
|
const { detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
173261
173507
|
const { homedir: homedir27 } = await import("os");
|
|
173262
173508
|
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
173509
|
let safeCwd;
|
|
173268
173510
|
try {
|
|
173269
173511
|
safeCwd = process.cwd();
|
|
@@ -173298,8 +173540,9 @@ var init_serve = __esm({
|
|
|
173298
173540
|
const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: "micro" });
|
|
173299
173541
|
const serverOptions = detected ? { toolProfile, deferProjectRuntimeInit: true } : { allowUntrackedFallback: allowUntracked, deferProjectInitUntilBound: !allowUntracked, deferProjectRuntimeInit: true, toolProfile };
|
|
173300
173542
|
const { server, projectId, deferredInit, switchProject } = await createMemorixServer2(projectRoot, void 0, void 0, serverOptions);
|
|
173301
|
-
const transport = new StdioServerTransport();
|
|
173543
|
+
const transport = new StdioServerTransport(startupGate.input, process.stdout);
|
|
173302
173544
|
await server.connect(transport);
|
|
173545
|
+
startupGate.markReady();
|
|
173303
173546
|
console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
|
|
173304
173547
|
console.error(`[memorix] Project root: ${detected?.rootPath ?? projectRoot}`);
|
|
173305
173548
|
const tryRootsSwitch = async () => {
|
|
@@ -4,6 +4,41 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.8.4] - 2026-08-27
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Stateless HTTP MCP requests (#255)** -- clients such as Claude Code that
|
|
11
|
+
send the current protocol header on every request can list and call tools
|
|
12
|
+
without echoing a custom project handle. Durable handles are still created
|
|
13
|
+
on `initialize` and remain available when a client opts into them.
|
|
14
|
+
- **MCP protocol self-description (#257)** -- discovery and HTTP response
|
|
15
|
+
headers now advertise the legacy revisions implemented by the pinned SDK,
|
|
16
|
+
instead of claiming complete support for the newer 2026 revision.
|
|
17
|
+
- **MCP stdio smoke contract** -- the release smoke script now checks the
|
|
18
|
+
truthful advertised revision set after the compatibility fix.
|
|
19
|
+
|
|
20
|
+
### MCP Compatibility
|
|
21
|
+
- The 1.8.4 line supports legacy stateful MCP, versionless stdio tool calls,
|
|
22
|
+
pre-initialize discovery, and the narrow stateless HTTP compatibility path.
|
|
23
|
+
It does not claim full 2026 result metadata, Tasks, subscriptions, or list
|
|
24
|
+
caching support.
|
|
25
|
+
|
|
26
|
+
## [1.8.3] - 2026-08-25
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- **Stdio MCP startup gate** -- `memorix serve` now reads and bounds
|
|
30
|
+
newline-delimited JSON-RPC input before project initialization completes.
|
|
31
|
+
Early `server/discover` requests receive the complete discovery result, while
|
|
32
|
+
initialize, notifications, responses, and versionless tool requests replay
|
|
33
|
+
through the pinned SDK transport in input order without duplicate handling.
|
|
34
|
+
|
|
35
|
+
### MCP Compatibility
|
|
36
|
+
- **Stable cold-start discovery** -- real stdio CLI startup now supports
|
|
37
|
+
pre-initialize discovery within the startup window and preserves legacy
|
|
38
|
+
initialize plus versionless `tools/list`/`tools/call` behavior. Tasks,
|
|
39
|
+
subscriptions, list-response caching, and modern non-discovery result
|
|
40
|
+
metadata remain unsupported or partial.
|
|
41
|
+
|
|
7
42
|
## [1.8.2] - 2026-08-25
|
|
8
43
|
|
|
9
44
|
### Fixed
|
package/dist/sdk.js
CHANGED
|
@@ -167806,35 +167806,47 @@ 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
|
-
|
|
167809
|
+
|
|
167810
|
+
// src/server/mcp-discovery.ts
|
|
167811
|
+
init_esm_shims();
|
|
167810
167812
|
var MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
|
|
167811
167813
|
var MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
|
|
167812
167814
|
var MCP_SERVER_DISCOVER_METHOD = "server/discover";
|
|
167813
167815
|
var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
|
|
167814
|
-
|
|
167815
|
-
|
|
167816
|
-
|
|
167817
|
-
|
|
167818
|
-
|
|
167819
|
-
|
|
167820
|
-
|
|
167816
|
+
function getMcpServerInfo() {
|
|
167817
|
+
return {
|
|
167818
|
+
name: "memorix",
|
|
167819
|
+
version: true ? "1.8.4" : "1.0.1"
|
|
167820
|
+
};
|
|
167821
|
+
}
|
|
167822
|
+
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
167821
167823
|
return {
|
|
167822
167824
|
resultType: "complete",
|
|
167823
167825
|
supportedVersions: [
|
|
167824
|
-
MCP_MODERN_PROTOCOL_VERSION,
|
|
167825
167826
|
MCP_LEGACY_PROTOCOL_VERSION,
|
|
167826
167827
|
MCP_OLDEST_LEGACY_PROTOCOL_VERSION
|
|
167827
167828
|
],
|
|
167828
|
-
capabilities
|
|
167829
|
+
capabilities,
|
|
167829
167830
|
_meta: {
|
|
167830
167831
|
[MCP_SERVER_INFO_META_KEY]: { ...serverInfo }
|
|
167831
167832
|
},
|
|
167832
|
-
// v1 has no response-cache negotiation. A zero private TTL
|
|
167833
|
-
//
|
|
167833
|
+
// v1 has no response-cache negotiation. A zero private TTL prevents clients
|
|
167834
|
+
// from inventing a shared cache for an implementation that cannot invalidate it.
|
|
167834
167835
|
ttlMs: 0,
|
|
167835
167836
|
cacheScope: "private"
|
|
167836
167837
|
};
|
|
167837
167838
|
}
|
|
167839
|
+
|
|
167840
|
+
// src/server/mcp-compat.ts
|
|
167841
|
+
var ServerDiscoverRequestSchema = RequestSchema.extend({
|
|
167842
|
+
method: z625.literal(MCP_SERVER_DISCOVER_METHOD),
|
|
167843
|
+
params: z625.looseObject({}).optional()
|
|
167844
|
+
});
|
|
167845
|
+
var registeredServers = /* @__PURE__ */ new WeakSet();
|
|
167846
|
+
function buildMcpDiscoverResult(server, serverInfo) {
|
|
167847
|
+
const getCapabilities = server.server.getCapabilities.bind(server.server);
|
|
167848
|
+
return createMcpDiscoverResult(getCapabilities(), serverInfo);
|
|
167849
|
+
}
|
|
167838
167850
|
function registerMcpDiscovery(server, serverInfo) {
|
|
167839
167851
|
if (registeredServers.has(server)) return;
|
|
167840
167852
|
server.server.setRequestHandler(ServerDiscoverRequestSchema, async () => buildMcpDiscoverResult(server, serverInfo));
|
|
@@ -168238,10 +168250,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
168238
168250
|
isError: true
|
|
168239
168251
|
};
|
|
168240
168252
|
};
|
|
168241
|
-
const serverInfo =
|
|
168242
|
-
name: "memorix",
|
|
168243
|
-
version: true ? "1.8.2" : "1.0.1"
|
|
168244
|
-
};
|
|
168253
|
+
const serverInfo = getMcpServerInfo();
|
|
168245
168254
|
const server = existingServer ?? new McpServer(serverInfo);
|
|
168246
168255
|
registerMcpDiscovery(server, serverInfo);
|
|
168247
168256
|
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.4 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.4 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.4 release line** and package metadata
|
|
17
|
+
is kept at `1.8.4` 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.4 MCP 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.4**. 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.4 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.4",
|
|
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/",
|