@echomem/mcp 1.3.1 → 1.3.2

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
@@ -167,6 +167,39 @@ function safeTextAnalytics(prefix, text, previewChars = 300) {
167
167
  function hashAnalyticsText(text) {
168
168
  return `sha256:${createHash("sha256").update(text).digest("hex")}`;
169
169
  }
170
+ function normalizeMcpHostPlatform(value) {
171
+ if (!value)
172
+ return undefined;
173
+ const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "_");
174
+ if (normalized.includes("cursor"))
175
+ return "cursor";
176
+ if (normalized.includes("windsurf"))
177
+ return "windsurf";
178
+ if (normalized.includes("codex"))
179
+ return "codex";
180
+ if (normalized.includes("claude_code") || (normalized.includes("claude") && normalized.includes("code"))) {
181
+ return "claude_code";
182
+ }
183
+ if (normalized.includes("claude_desktop"))
184
+ return "claude_desktop";
185
+ if (normalized.includes("claude"))
186
+ return "claude";
187
+ if (normalized.includes("vscode") || normalized.includes("visual_studio_code"))
188
+ return "vscode";
189
+ return undefined;
190
+ }
191
+ function detectMcpHostFromEnv() {
192
+ if (process.env.CURSOR_EXTENSION_HOST_ROLE || process.env.CURSOR_WORKSPACE_LABEL)
193
+ return "cursor";
194
+ if (process.env.WINDSURF_WORKSPACE_ID || process.env.WINDSURF_USER_ID)
195
+ return "windsurf";
196
+ if (process.env.CLAUDE_CODE || process.env.CLAUDECODE || process.env.ANTHROPIC_CLAUDE_CODE) {
197
+ return "claude_code";
198
+ }
199
+ if (process.env.CODEX_SANDBOX || process.env.CODEX_HOME)
200
+ return "codex";
201
+ return undefined;
202
+ }
170
203
  function isUserRole(role) {
171
204
  return typeof role === "string" && ["user", "human"].includes(role.toLowerCase());
172
205
  }
@@ -751,6 +784,8 @@ class EchoMemMCPServer {
751
784
  client;
752
785
  mapCache = null;
753
786
  events;
787
+ mcpClientName;
788
+ mcpClientVersion;
754
789
  /** Whether the most recent ListTools response carried the memory map (per-session recall signal). */
755
790
  mapInjected = false;
756
791
  constructor(store) {
@@ -771,9 +806,23 @@ class EchoMemMCPServer {
771
806
  process.exit(0);
772
807
  });
773
808
  }
809
+ getMcpClientAnalytics() {
810
+ const hostPlatform = normalizeMcpHostPlatform(this.mcpClientName)
811
+ ?? detectMcpHostFromEnv()
812
+ ?? "unknown";
813
+ return {
814
+ mcp_client_name: this.mcpClientName,
815
+ mcp_client_version: this.mcpClientVersion,
816
+ host_platform: hostPlatform,
817
+ platform_source: hostPlatform,
818
+ };
819
+ }
774
820
  setupToolHandlers() {
775
821
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
776
- this.events.setClient(this.server.getClientVersion()?.name);
822
+ const clientVersion = this.server.getClientVersion();
823
+ this.mcpClientName = clientVersion?.name ?? this.mcpClientName;
824
+ this.mcpClientVersion = clientVersion?.version ?? this.mcpClientVersion;
825
+ this.events.setClient(this.mcpClientName);
777
826
  // Inject a compact topic map of the user's memory into the search-tool description so the agent
778
827
  // knows the boundary up front and recalls proactively (cached; best-effort — no map on failure).
779
828
  if (this.client.hasToken() && !this.mapCache) {
@@ -793,12 +842,16 @@ class EchoMemMCPServer {
793
842
  });
794
843
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
795
844
  const canonicalName = resolveCanonicalToolName(request.params.name);
845
+ const clientVersion = this.server.getClientVersion();
846
+ this.mcpClientName = clientVersion?.name ?? this.mcpClientName;
847
+ this.mcpClientVersion = clientVersion?.version ?? this.mcpClientVersion;
796
848
  const t0 = Date.now();
797
849
  const analyticsBase = {
798
850
  surface: "mcp",
799
851
  event_family: "mcp",
800
852
  integration: "echomem_mcp",
801
853
  telemetry_source: "local_bridge",
854
+ ...this.getMcpClientAnalytics(),
802
855
  codex_session_id: this.client.getSessionId(),
803
856
  conversation_id: this.client.getSessionId(),
804
857
  tool_name: request.params.name,
@@ -994,17 +1047,21 @@ Details: ${m.details || "N/A"}`)
994
1047
  return { content: [{ type: "text", text: `Found ${memories.length} relevant memories:\n\n${formattedResults}` }] };
995
1048
  }
996
1049
  async handleSave(args, rec) {
1050
+ const sourceFallback = this.getMcpClientAnalytics().platform_source;
1051
+ const enrichedArgs = isRecord(args) && !readString(args, "source")
1052
+ ? { ...args, source: sourceFallback }
1053
+ : args;
997
1054
  if (rec) {
998
- const a = args;
1055
+ const a = enrichedArgs;
999
1056
  const text = typeof a?.conversation === "string"
1000
1057
  ? a.conversation
1001
1058
  : Array.isArray(a?.messages)
1002
1059
  ? a.messages.map((m) => String(m?.content ?? "")).join("\n")
1003
1060
  : "";
1004
1061
  rec.conversation_chars = text.length;
1005
- rec.save_source = typeof a?.source === "string" ? a.source : "mcp_server";
1062
+ rec.save_source = typeof a?.source === "string" ? a.source : sourceFallback;
1006
1063
  }
1007
- const { success, memoriesExtracted, error } = await this.client.saveConversation(args);
1064
+ const { success, memoriesExtracted, error } = await this.client.saveConversation(enrichedArgs);
1008
1065
  if (!success)
1009
1066
  throw new Error(`EchoMem API Error: ${error}`);
1010
1067
  if (rec)