@echomem/mcp 1.4.13 → 1.4.14

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.
Files changed (2) hide show
  1. package/dist/index.js +26 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -467,11 +467,11 @@ class EchoMemApiClient {
467
467
  getSessionId() {
468
468
  return this.sessionId;
469
469
  }
470
- async trackMcpAnalyticsEvent(eventType, eventProperties) {
470
+ async trackMcpAnalyticsEvent(eventType, eventProperties, insertId) {
471
471
  if (!this.hasToken())
472
472
  return;
473
473
  try {
474
- await this.axios.post("/api/extension/mcp/events", { eventType, eventProperties }, { timeout: 1500 });
474
+ await this.axios.post("/api/extension/mcp/events", { eventType, eventProperties, insertId }, { timeout: 1500 });
475
475
  }
476
476
  catch {
477
477
  // Remote analytics is best-effort and must never change MCP behavior.
@@ -575,6 +575,8 @@ class EchoMemApiClient {
575
575
  k: opts.limit,
576
576
  similarityThreshold: opts.threshold,
577
577
  timeFrameDays: opts.timeFrameDays,
578
+ requestId: opts.requestId,
579
+ sessionId: this.sessionId,
578
580
  });
579
581
  const data = enc.enabled ? await this.decryptResult(response.data, enc.key) : response.data;
580
582
  return retrievalOnly && isRecord(data)
@@ -584,7 +586,8 @@ class EchoMemApiClient {
584
586
  async searchMemoriesRetrieved(query, opts, enc) {
585
587
  const response = await this.axios.post("/api/extension/memories/deep-search/candidates", {
586
588
  query,
587
- requestId: randomUUID(),
589
+ requestId: opts.requestId,
590
+ logicalRequestId: opts.requestId,
588
591
  sessionId: this.sessionId,
589
592
  userTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
590
593
  });
@@ -645,9 +648,12 @@ class EchoMemApiClient {
645
648
  // never hits dg-web directly, so no new auth is needed and no other caller is
646
649
  // disrupted. NOTE: encrypted users still get server-side plaintext only; the
647
650
  // phase-1 + bridge-local-decrypt + phase-2 flow (spec §11) is the remaining work.
648
- async searchMemoriesTuned(query) {
651
+ async searchMemoriesTuned(query, requestId) {
649
652
  const response = await this.axios.post("/api/extension/memories/deep-search", {
650
653
  query,
654
+ requestId,
655
+ logicalRequestId: requestId,
656
+ sessionId: this.sessionId,
651
657
  userTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
652
658
  });
653
659
  const data = response.data ?? {};
@@ -682,27 +688,24 @@ class EchoMemApiClient {
682
688
  });
683
689
  return enc.enabled ? await this.decryptResult(response.data, enc.key) : response.data;
684
690
  }
685
- const searchOpts = { limit, threshold, timeFrameDays: parsed.timeFrameDays };
691
+ const searchOpts = {
692
+ limit,
693
+ threshold,
694
+ timeFrameDays: parsed.timeFrameDays,
695
+ requestId: randomUUID(),
696
+ };
686
697
  if (!parsed.includeAnswer) {
687
- try {
688
- return await this.searchMemoriesRetrieved(query, searchOpts, enc);
689
- }
690
- catch {
691
- return await this.searchMemoriesRaw(query, searchOpts, enc, true);
692
- }
698
+ return this.searchMemoriesRetrieved(query, searchOpts, enc);
693
699
  }
694
700
  // Encrypted account: the server can't synthesize over plaintext it doesn't hold, so use the raw
695
701
  // retriever (returns ciphertext) and decrypt locally — zero-knowledge preserved end-to-end.
696
702
  if (enc.enabled) {
697
703
  return await this.searchMemoriesRaw(query, searchOpts, enc);
698
704
  }
699
- // Unencrypted: prefer the tuned two-phase retriever; degrade gracefully to the untuned path.
700
- try {
701
- return await this.searchMemoriesTuned(query);
702
- }
703
- catch {
704
- return await this.searchMemoriesRaw(query, searchOpts, enc);
705
- }
705
+ // A synthesized answer is metered through the legacy route. Do not fall
706
+ // back to raw retrieval after a ledger outage: that would return a result
707
+ // without a durable usage fact.
708
+ return this.searchMemoriesTuned(query, searchOpts.requestId);
706
709
  }
707
710
  /** Decrypt the model-visible fields on a `{ memories: [...] }` response locally. */
708
711
  async decryptResult(data, key) {
@@ -971,6 +974,7 @@ class EchoMemMCPServer {
971
974
  ...triggerAnalyticsForTool(canonicalName, request.params.arguments),
972
975
  ...inputAnalyticsForTool(canonicalName, request.params.arguments),
973
976
  };
977
+ const analyticsCallId = randomUUID();
974
978
  // One event per call. Handlers enrich `rec` with tool-specific detail; we finalize + log in `finally`.
975
979
  const rec = {
976
980
  type: "tool_call",
@@ -1030,8 +1034,8 @@ class EchoMemMCPServer {
1030
1034
  if (!this.client.hasToken())
1031
1035
  throw new NoTokenError();
1032
1036
  if (canonicalName !== canonicalToolNames.save) {
1033
- void this.client.trackMcpAnalyticsEvent("[MCP] EchoMem Tool Called", analyticsBase);
1034
- void this.client.trackMcpAnalyticsEvent(toolEventName(canonicalName, "Called"), analyticsBase);
1037
+ void this.client.trackMcpAnalyticsEvent("[MCP] EchoMem Tool Called", analyticsBase, `${analyticsCallId}:generic-called`);
1038
+ void this.client.trackMcpAnalyticsEvent(toolEventName(canonicalName, "Called"), analyticsBase, `${analyticsCallId}:called`);
1035
1039
  }
1036
1040
  if (canonicalName !== canonicalToolNames.save && analyticsBase.trigger_message_available === true) {
1037
1041
  void this.client.trackMcpAnalyticsEvent("[MCP] EchoMem Triggered By User Turn", analyticsBase);
@@ -1126,8 +1130,8 @@ class EchoMemMCPServer {
1126
1130
  encrypted_user: rec.encrypted,
1127
1131
  extracted_memory_count: rec.memories_extracted,
1128
1132
  };
1129
- void this.client.trackMcpAnalyticsEvent(rec.ok ? "[MCP] EchoMem Tool Succeeded" : "[MCP] EchoMem Tool Failed", finalAnalytics);
1130
- void this.client.trackMcpAnalyticsEvent(toolEventName(canonicalName, rec.ok ? "Succeeded" : "Failed"), finalAnalytics);
1133
+ void this.client.trackMcpAnalyticsEvent(rec.ok ? "[MCP] EchoMem Tool Succeeded" : "[MCP] EchoMem Tool Failed", finalAnalytics, `${analyticsCallId}:generic-completed`);
1134
+ void this.client.trackMcpAnalyticsEvent(toolEventName(canonicalName, rec.ok ? "Succeeded" : "Failed"), finalAnalytics, `${analyticsCallId}:completed`);
1131
1135
  }
1132
1136
  }
1133
1137
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@echomem/mcp",
3
- "version": "1.4.13",
3
+ "version": "1.4.14",
4
4
  "description": "EchoMem MCP bridge: cloud-first memory tools, local context HUD, and the Agent Doctor workspace forensics report (cost ledger + 3D repo city)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",