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 CHANGED
@@ -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/cli/index.js CHANGED
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3232
3232
 
3233
3233
  // src/cli/version.ts
3234
3234
  function getCliVersion() {
3235
- return true ? "1.8.2" : pkg.version;
3235
+ return true ? "1.8.3" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -205764,6 +205764,259 @@ var init_tool_profile = __esm({
205764
205764
  }
205765
205765
  });
205766
205766
 
205767
+ // src/server/mcp-discovery.ts
205768
+ function getMcpServerInfo() {
205769
+ return {
205770
+ name: "memorix",
205771
+ version: true ? "1.8.3" : "1.0.1"
205772
+ };
205773
+ }
205774
+ function createMcpDiscoverResult(capabilities, serverInfo) {
205775
+ return {
205776
+ resultType: "complete",
205777
+ supportedVersions: [
205778
+ MCP_MODERN_PROTOCOL_VERSION,
205779
+ MCP_LEGACY_PROTOCOL_VERSION,
205780
+ MCP_OLDEST_LEGACY_PROTOCOL_VERSION
205781
+ ],
205782
+ capabilities,
205783
+ _meta: {
205784
+ [MCP_SERVER_INFO_META_KEY]: { ...serverInfo }
205785
+ },
205786
+ // v1 has no response-cache negotiation. A zero private TTL prevents clients
205787
+ // from inventing a shared cache for an implementation that cannot invalidate it.
205788
+ ttlMs: 0,
205789
+ cacheScope: "private"
205790
+ };
205791
+ }
205792
+ function createMcpStartupDiscoverResult(serverInfo) {
205793
+ return createMcpDiscoverResult({ tools: { listChanged: true } }, serverInfo);
205794
+ }
205795
+ var MCP_MODERN_PROTOCOL_VERSION, MCP_LEGACY_PROTOCOL_VERSION, MCP_OLDEST_LEGACY_PROTOCOL_VERSION, MCP_SERVER_DISCOVER_METHOD, MCP_SERVER_INFO_META_KEY;
205796
+ var init_mcp_discovery = __esm({
205797
+ "src/server/mcp-discovery.ts"() {
205798
+ "use strict";
205799
+ init_esm_shims();
205800
+ MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
205801
+ MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
205802
+ MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
205803
+ MCP_SERVER_DISCOVER_METHOD = "server/discover";
205804
+ MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
205805
+ }
205806
+ });
205807
+
205808
+ // src/server/stdio-startup-gate.ts
205809
+ import { PassThrough as PassThrough3 } from "stream";
205810
+ var STDIO_STARTUP_GATE_MAX_BUFFER_SIZE, StdioStartupGate;
205811
+ var init_stdio_startup_gate = __esm({
205812
+ "src/server/stdio-startup-gate.ts"() {
205813
+ "use strict";
205814
+ init_esm_shims();
205815
+ init_mcp_discovery();
205816
+ STDIO_STARTUP_GATE_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
205817
+ StdioStartupGate = class {
205818
+ input = new PassThrough3();
205819
+ stdin;
205820
+ stdout;
205821
+ serverInfo;
205822
+ maxBufferSize;
205823
+ onError;
205824
+ onEnd;
205825
+ queuedMessages = [];
205826
+ pendingInput = "";
205827
+ queuedBytes = 0;
205828
+ started = false;
205829
+ ready = false;
205830
+ stopped = false;
205831
+ stdinEnded = false;
205832
+ endNotified = false;
205833
+ onData = (chunk) => {
205834
+ if (this.stopped) return;
205835
+ if (this.ready) {
205836
+ if (Buffer.byteLength(chunk, "utf8") > this.maxBufferSize) {
205837
+ this.fail(new Error(`stdio input chunk exceeded ${this.maxBufferSize} bytes`));
205838
+ return;
205839
+ }
205840
+ this.input.write(chunk);
205841
+ return;
205842
+ }
205843
+ const text2 = typeof chunk === "string" ? chunk : chunk.toString("utf8");
205844
+ if (this.queuedBytes + Buffer.byteLength(this.pendingInput, "utf8") + Buffer.byteLength(text2, "utf8") > this.maxBufferSize) {
205845
+ this.fail(new Error(`stdio startup input exceeded ${this.maxBufferSize} bytes before a complete JSON-RPC line`));
205846
+ return;
205847
+ }
205848
+ this.pendingInput += text2;
205849
+ let newline = this.pendingInput.indexOf("\n");
205850
+ while (newline >= 0 && !this.stopped) {
205851
+ const line = this.pendingInput.slice(0, newline + 1);
205852
+ this.pendingInput = this.pendingInput.slice(newline + 1);
205853
+ this.handleLine(line);
205854
+ newline = this.pendingInput.indexOf("\n");
205855
+ }
205856
+ if (!this.stopped && Buffer.byteLength(this.pendingInput, "utf8") > this.maxBufferSize) {
205857
+ this.fail(new Error(`stdio startup input exceeded ${this.maxBufferSize} bytes`));
205858
+ }
205859
+ };
205860
+ onStdinError = (error40) => {
205861
+ this.fail(error40);
205862
+ };
205863
+ onStdinEnd = () => {
205864
+ this.stdinEnded = true;
205865
+ if (this.ready) this.finishInput();
205866
+ };
205867
+ constructor(options) {
205868
+ this.stdin = options.stdin;
205869
+ this.stdout = options.stdout;
205870
+ this.serverInfo = { ...options.serverInfo };
205871
+ this.maxBufferSize = options.maxBufferSize ?? STDIO_STARTUP_GATE_MAX_BUFFER_SIZE;
205872
+ this.onError = options.onError;
205873
+ this.onEnd = options.onEnd;
205874
+ }
205875
+ start() {
205876
+ if (this.started || this.stopped) return;
205877
+ this.started = true;
205878
+ this.stdin.on("data", this.onData);
205879
+ this.stdin.on("error", this.onStdinError);
205880
+ this.stdin.on("end", this.onStdinEnd);
205881
+ }
205882
+ /** Release queued messages only after StdioServerTransport has installed its listener. */
205883
+ markReady() {
205884
+ if (this.stopped || this.ready) return;
205885
+ this.ready = true;
205886
+ for (const message of this.queuedMessages) this.input.write(message);
205887
+ this.queuedMessages.length = 0;
205888
+ this.queuedBytes = 0;
205889
+ if (this.pendingInput) {
205890
+ this.input.write(this.pendingInput);
205891
+ this.pendingInput = "";
205892
+ }
205893
+ if (this.stdinEnded) this.finishInput();
205894
+ }
205895
+ stop() {
205896
+ if (this.stopped) return;
205897
+ this.stopped = true;
205898
+ this.stdin.off("data", this.onData);
205899
+ this.stdin.off("error", this.onStdinError);
205900
+ this.stdin.off("end", this.onStdinEnd);
205901
+ this.queuedMessages.length = 0;
205902
+ this.pendingInput = "";
205903
+ this.queuedBytes = 0;
205904
+ if (!this.input.destroyed) this.input.end();
205905
+ }
205906
+ handleLine(line) {
205907
+ const parsed = this.parseLine(line);
205908
+ if (!parsed) return;
205909
+ if ("error" in parsed) {
205910
+ this.writeJsonRpcError(null, parsed.error.code, parsed.error.message);
205911
+ return;
205912
+ }
205913
+ if (!this.ready && this.isDiscoveryRequest(parsed.message)) {
205914
+ const response = {
205915
+ jsonrpc: "2.0",
205916
+ id: parsed.message.id,
205917
+ result: createMcpStartupDiscoverResult(this.serverInfo)
205918
+ };
205919
+ try {
205920
+ this.stdout.write(`${JSON.stringify(response)}
205921
+ `);
205922
+ } catch (error40) {
205923
+ this.fail(error40 instanceof Error ? error40 : new Error(String(error40)));
205924
+ }
205925
+ return;
205926
+ }
205927
+ if (this.ready) {
205928
+ this.input.write(line);
205929
+ return;
205930
+ }
205931
+ const bytes = Buffer.byteLength(line, "utf8");
205932
+ if (this.queuedBytes + bytes > this.maxBufferSize) {
205933
+ this.fail(new Error(`stdio startup queue exceeded ${this.maxBufferSize} bytes`));
205934
+ return;
205935
+ }
205936
+ this.queuedMessages.push(line);
205937
+ this.queuedBytes += bytes;
205938
+ }
205939
+ parseLine(line) {
205940
+ const trimmed = line.endsWith("\n") ? line.slice(0, -1).replace(/\r$/, "") : line;
205941
+ if (!trimmed.trim()) return null;
205942
+ let value;
205943
+ try {
205944
+ value = JSON.parse(trimmed);
205945
+ } catch {
205946
+ return { error: { code: -32700, message: "Parse error" } };
205947
+ }
205948
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
205949
+ return { error: { code: -32600, message: "Invalid Request" } };
205950
+ }
205951
+ const message = value;
205952
+ if (message.jsonrpc !== "2.0") {
205953
+ return { error: { code: -32600, message: "Invalid Request" } };
205954
+ }
205955
+ const hasId = Object.prototype.hasOwnProperty.call(message, "id");
205956
+ const hasMethod = typeof message.method === "string";
205957
+ const hasResult = Object.prototype.hasOwnProperty.call(message, "result");
205958
+ const hasError = Object.prototype.hasOwnProperty.call(message, "error");
205959
+ if (hasMethod && (hasResult || hasError)) {
205960
+ return { error: { code: -32600, message: "Invalid Request" } };
205961
+ }
205962
+ if (!hasMethod && !hasResult && !hasError) {
205963
+ return { error: { code: -32600, message: "Invalid Request" } };
205964
+ }
205965
+ if (hasId && !this.isValidRequestId(message.id)) {
205966
+ return { error: { code: -32600, message: "Invalid Request" } };
205967
+ }
205968
+ if (!hasMethod && !hasId) {
205969
+ return { error: { code: -32600, message: "Invalid Request" } };
205970
+ }
205971
+ return { message };
205972
+ }
205973
+ isDiscoveryRequest(message) {
205974
+ if (message.method !== MCP_SERVER_DISCOVER_METHOD) return false;
205975
+ if (!Object.prototype.hasOwnProperty.call(message, "id")) return false;
205976
+ const params = message.params;
205977
+ return params === void 0 || typeof params === "object" && params !== null && !Array.isArray(params);
205978
+ }
205979
+ isValidRequestId(id) {
205980
+ return id === null || typeof id === "string" || typeof id === "number" && Number.isFinite(id);
205981
+ }
205982
+ writeJsonRpcError(id, code, message) {
205983
+ try {
205984
+ this.stdout.write(`${JSON.stringify({
205985
+ jsonrpc: "2.0",
205986
+ id,
205987
+ error: { code, message }
205988
+ })}
205989
+ `);
205990
+ } catch (error40) {
205991
+ this.fail(error40 instanceof Error ? error40 : new Error(String(error40)));
205992
+ }
205993
+ }
205994
+ finishInput() {
205995
+ if (!this.ready || this.input.destroyed || this.input.writableEnded) return;
205996
+ this.input.end();
205997
+ this.input.once("end", () => this.notifyEnd());
205998
+ }
205999
+ notifyEnd() {
206000
+ if (this.endNotified) return;
206001
+ this.endNotified = true;
206002
+ this.onEnd?.();
206003
+ }
206004
+ fail(error40) {
206005
+ if (this.stopped) return;
206006
+ this.stopped = true;
206007
+ this.stdin.off("data", this.onData);
206008
+ this.stdin.off("error", this.onStdinError);
206009
+ this.stdin.off("end", this.onStdinEnd);
206010
+ this.queuedMessages.length = 0;
206011
+ this.pendingInput = "";
206012
+ this.queuedBytes = 0;
206013
+ if (!this.input.destroyed) this.input.destroy();
206014
+ this.onError?.(error40);
206015
+ }
206016
+ };
206017
+ }
206018
+ });
206019
+
205767
206020
  // src/cli/mcp-root-path.ts
