@echomem/mcp 1.4.23 → 1.4.24

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
@@ -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, 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";
7
+ import { canonicalToolNames, completeGroupPublicationSchema, createGroupInviteSchema, createGroupSchema, deleteMemorySchema, getByContextSchema, groupContextSchema, joinGroupSchema, keywordsSchema, listFriendsSchema, listToolSpecs, othersSchema, publishBatchToGroupSchema, publishToGroupSchema, prepareGroupPublicationSchema, publicMemorySchema, resolveCanonicalToolName, saveConversationSchema, searchMemoriesSchema, searchUsersSchema, sendFriendRequestSchema, timeRangeSchema, updateGroupProfileSchema, } 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";
@@ -983,6 +983,11 @@ class EchoMemApiClient {
983
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
984
  return response.data;
985
985
  }
986
+ async updateGroupProfile(args) {
987
+ const parsed = updateGroupProfileSchema.parse(args ?? {});
988
+ const response = await this.axios.patch("/api/extension/social/groups/current/profile", parsed);
989
+ return response.data;
990
+ }
986
991
  async completeGroupPublication(args) {
987
992
  const parsed = completeGroupPublicationSchema.parse(args ?? {});
988
993
  const enc = await this.encState();
@@ -1205,6 +1210,8 @@ class EchoMemMCPServer {
1205
1210
  return await this.handleJoinGroup(request.params.arguments);
1206
1211
  case canonicalToolNames.prepareGroupPublication:
1207
1212
  return await this.handlePrepareGroupPublication(request.params.arguments);
1213
+ case canonicalToolNames.updateGroupProfile:
1214
+ return await this.handleUpdateGroupProfile(request.params.arguments);
1208
1215
  case canonicalToolNames.completeGroupPublication:
1209
1216
  return await this.handleCompleteGroupPublication(request.params.arguments);
1210
1217
  case canonicalToolNames.publishToGroup:
@@ -1776,6 +1783,9 @@ Details: ${m.details || "N/A"}`;
1776
1783
  const participants = Array.isArray(payload?.participants)
1777
1784
  ? payload.participants.filter(isRecord)
1778
1785
  : [];
1786
+ const currentParticipant = isRecord(payload?.currentParticipant)
1787
+ ? payload.currentParticipant
1788
+ : null;
1779
1789
  const coverage = isRecord(payload?.coverage) ? payload.coverage : {};
1780
1790
  if (!group) {
1781
1791
  return {
@@ -1803,6 +1813,10 @@ Details: ${m.details || "N/A"}`;
1803
1813
  participantText,
1804
1814
  "",
1805
1815
  "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.",
1816
+ currentParticipant
1817
+ && (!readString(currentParticipant, "title") || !readString(currentParticipant, "responsibilitySummary"))
1818
+ ? "Your group profile is incomplete. Use prepare_group_publication to review your memory evidence, propose the missing fields, and save them only after confirmation with update_group_profile."
1819
+ : "",
1806
1820
  ].filter(Boolean).join("\n");
1807
1821
  return { content: [{ type: "text", text }] };
1808
1822
  }
@@ -1854,8 +1868,8 @@ Details: ${m.details || "N/A"}`;
1854
1868
  content: [{
1855
1869
  type: "text",
1856
1870
  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.`,
1871
+ ? `You are already a member of ${payload?.group?.name ?? "this group"}. No memories were published. Use prepare_group_publication to review memories and propose any missing title or responsibility fields.`
1872
+ : `Joined ${payload?.group?.name ?? "the company group"}. No memories were published. Next use prepare_group_publication to review candidates, infer a proposed title and responsibility summary, and ask the user to confirm that profile together with the publication preview.`,
1859
1873
  }],
1860
1874
  };
1861
1875
  }
@@ -1863,6 +1877,9 @@ Details: ${m.details || "N/A"}`;
1863
1877
  const parsed = prepareGroupPublicationSchema.parse(args ?? {});
1864
1878
  const payload = await this.client.prepareGroupPublication(parsed);
1865
1879
  const candidates = Array.isArray(payload?.candidates) ? payload.candidates : [];
1880
+ const participantProfile = isRecord(payload?.participantProfile)
1881
+ ? payload.participantProfile
1882
+ : {};
1866
1883
  const formatted = candidates.map((candidate, index) => [
1867
1884
  `[${index + 1}] Memory ID: ${readString(candidate, "memoryId") ?? "unknown"}`,
1868
1885
  `Created: ${readString(candidate, "createdAt") ?? "unknown"}`,
@@ -1879,10 +1896,29 @@ Details: ${m.details || "N/A"}`;
1879
1896
  `Prepared publication scan ${payload?.scanId} for ${payload?.group?.name ?? "your group"}.`,
