@echomem/mcp 1.4.12 → 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 -21
  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,6 +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,
589
+ requestId: opts.requestId,
590
+ logicalRequestId: opts.requestId,
587
591
  sessionId: this.sessionId,
588
592
  userTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
589
593
  });
@@ -644,9 +648,12 @@ class EchoMemApiClient {
644
648
  // never hits dg-web directly, so no new auth is needed and no other caller is
645
649
  // disrupted. NOTE: encrypted users still get server-side plaintext only; the
646
650
  // phase-1 + bridge-local-decrypt + phase-2 flow (spec §11) is the remaining work.
647
- async searchMemoriesTuned(query) {
651
+ async searchMemoriesTuned(query, requestId) {
648
652
  const response = await this.axios.post("/api/extension/memories/deep-search", {
649
653
  query,
654
+ requestId,
655
+ logicalRequestId: requestId,
656
+ sessionId: this.sessionId,
650
657
  userTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
651
658
  });
652
659
  const data = response.data ?? {};
@@ -681,27 +688,24 @@ class EchoMemApiClient {
681
688
  });
682
689
  return enc.enabled ? await this.decryptResult(response.data, enc.key) : response.data;
683
690
  }
684
- const searchOpts = { limit, threshold, timeFrameDays: parsed.timeFrameDays };
691
+ const searchOpts = {
692
+ limit,
693
+ threshold,
694
+ timeFrameDays: parsed.timeFrameDays,
695
+ requestId: randomUUID(),
696
+ };
685
697
  if (!parsed.includeAnswer) {
686
- try {
687
- return await this.searchMemoriesRetrieved(query, searchOpts, enc);
688
- }
689
- catch {
690
- return await this.searchMemoriesRaw(query, searchOpts, enc, true);
691
- }
698
+ return this.searchMemoriesRetrieved(query, searchOpts, enc);
692
699
  }
693
700
  // Encrypted account: the server can't synthesize over plaintext it doesn't hold, so use the raw
694
701
  // retriever (returns ciphertext) and decrypt locally — zero-knowledge preserved end-to-end.
695
702
  if (enc.enabled) {
696
703
  return await this.searchMemoriesRaw(query, searchOpts, enc);
697
704
  }
698
- // Unencrypted: prefer the tuned two-phase retriever; degrade gracefully to the untuned path.
699
- try {
700
- return await this.searchMemoriesTuned(query);
701
- }
702
- catch {
703
- return await this.searchMemoriesRaw(query, searchOpts, enc);
704
- }
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);
705
709
  }
706
710
  /** Decrypt the model-visible fields on a `{ memories: [...] }` response locally. */
707
711
  async decryptResult(data, key) {
@@ -970,6 +974,7 @@ class EchoMemMCPServer {
970
974
  ...triggerAnalyticsForTool(canonicalName, request.params.arguments),
971
975
  ...inputAnalyticsForTool(canonicalName, request.params.arguments),
972
976
  };
977
+ const analyticsCallId = randomUUID();
973
978
  // One event per call. Handlers enrich `rec` with tool-specific detail; we finalize + log in `finally`.
974
979
  const rec = {
975
980
  type: "tool_call",
@@ -1029,8 +1034,8 @@ class EchoMemMCPServer {
1029
1034
  if (!this.client.hasToken())
1030
1035
  throw new NoTokenError();
1031
1036
  if (canonicalName !== canonicalToolNames.save) {
1032
- void this.client.trackMcpAnalyticsEvent("[MCP] EchoMem Tool Called", analyticsBase);
1033
- 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`);
1034
1039
  }
1035
1040
  if (canonicalName !== canonicalToolNames.save && analyticsBase.trigger_message_available === true) {
1036
1041
  void this.client.trackMcpAnalyticsEvent("[MCP] EchoMem Triggered By User Turn", analyticsBase);
@@ -1125,8 +1130,8 @@ class EchoMemMCPServer {
1125
1130
  encrypted_user: rec.encrypted,
1126
1131
  extracted_memory_count: rec.memories_extracted,
1127
1132
  };
1128
- void this.client.trackMcpAnalyticsEvent(rec.ok ? "[MCP] EchoMem Tool Succeeded" : "[MCP] EchoMem Tool Failed", finalAnalytics);
1129
- 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`);
1130
1135
  }
1131
1136
  }
1132
1137
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@echomem/mcp",
3
- "version": "1.4.12",
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",