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/dist/index.js CHANGED
@@ -158117,43 +158117,64 @@ var init_request_context = __esm({
158117
158117
  }
158118
158118
  });
158119
158119
 
158120
- // src/server/mcp-compat.ts
158121
- import { RequestSchema } from "@modelcontextprotocol/sdk/types.js";
158122
- import { z as z625 } from "zod/v4";
158123
- function buildMcpDiscoverResult(server, serverInfo) {
158124
- const getCapabilities = server.server.getCapabilities.bind(server.server);
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: getCapabilities(),
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 is the
158137
- // conservative result and prevents clients from inventing a shared cache.
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 MCP_MODERN_PROTOCOL_VERSION, MCP_LEGACY_PROTOCOL_VERSION, MCP_OLDEST_LEGACY_PROTOCOL_VERSION, MCP_SERVER_DISCOVER_METHOD, MCP_SERVER_INFO_META_KEY, ServerDiscoverRequestSchema, registeredServers;
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
- MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
158153
- MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
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.2" : pkg.version;
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
- var MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
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
- var ServerDiscoverRequestSchema = RequestSchema.extend({
167815
- method: z625.literal(MCP_SERVER_DISCOVER_METHOD),
167816
- params: z625.looseObject({}).optional()
167817
- });
167818
- var registeredServers = /* @__PURE__ */ new WeakSet();
167819
- function buildMcpDiscoverResult(server, serverInfo) {
167820
- const getCapabilities = server.server.getCapabilities.bind(server.server);
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: getCapabilities(),
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 is the
167833
- // conservative result and prevents clients from inventing a shared cache.
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.2 also registers a
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. HTTP stateless project handles remain a compatibility adapter: they
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.
@@ -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
- ### HTTP MCP Compatibility
151
-
152
- Legacy clients use stateful Streamable HTTP with `Mcp-Session-Id`. Current
153
- Legacy clients use the 2025-era `initialize` handshake and stateful
154
- `Mcp-Session-Id`. The stdio server also accepts the 2026-07-28
155
- `server/discover` request before `initialize` and returns the required
156
- `DiscoverResult`. Versionless `tools/list` and `tools/call` use the pinned
157
- SDK's legacy result envelopes. HTTP stateless clients may use a durable
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
@@ -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.2 release line** and package metadata
17
- is kept at `1.8.2` for this release commit.
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.2 context-control and compatibility acceptance contract | [1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md) |
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.2**. The authoritative acceptance contract is
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.2 preserves legacy 2025-era initialize and
145
- stateful Streamable HTTP behavior, and adds a tested 2026-07-28
146
- `server/discover` adapter. Versionless `tools/list` and `tools/call` continue
147
- through the SDK's legacy result envelopes. Modern result metadata is limited to
148
- the discovery response (`ttlMs: 0`, private cache scope); Tasks, subscriptions,
149
- and list-response caching are unsupported. Stateless HTTP project handles are
150
- a compatibility adapter, not a claim of complete 2026 conformance.
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.2",
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/",
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.8.2"
3
+ "version": "1.8.4"
4
4
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "memorix",
4
- "version": "1.8.2",
4
+ "version": "1.8.4",
5
5
  "description": "Shared workspace memory for Claude Code and other AI coding agents.",
6
6
  "author": {
7
7
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "Local-first project memory for CodeBuddy Code and other coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "Shared workspace memory for Codex and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "Shared local workspace memory for GitHub Copilot CLI and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "mcpServers": {
5
5
  "memorix": {
6
6
  "command": "memorix",