1880
1897
  `Window: ${payload?.windowStart ?? "context start"} → ${payload?.windowEnd}`,
1881
1898
  `Candidates: ${candidates.length}. Nothing has been published.`,
1899
+ `Current title: ${readString(participantProfile, "title") ?? "Not declared"}`,
1900
+ `Current declared responsibility: ${readString(participantProfile, "responsibilitySummary") ?? "Not provided"}`,
1882
1901
  "",
1883
1902
  formatted,
1884
1903
  "",
1885
- "Select exact memory IDs matching the user's instruction, show the preview, and ask for confirmation before calling complete_group_publication.",
1904
+ "Select exact memory IDs matching the user's instruction. From memory evidence, draft a concise title and responsibility summary. Show the profile proposal and memory preview together, then ask for one explicit confirmation. After confirmation, call update_group_profile and complete_group_publication.",
1905
+ ].join("\n"),
1906
+ }],
1907
+ };
1908
+ }
1909
+ async handleUpdateGroupProfile(args) {
1910
+ const parsed = updateGroupProfileSchema.parse(args ?? {});
1911
+ const payload = await this.client.updateGroupProfile(parsed);
1912
+ const participant = isRecord(payload?.participant) ? payload.participant : {};
1913
+ return {
1914
+ content: [{
1915
+ type: "text",
1916
+ text: [
1917
+ "Confirmed company-group profile updated.",
1918
+ `Display name: ${readString(participant, "displayName") ?? parsed.displayName ?? "unchanged"}`,
1919
+ `Title: ${readString(participant, "title") ?? parsed.title}`,
1920
+ `Declared responsibility: ${readString(participant, "responsibilitySummary") ?? parsed.responsibilitySummary}`,
1921
+ "These fields are now declared directory facts visible to group members.",
1886
1922
  ].join("\n"),
1887
1923
  }],
1888
1924
  };
@@ -28,8 +28,9 @@ 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
+ "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-publication preview.",
32
+ "After joining or when profile fields are missing, use candidate memory evidence to propose a title and responsibility summary. Ask the user to confirm that proposal together with the publication preview, then call update_group_profile and complete_group_publication.",
33
+ "Never save an inferred group profile or complete a group publication without explicit confirmation, and never store or log an echo_grp_ invite code.",
33
34
  ].join(" ");
