@echomem/mcp 1.4.21 → 1.4.23

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/README.md CHANGED
@@ -66,7 +66,7 @@ HUD; the granular commands below still work.
66
66
  | `npx -y @echomem/mcp@latest setup` | One-off setup without keeping a global CLI command |
67
67
  | `echomem-mcp setup [--client cursor\|windsurf\|claude-desktop\|claude-code\|codex]` | Write client config + log in |
68
68
  | `echomem-mcp setup --skip-login [--client cursor\|windsurf\|claude-desktop\|claude-code\|codex]` | Write client config without opening the browser or changing credentials |
69
- | `npx -y @echomem/mcp@latest update --all` | One-shot update: repoint detected client configs to the latest bridge, with no browser login |
69
+ | `npx -y @echomem/mcp@latest update --all` | One-shot update: install the latest bridge durably and repoint detected client configs, with no browser login |
70
70
  | `npx -y @echomem/mcp@latest update --client codex` | Update one client only |
71
71
  | `echomem-mcp setup --with-hud [--client codex]` | Write client config + log in + launch the EchoMem context HUD |
72
72
  | `echomem-mcp login` | Approve device in browser (or use `--token` / `--passphrase`) |
package/dist/hud/web.js CHANGED
@@ -1723,7 +1723,7 @@ export const HUD_HTML = String.raw `<!doctype html>
1723
1723
  if (previewParams.get("capture") === "1") hud.classList.add("capture-preview");
1724
1724
  if (previewColor === "green" || previewColor === "amber" || previewColor === "red") {
1725
1725
  const previewProcessingUsed = previewColor === "red" ? 200000 : previewColor === "amber" ? 163000 : 84000;
1726
- const previewSearchUsed = previewColor === "red" ? 100 : previewColor === "amber" ? 82 : 32;
1726
+ const previewSearchUsed = previewColor === "red" ? 500 : previewColor === "amber" ? 410 : 160;
1727
1727
  billingStatus = {
1728
1728
  ok: true,
1729
1729
  state: "ok",
@@ -1731,8 +1731,8 @@ export const HUD_HTML = String.raw `<!doctype html>
1731
1731
  paid: true,
1732
1732
  pricingUrl: "https://yeahecho.com/pricing?source=hud_preview",
1733
1733
  memoryProcessingQuota: { used: previewProcessingUsed, limit: 200000, remaining: 200000 - previewProcessingUsed, resetAt: new Date(Date.now() + 31 * 60 * 60 * 1000).toISOString() },
1734
- memorySearchQuota: { used: previewSearchUsed, limit: 100, remaining: 100 - previewSearchUsed, resetAt: new Date(Date.now() + 31 * 60 * 60 * 1000).toISOString() },
1735
- historicalConversationQuota: { used: 191, limit: 250, remaining: 59 },
1734
+ memorySearchQuota: { used: previewSearchUsed, limit: 500, remaining: 500 - previewSearchUsed, resetAt: new Date(Date.now() + 31 * 60 * 60 * 1000).toISOString() },
1735
+ historicalConversationQuota: { used: 191, limit: 500, remaining: 309 },
1736
1736
  };
1737
1737
  const sample = {
1738
1738
  client: "codex",
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
5
5
  import axios from "axios";
6
6
  import { ZodError } from "zod";
7
- import { canonicalToolNames, deleteMemorySchema, getByContextSchema, keywordsSchema, listFriendsSchema, listToolSpecs, othersSchema, publicMemorySchema, resolveCanonicalToolName, saveConversationSchema, searchMemoriesSchema, searchUsersSchema, sendFriendRequestSchema, timeRangeSchema, } from "./v1-contract.js";
7
+ import { canonicalToolNames, completeGroupPublicationSchema, createGroupInviteSchema, createGroupSchema, deleteMemorySchema, getByContextSchema, groupContextSchema, joinGroupSchema, keywordsSchema, listFriendsSchema, listToolSpecs, othersSchema, publishBatchToGroupSchema, publishToGroupSchema, prepareGroupPublicationSchema, publicMemorySchema, resolveCanonicalToolName, saveConversationSchema, searchMemoriesSchema, searchUsersSchema, sendFriendRequestSchema, timeRangeSchema, } from "./v1-contract.js";
8
8
  import { KeyStore } from "./keystore.js";
9
9
  import { EventLogger, hashText } from "./events.js";
10
10
  import { buildReportText } from "./report.js";
@@ -488,12 +488,20 @@ function inputAnalyticsForTool(canonicalName, args) {
488
488
  limit: numberArg(a, "limit"),
489
489
  };
490
490
  }