205768
206021
  import path37 from "path";
205769
206022
  function mcpFileUriToPath(uri, platform2 = process.platform) {
@@ -219212,40 +219465,22 @@ var init_request_context = __esm({
219212
219465
  // src/server/mcp-compat.ts
219213
219466
  function buildMcpDiscoverResult(server, serverInfo) {
219214
219467
  const getCapabilities = server.server.getCapabilities.bind(server.server);
219215
- return {
219216
- resultType: "complete",
219217
- supportedVersions: [
219218
- MCP_MODERN_PROTOCOL_VERSION,
219219
- MCP_LEGACY_PROTOCOL_VERSION,
219220
- MCP_OLDEST_LEGACY_PROTOCOL_VERSION
219221
- ],
219222
- capabilities: getCapabilities(),
219223
- _meta: {
219224
- [MCP_SERVER_INFO_META_KEY]: { ...serverInfo }
219225
- },
219226
- // v1 has no response-cache negotiation. A zero private TTL is the
219227
- // conservative result and prevents clients from inventing a shared cache.
219228
- ttlMs: 0,
219229
- cacheScope: "private"
219230
- };
219468
+ return createMcpDiscoverResult(getCapabilities(), serverInfo);
219231
219469
  }
219232
219470
  function registerMcpDiscovery(server, serverInfo) {
219233
219471
  if (registeredServers.has(server)) return;
219234
219472
  server.server.setRequestHandler(ServerDiscoverRequestSchema, async () => buildMcpDiscoverResult(server, serverInfo));
219235
219473
  registeredServers.add(server);
219236
219474
  }
219237
- 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;
219475
+ var ServerDiscoverRequestSchema, registeredServers;
219238
219476
  var init_mcp_compat = __esm({
219239
219477
  "src/server/mcp-compat.ts"() {
219240
219478
  "use strict";
219241
219479
  init_esm_shims();
219242
219480
  init_types8();
219243
219481
  init_v4();
219244
- MCP_MODERN_PROTOCOL_VERSION = "2026-07-28";
219245
- MCP_LEGACY_PROTOCOL_VERSION = "2025-11-25";
219246
- MCP_OLDEST_LEGACY_PROTOCOL_VERSION = "2024-11-05";
219247
- MCP_SERVER_DISCOVER_METHOD = "server/discover";
219248
- MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
219482
+ init_mcp_discovery();
219483
+ init_mcp_discovery();
219249
219484
  ServerDiscoverRequestSchema = RequestSchema.extend({
219250
219485
  method: external_exports.literal(MCP_SERVER_DISCOVER_METHOD),
219251
219486
  params: external_exports.looseObject({}).optional()
@@ -261633,10 +261868,7 @@ The path should point to a directory containing a .git folder.`
261633
261868
  isError: true
261634
261869
  };
261635
261870
  };
261636
- const serverInfo = {
261637
- name: "memorix",
261638
- version: true ? "1.8.2" : "1.0.1"
261639
- };
261871
+ const serverInfo = getMcpServerInfo();
261640
261872
  const server = existingServer ?? new McpServer(serverInfo);
261641
261873
  registerMcpDiscovery(server, serverInfo);
261642
261874
  const originalRegisterTool = server.registerTool.bind(server);
@@ -266372,6 +266604,8 @@ var init_serve = __esm({
266372
266604
  init_esm_shims();
266373
266605
  init_dist2();
266374
266606
  init_tool_profile();
266607
+ init_mcp_discovery();
266608
+ init_stdio_startup_gate();
266375
266609
  init_mcp_root_path();
266376
266610
  serve_default = defineCommand({
266377
266611
  meta: {
@@ -266396,15 +266630,25 @@ var init_serve = __esm({
266396
266630
  }
266397
266631
  },
266398
266632
  run: async ({ args }) => {
266633
+ const startupGate = new StdioStartupGate({
266634
+ stdin: process.stdin,
266635
+ stdout: process.stdout,
266636
+ serverInfo: getMcpServerInfo(),
266637
+ onError: (error40) => {
266638
+ console.error(`[memorix] stdio startup gate error: ${error40.message}`);
266639
+ process.exit(1);
266640
+ },
266641
+ onEnd: () => {
266642
+ console.error("[memorix] stdin closed \u2014 exiting");
266643
+ process.exitCode = 0;
266644
+ }
266645
+ });
266646
+ startupGate.start();
266399
266647
  const { StdioServerTransport: StdioServerTransport2 } = await Promise.resolve().then(() => (init_stdio2(), stdio_exports));
266400
266648
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
266401
266649
  const { detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
266402
266650
  const { homedir: homedir36 } = await import("os");
266403
266651
  const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
266404
- process.stdin.on("end", () => {
266405
- console.error("[memorix] stdin closed \u2014 exiting");
266406
- process.exit(0);
266407
- });
266408
266652
  let safeCwd;
266409
266653
  try {
266410
266654
  safeCwd = process.cwd();
@@ -266439,8 +266683,9 @@ var init_serve = __esm({
266439
266683
  const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: "micro" });
266440
266684
  const serverOptions = detected ? { toolProfile, deferProjectRuntimeInit: true } : { allowUntrackedFallback: allowUntracked, deferProjectInitUntilBound: !allowUntracked, deferProjectRuntimeInit: true, toolProfile };
266441
266685
  const { server, projectId, deferredInit, switchProject } = await createMemorixServer2(projectRoot, void 0, void 0, serverOptions);
266442
- const transport = new StdioServerTransport2();
266686
+ const transport = new StdioServerTransport2(startupGate.input, process.stdout);
266443
266687
  await server.connect(transport);
266688
+ startupGate.markReady();
266444
266689
  console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
266445
266690
  console.error(`[memorix] Project root: ${detected?.rootPath ?? projectRoot}`);
266446
266691
  const tryRootsSwitch = async () => {