34
35
  export function withMcpVersion(description) {
35
36
  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.`;
package/dist/setup.js CHANGED
@@ -396,10 +396,11 @@ function echomemGuidanceBlock() {
396
396
  "- Use `get_group_context` when the user asks who is in their company group, what teammates are responsible for, or what work is already covered. Treat declared participant fields as facts and published-memory conclusions as evidence or inference.",
397
397
  "- A group publication is separate from a globally public memory: publishing to a group creates a group-scoped snapshot and must not change the encrypted original or its global `is_public` setting.",
398
398
  "- If a user asks to create a group, call `create_memory_group`; if they ask for a code to share, call `create_group_invite` and return the secret invite code only to that user. Never save the invite code to memory or include it in logs, analytics, summaries, or unrelated output.",
399
- "- If a user supplies an `echo_grp_...` code and explicitly asks to join, call `join_memory_group`. Joining never authorizes publishing by itself and must not move a user out of another group.",
400
- "- For requests such as “prepare my recent work memories,” “upload work from this ticket,” or “publish work since my last sync,” call `prepare_group_publication` first. This is a no-write preview. For encrypted accounts, tell the user to run `echomem-mcp unlock` locally if the tool reports that the key is required.",
401
- "- After preparing, select only exact candidate memory IDs that match the user's stated scope, exclude already-published or exact-content duplicates, summarize what would be shared, and ask the user to confirm. Do not call `complete_group_publication` until the user explicitly confirms that preview.",
402
- "- On confirmation, call `complete_group_publication` with the exact `scanId`, selected memory IDs, and `confirmed: true`. An explicit request to join and upload still requires the publication preview and confirmation after joining.",
399
+ "- If a user supplies an `echo_grp_...` code and explicitly asks to join, call `join_memory_group`. Joining never authorizes publishing by itself and must not move a user out of another group. After joining, continue into the profile-and-publication preview instead of leaving title or responsibility blank.",
400
+ "- For requests such as “prepare my recent work memories,” “upload work from this ticket,” or “publish work since my last sync,” call `prepare_group_publication` first. This is a no-publication preview. For encrypted accounts, tell the user to run `echomem-mcp unlock` locally if the tool reports that the key is required.",
401
+ "- After preparing, select only exact candidate memory IDs that match the user's stated scope and exclude already-published or exact-content duplicates. Use the candidate evidence to draft a concise title and responsibility summary for the current member, but label both as proposals rather than facts.",
402
+ "- Present the proposed title/responsibility and the memory publication preview together and ask for one explicit confirmation. Never save an inferred profile or publish memories before confirmation.",
403
+ "- On confirmation, call `update_group_profile` with the confirmed title, responsibility summary, and `confirmed: true`, then call `complete_group_publication` with the exact `scanId`, selected memory IDs, and `confirmed: true`. If the user edits either proposal, use their wording. An explicit request to join and upload still requires this combined preview and confirmation.",
403
404
  "- If the user asks to show, reopen, restart, or bring back the EchoMem HUD (the context-health overlay), run the shell command `echomem-hud app --client auto`.",
404
405
  "- If the user wants the HUD to come back after a computer restart, run the shell command `echomem-hud autostart on --client auto`.",
405
406
  AGENTS_MD_END,
@@ -15,6 +15,7 @@ export const canonicalToolNames = {
15
15
  createGroupInvite: "create_group_invite",
16
16
  joinGroup: "join_memory_group",
17
17
  prepareGroupPublication: "prepare_group_publication",
18
+ updateGroupProfile: "update_group_profile",
18
19
  completeGroupPublication: "complete_group_publication",
19
20
  publishToGroup: "publish_memory_to_group",
20
21
  publishBatchToGroup: "publish_memories_to_group",
@@ -143,6 +144,13 @@ export const prepareGroupPublicationSchema = z.object({
143
144
  limit: z.number().int().min(1).max(50).optional(),
144
145
  instruction: z.string().max(500).optional(),
145
146
  });
147
+ export const updateGroupProfileSchema = z.object({
148
+ ...triggerMetadataSchema,
149
+ displayName: z.string().min(1).max(120).optional(),
150
+ title: z.string().min(1).max(160),
151
+ responsibilitySummary: z.string().min(1).max(1000),
152
+ confirmed: z.literal(true),
153
+ });
146
154
  export const completeGroupPublicationSchema = z.object({
147
155
  ...triggerMetadataSchema,
148
156
  scanId: z.string().min(1),
@@ -460,7 +468,7 @@ export function listToolSpecs(opts = {}) {
460
468
  },
461
469
  {
462
470
  name: canonicalToolNames.joinGroup,
463
- description: "Join a company memory group using an invite code after the user explicitly asks to join. Joining never publishes memories; call prepare_group_publication separately.",
471
+ description: "Join a company memory group using an invite code after the user explicitly asks to join. Joining never publishes memories. Next call prepare_group_publication, infer a proposed title and responsibility summary from the user's own candidate memories, and ask the user to confirm the profile together with the publication preview.",
464
472
  inputSchema: {
465
473
  type: "object",
466
474
  properties: {
@@ -474,7 +482,7 @@ export function listToolSpecs(opts = {}) {
474
482
  },
475
483
  {
476
484
  name: canonicalToolNames.prepareGroupPublication,
477
- description: "Prepare a no-write preview of up to 50 owned memories for group publication. For encrypted accounts the local bridge must be unlocked. The host agent selects exact work-related memory ids from the returned candidates and asks the user to confirm; this tool never publishes.",
485
+ description: "Prepare a no-publication preview of up to 50 owned memories for group publication. For encrypted accounts the local bridge must be unlocked. The host agent selects exact work-related memory ids, drafts a concise title and responsibility summary from memory evidence, and asks the user to confirm both the profile and publication selection together. This tool never publishes.",
478
486
  inputSchema: {
479
487
  type: "object",
480
488
  properties: {
@@ -488,6 +496,20 @@ export function listToolSpecs(opts = {}) {
488
496
  },
489
497
  },
490
498
  },
499
+ {
500
+ name: canonicalToolNames.updateGroupProfile,
501
+ description: "Save the current member's declared group title and responsibility summary only after the user explicitly confirms the agent's proposal. These become directory facts visible to group members; never save an unconfirmed inference.",
502
+ inputSchema: {
503
+ type: "object",
504
+ properties: {
505
+ displayName: { type: "string" },
506
+ title: { type: "string" },
507
+ responsibilitySummary: { type: "string" },
508
+ confirmed: { type: "boolean", const: true },
509
+ },
510
+ required: ["title", "responsibilitySummary", "confirmed"],
511
+ },
512
+ },
491
513
  {
492
514
  name: canonicalToolNames.completeGroupPublication,
493
515
  description: "Publish only the exact memory ids from a prepared scan after the user explicitly confirms the preview. confirmed must be true. Completing an empty selection safely advances the scan cursor without publishing.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@echomem/mcp",
3
- "version": "1.4.23",
3
+ "version": "1.4.24",
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",