491
- case canonicalToolNames.publicMemory: {
491
+ case canonicalToolNames.publicMemory:
492
+ case canonicalToolNames.publishToGroup: {
492
493
  const memoryId = readString(a, "memoryId");
493
494
  return {
494
495
  memory_id_hash: memoryId ? hashText(memoryId) : undefined,
495
496
  };
496
497
  }
498
+ case canonicalToolNames.publishBatchToGroup: {
499
+ const memoryIds = Array.isArray(a.memoryIds) ? a.memoryIds.filter((id) => typeof id === "string") : [];
500
+ return {
501
+ memory_count: memoryIds.length,
502
+ context_id_hash: typeof a.contextId === "string" ? hashText(a.contextId) : undefined,
503
+ };
504
+ }
497
505
  default:
498
506
  return {};
499
507
  }
@@ -944,6 +952,72 @@ class EchoMemApiClient {
944
952
  throw new Error(`get_public_memory failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
945
953
  }
946
954
  }
955
+ async getGroupContext(args) {
956
+ groupContextSchema.parse(args ?? {});
957
+ try {
958
+ const response = await this.axios.get("/api/extension/social/groups/current");
959
+ return response.data;
960
+ }
961
+ catch (error) {
962
+ throw new Error(`get_group_context failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
963
+ }
964
+ }
965
+ async createGroup(args) {
966
+ const parsed = createGroupSchema.parse(args ?? {});
967
+ const response = await this.axios.post("/api/extension/social/groups", parsed);
968
+ return response.data;
969
+ }
970
+ async createGroupInvite(args) {
971
+ const parsed = createGroupInviteSchema.parse(args ?? {});
972
+ const response = await this.axios.post("/api/extension/social/groups/current/invite", parsed);
973
+ return response.data;
974
+ }
975
+ async joinGroup(args) {
976
+ const parsed = joinGroupSchema.parse(args ?? {});
977
+ const response = await this.axios.post("/api/extension/social/groups/join", parsed);
978
+ return response.data;
979
+ }
980
+ async prepareGroupPublication(args) {
981
+ const parsed = prepareGroupPublicationSchema.parse(args ?? {});
982
+ const enc = await this.encState();
983
+ const response = await this.axios.post("/api/extension/social/groups/current/publications/prepare", parsed, { headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined });
984
+ return response.data;
985
+ }
986
+ async completeGroupPublication(args) {
987
+ const parsed = completeGroupPublicationSchema.parse(args ?? {});
988
+ const enc = await this.encState();
989
+ const response = await this.axios.post("/api/extension/social/groups/current/publications/complete", parsed, { headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined });
990
+ return response.data;
991
+ }
992
+ async publishMemoryToGroup(args) {
993
+ const parsed = publishToGroupSchema.parse(args ?? {});
994
+ const enc = await this.encState();
995
+ try {
996
+ const response = await this.axios.post(`/api/extension/social/groups/current/memories/${encodeURIComponent(parsed.memoryId)}/publish`, {}, {
997
+ headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined,
998
+ });
999
+ return response.data;
1000
+ }
1001
+ catch (error) {
1002
+ throw new Error(`publish_memory_to_group failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
1003
+ }
1004
+ }
1005
+ async publishMemoriesToGroup(args) {
1006
+ const parsed = publishBatchToGroupSchema.parse(args ?? {});
1007
+ const enc = await this.encState();
1008
+ try {
1009
+ const response = await this.axios.post("/api/extension/social/groups/current/memories/publish-batch", {
1010
+ memoryIds: parsed.memoryIds,
1011
+ contextId: parsed.contextId,
1012
+ }, {
1013
+ headers: enc.enabled && enc.key ? { "X-Encryption-Key": enc.key } : undefined,
1014
+ });
1015
+ return response.data;
1016
+ }
1017
+ catch (error) {
1018
+ throw new Error(`publish_memories_to_group failed against ${ECHO_API_BASE_URL}: ${describeError(error)}`);
1019
+ }
1020
+ }
947
1021
  }
948
1022
  const SERVER_VERSION = MCP_PACKAGE_VERSION;
949
1023
  class EchoMemMCPServer {
@@ -1121,6 +1195,22 @@ class EchoMemMCPServer {
1121
1195
  return await this.handleOthers(request.params.arguments);
1122
1196
  case canonicalToolNames.publicMemory:
1123
1197
  return await this.handlePublicMemory(request.params.arguments);
1198
+ case canonicalToolNames.groupContext:
1199
+ return await this.handleGroupContext(request.params.arguments);
1200
+ case canonicalToolNames.createGroup:
1201
+ return await this.handleCreateGroup(request.params.arguments);
1202
+ case canonicalToolNames.createGroupInvite:
1203
+ return await this.handleCreateGroupInvite(request.params.arguments);
1204
+ case canonicalToolNames.joinGroup:
1205
+ return await this.handleJoinGroup(request.params.arguments);
1206
+ case canonicalToolNames.prepareGroupPublication:
1207
+ return await this.handlePrepareGroupPublication(request.params.arguments);
1208
+ case canonicalToolNames.completeGroupPublication:
1209
+ return await this.handleCompleteGroupPublication(request.params.arguments);
1210
+ case canonicalToolNames.publishToGroup:
1211
+ return await this.handlePublishToGroup(request.params.arguments);
1212
+ case canonicalToolNames.publishBatchToGroup:
1213
+ return await this.handlePublishBatchToGroup(request.params.arguments);
1124
1214
  case canonicalToolNames.delete:
1125
1215
  return await this.handleDelete(request.params.arguments, rec);
1126
1216
  default:
@@ -1302,7 +1392,7 @@ class EchoMemMCPServer {
1302
1392
  return { content: [{ type: "text", text: "No relevant memories found." }] };
1303
1393
  }
1304
1394
  const formattedResults = memories
1305
- .map((m, idx) => `[Result ${idx + 1}] (Similarity: ${m.similarity_score?.toFixed(3) || "N/A"})
1395
+ .map((m, idx) => `[Result ${idx + 1}] Memory ID: ${m.id || "unknown"} (Similarity: ${m.similarity_score?.toFixed(3) || "N/A"})
1306
1396
  Time: ${m.time} | Location: ${m.location}
1307
1397
  Category: ${m.category} | Object: ${m.object} | Emotion: ${m.emotion}
1308
1398
  Description: ${m.description}
@@ -1504,11 +1594,11 @@ Details: ${m.details || "N/A"}`)
1504
1594
  const memories = payload?.memories ?? [];
1505
1595
  if (!memories.length) {
1506
1596
  const scope = parsed.ownerUserId
1507
- ? ` for friend ${parsed.ownerUserId}`
1597
+ ? ` for peer ${parsed.ownerUserId}`
1508
1598
  : parsed.ownerName
1509
- ? ` for friend ${parsed.ownerName}`
1599
+ ? ` for peer ${parsed.ownerName}`
1510
1600
  : parsed.target
1511
- ? ` for friend ${parsed.target}`
1601
+ ? ` for peer ${parsed.target}`
1512
1602
  : parsed.targetFriendIds?.length
1513
1603
  ? ` for ${parsed.targetFriendIds.length} selected friends`
1514
1604
  : parsed.targetFriendNames?.length
@@ -1521,7 +1611,12 @@ Details: ${m.details || "N/A"}`)
1521
1611
  }
1522
1612
  const metadata = [
1523
1613
  typeof payload?.friendCount === "number" ? `friendCount=${payload.friendCount}` : "",
1524
- typeof payload?.searchedFriendCount === "number" ? `searchedFriendCount=${payload.searchedFriendCount}` : "",
1614
+ typeof payload?.groupPeerCount === "number" ? `groupPeerCount=${payload.groupPeerCount}` : "",
1615
+ typeof payload?.searchedPeerCount === "number"
1616
+ ? `searchedPeerCount=${payload.searchedPeerCount}`
1617
+ : typeof payload?.searchedFriendCount === "number"
1618
+ ? `searchedPeerCount=${payload.searchedFriendCount}`
1619
+ : "",
1525
1620
  typeof payload?.memoryViewsInserted === "number" ? `memoryViewsInserted=${payload.memoryViewsInserted}` : "",
1526
1621
  ].filter(Boolean).join(", ");
1527
1622
  const formattedResults = memories
@@ -1674,6 +1769,168 @@ Details: ${m.details || "N/A"}`;
1674
1769
  content: [{ type: "text", text }],
1675
1770
  };
1676
1771
  }
1772
+ async handleGroupContext(args) {
1773
+ groupContextSchema.parse(args ?? {});
1774
+ const payload = await this.client.getGroupContext(args);
1775
+ const group = isRecord(payload?.group) ? payload.group : null;
1776
+ const participants = Array.isArray(payload?.participants)
1777
+ ? payload.participants.filter(isRecord)
1778
+ : [];
1779
+ const coverage = isRecord(payload?.coverage) ? payload.coverage : {};
1780
+ if (!group) {
1781
+ return {
1782
+ content: [{
1783
+ type: "text",
1784
+ text: "You are not assigned to a company group yet. Ask an EchoMem administrator to add you before using group orientation or group publishing.",
1785
+ }],
1786
+ };
1787
+ }
1788
+ const participantText = participants.map((participant, index) => [
1789
+ `[${index + 1}] ${readString(participant, "displayName") ?? "Unnamed participant"}`,
1790
+ `User ID: ${readString(participant, "userId") ?? "unknown"}`,
1791
+ `Title: ${readString(participant, "title") ?? "Not declared"}`,
1792
+ `Declared responsibility: ${readString(participant, "responsibilitySummary") ?? "Not provided"}`,
1793
+ `Published memories: ${readNumber(participant, "publicMemoryCount") ?? 0}`,
1794
+ ].join("\n")).join("\n\n");
1795
+ const participantCount = readNumber(coverage, "participantCount") ?? participants.length;
1796
+ const coveredCount = readNumber(coverage, "participantsWithPublishedMemories") ?? 0;
1797
+ const totalPublished = readNumber(coverage, "totalPublishedMemories") ?? 0;
1798
+ const text = [
1799
+ `Company group: ${readString(group, "name") ?? "Unnamed group"}`,
1800
+ readString(group, "description") ? `Description: ${readString(group, "description")}` : "",
1801
+ `Coverage: ${coveredCount} of ${participantCount} participants have published memories (${totalPublished} total).`,
1802
+ "",
1803
+ participantText,
1804
+ "",
1805
+ "Declared titles and responsibilities are directory facts. Use search_others_memories for current work evidence, and label suggested contribution areas as inference that should be confirmed with the team.",
1806
+ ].filter(Boolean).join("\n");
1807
+ return { content: [{ type: "text", text }] };
1808
+ }
1809
+ async handlePublishToGroup(args) {
1810
+ const parsed = publishToGroupSchema.parse(args ?? {});
1811
+ const payload = await this.client.publishMemoryToGroup(args);
1812
+ if (payload?.success === false) {
1813
+ throw new Error(`EchoMem API Error: ${payload.error || "publish failed"}`);
1814
+ }
1815
+ const group = isRecord(payload?.group) ? payload.group : {};
1816
+ const groupName = readString(group, "name") ?? "your company group";
1817
+ const memberCount = typeof payload?.searchableByGroupMemberCount === "number"
1818
+ ? payload.searchableByGroupMemberCount
1819
+ : 0;
1820
+ const alreadyPublished = payload?.alreadyPublished === true;
1821
+ return {
1822
+ content: [{
1823
+ type: "text",
1824
+ text: alreadyPublished
1825
+ ? `Memory ${parsed.memoryId} was already published to ${groupName}. It is searchable by up to ${memberCount} other group members.`
1826
+ : `Published memory ${parsed.memoryId} to ${groupName}. It is now searchable by up to ${memberCount} other group members.`,
1827
+ }],
1828
+ };
1829
+ }
1830
+ async handleCreateGroup(args) {
1831
+ const parsed = createGroupSchema.parse(args ?? {});
1832
+ const payload = await this.client.createGroup(parsed);
1833
+ return { content: [{ type: "text", text: `Created company group ${payload?.group?.name ?? parsed.name}. Generate an invite code when you are ready to add teammates.` }] };
1834
+ }
1835
+ async handleCreateGroupInvite(args) {
1836
+ createGroupInviteSchema.parse(args ?? {});
1837
+ const payload = await this.client.createGroupInvite(args);
1838
+ return {
1839
+ content: [{
1840
+ type: "text",
1841
+ text: [
1842
+ `Invite code for ${payload?.group?.name ?? "your group"}: ${payload?.code}`,
1843
+ `Expires: ${payload?.expiresAt}`,
1844
+ `Maximum uses: ${payload?.maxUses}`,
1845
+ "Share this secret code only with intended teammates.",
1846
+ ].join("\n"),
1847
+ }],
1848
+ };
1849
+ }
1850
+ async handleJoinGroup(args) {
1851
+ const parsed = joinGroupSchema.parse(args ?? {});
1852
+ const payload = await this.client.joinGroup(parsed);
1853
+ return {
1854
+ content: [{
1855
+ type: "text",
1856
+ text: payload?.alreadyMember
1857
+ ? `You are already a member of ${payload?.group?.name ?? "this group"}. No memories were published.`
1858
+ : `Joined ${payload?.group?.name ?? "the company group"}. No memories were published. Use prepare_group_publication to review candidates.`,
1859
+ }],
1860
+ };
1861
+ }
1862
+ async handlePrepareGroupPublication(args) {
1863
+ const parsed = prepareGroupPublicationSchema.parse(args ?? {});
1864
+ const payload = await this.client.prepareGroupPublication(parsed);
1865
+ const candidates = Array.isArray(payload?.candidates) ? payload.candidates : [];
1866
+ const formatted = candidates.map((candidate, index) => [
1867
+ `[${index + 1}] Memory ID: ${readString(candidate, "memoryId") ?? "unknown"}`,
1868
+ `Created: ${readString(candidate, "createdAt") ?? "unknown"}`,
1869
+ `Category: ${readString(candidate, "category") ?? "unknown"}`,
1870
+ `Description: ${readString(candidate, "description") ?? ""}`,
1871
+ readString(candidate, "details") ? `Details: ${readString(candidate, "details")}` : "",
1872
+ `Already published: ${candidate.alreadyPublished === true ? "yes" : "no"}`,
1873
+ `Exact content duplicate: ${candidate.exactContentDuplicate === true ? "yes" : "no"}`,
1874
+ ].filter(Boolean).join("\n")).join("\n\n");
1875
+ return {
1876
+ content: [{
1877
+ type: "text",
1878
+ text: [
1879
+ `Prepared publication scan ${payload?.scanId} for ${payload?.group?.name ?? "your group"}.`,
1880
+ `Window: ${payload?.windowStart ?? "context start"} → ${payload?.windowEnd}`,
1881
+ `Candidates: ${candidates.length}. Nothing has been published.`,
1882
+ "",
1883
+ formatted,
1884
+ "",
1885
+ "Select exact memory IDs matching the user's instruction, show the preview, and ask for confirmation before calling complete_group_publication.",
1886
+ ].join("\n"),
1887
+ }],
1888
+ };
1889
+ }
1890
+ async handleCompleteGroupPublication(args) {
1891
+ const parsed = completeGroupPublicationSchema.parse(args ?? {});
1892
+ const payload = await this.client.completeGroupPublication(parsed);
1893
+ const memories = Array.isArray(payload?.memories) ? payload.memories : [];
1894
+ const published = memories.filter((memory) => memory.alreadyPublished !== true && memory.skippedDuplicate !== true).length;
1895
+ const duplicates = memories.filter((memory) => memory.skippedDuplicate === true).length;
1896
+ return {
1897
+ content: [{
1898
+ type: "text",
1899
+ text: [
1900
+ `Completed publication scan ${parsed.scanId}.`,
1901
+ `Newly published: ${published}.`,
1902
+ `Exact duplicates skipped: ${duplicates}.`,
1903
+ `Selected memory IDs: ${parsed.memoryIds.join(", ") || "(none)"}.`,
1904
+ "The scan cursor advanced; encrypted originals and global public settings were unchanged.",
1905
+ ].join("\n"),
1906
+ }],
1907
+ };
1908
+ }
1909
+ async handlePublishBatchToGroup(args) {
1910
+ const parsed = publishBatchToGroupSchema.parse(args ?? {});
1911
+ const payload = await this.client.publishMemoriesToGroup(parsed);
1912
+ if (payload?.success === false) {
1913
+ throw new Error(`EchoMem API Error: ${payload.error || "batch publish failed"}`);
1914
+ }
1915
+ const group = isRecord(payload?.group) ? payload.group : {};
1916
+ const groupName = readString(group, "name") ?? "your company group";
1917
+ const publishedCount = typeof payload?.publishedCount === "number" ? payload.publishedCount : 0;
1918
+ const alreadyPublishedCount = typeof payload?.alreadyPublishedCount === "number"
1919
+ ? payload.alreadyPublishedCount
1920
+ : 0;
1921
+ return {
1922
+ content: [{
1923
+ type: "text",
1924
+ text: [
1925
+ `Published ${publishedCount} memory snapshots to ${groupName}.`,
1926
+ alreadyPublishedCount ? `${alreadyPublishedCount} were already published and were refreshed.` : "",
1927
+ `Selected memory IDs: ${parsed.memoryIds.join(", ")}`,
1928
+ parsed.selectionReason ? `Selection reason: ${parsed.selectionReason}` : "",
1929
+ "The encrypted originals and their global public settings were not changed.",
1930
+ ].filter(Boolean).join("\n"),
1931
+ }],
1932
+ };
1933
+ }
1677
1934
  async handleDelete(args, rec) {
1678
1935
  const parsed = deleteMemorySchema.parse(args ?? {});
1679
1936
  if (rec) {
package/dist/migrate.js CHANGED
@@ -4,9 +4,10 @@
4
4
  *
5
5
  * Why this lives in the bridge (client-side): the session logs exist ONLY on the user's machine
6
6
  * (~/.codex/sessions, ~/.claude/projects). The bridge discovers each session, assembles it into a
7
- * text-turn-only `## ` transcript, and feeds it to the EXISTING durable import queue
8
- * (the same one the extension uses): POST /api/extension/import-sessions creates one import_jobs row per session, then the bridge
9
- * POSTs each transcript to /api/extension/import-jobs/{id}/run. The web dashboard polls
7
+ * text-turn-only `## ` transcript, and feeds it to the shared durable import queue
8
+ * through MCP-owned policy entrypoints. POST /api/extension/mcp/historical-import-sessions
9
+ * creates one import_jobs row per session, then the bridge POSTs each transcript to
10
+ * /api/extension/mcp/historical-import-jobs/{id}/run. The web dashboard polls
10
11
  * GET /import-sessions/{id} for live progress — the bridge owns local-file access, the server owns the
11
12
  * queue + status. (Headless CLI also works; it just shows progress in the terminal.)
12
13
  *
@@ -1353,7 +1354,7 @@ async function createImportSession(client, sessions, bareId, userTz, signal) {
1353
1354
  sourceDate: s.firstTs,
1354
1355
  userTz,
1355
1356
  }));
1356
- const res = await client.post("/api/extension/import-sessions", { items }, signal ? { signal } : undefined);
1357
+ const res = await client.post("/api/extension/mcp/historical-import-sessions", { items }, signal ? { signal } : undefined);
1357
1358
  const data = res.data || {};
1358
1359
  const serverConcurrency = Number(data.session?.concurrency);
1359
1360
  return {
@@ -1378,7 +1379,7 @@ async function runImportJob(client, jobId, s, userTz, encKey) {
1378
1379
  const cfg = encKey ? { headers: { "X-Encryption-Key": encKey } } : undefined;
1379
1380
  for (let attempt = 0;; attempt++) {
1380
1381
  try {
1381
- const res = await client.post(`/api/extension/import-jobs/${jobId}/run`, body, cfg);
1382
+ const res = await client.post(`/api/extension/mcp/historical-import-jobs/${jobId}/run`, body, cfg);
1382
1383
  const data = res.data || {};
1383
1384
  if (data.claimed === false) {
1384
1385
  // Already completed on a prior run → idempotent, not an error; otherwise it didn't start.
@@ -28,6 +28,8 @@ export const MCP_SERVER_INSTRUCTIONS = [
28
28
  `If the user or local config expects a newer EchoMem MCP version than ${MCP_PACKAGE_VERSION}, update once with \`${MCP_UPDATE_ALL_COMMAND}\` and start a new MCP session.`,
29
29
  "Use echomem_update_status to check whether npm has a newer bridge; it is cached and non-blocking during normal tool listing.",
30
30
  "Do not auto-update on every MCP startup; this bridge is intentionally stable between explicit updates.",
31
+ "For company-group sharing, use get_group_context for orientation; create_memory_group/create_group_invite/join_memory_group for membership; and prepare_group_publication as a no-write preview before complete_group_publication.",
32
+ "Never complete a group publication without explicit confirmation of the prepared candidate set, and never store or log an echo_grp_ invite code.",
31
33
  ].join(" ");
32
34
  export function withMcpVersion(description) {
33
35
  return `${description}\n\nEchoMem MCP bridge: ${MCP_PACKAGE_LABEL}. If this version is stale, update once with \`${MCP_UPDATE_ALL_COMMAND}\` (or add \`--client cursor|windsurf|claude-desktop|claude-code|codex\` for a single client), then start a new MCP session. Do not run updates repeatedly or on every startup.`;