@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.
@@ -10,6 +10,14 @@ export const canonicalToolNames = {
10
10
  sendFriendRequest: "send_friend_request",
11
11
  others: "search_others_memories",
12
12
  publicMemory: "get_public_memory",
13
+ groupContext: "get_group_context",
14
+ createGroup: "create_memory_group",
15
+ createGroupInvite: "create_group_invite",
16
+ joinGroup: "join_memory_group",
17
+ prepareGroupPublication: "prepare_group_publication",
18
+ completeGroupPublication: "complete_group_publication",
19
+ publishToGroup: "publish_memory_to_group",
20
+ publishBatchToGroup: "publish_memories_to_group",
13
21
  report: "echomem_usage_report",
14
22
  updateStatus: "echomem_update_status",
15
23
  contextHealth: "echo_context_health",
@@ -59,9 +67,17 @@ export const timeRangeSchema = z.object({
59
67
  endDate: z.string(),
60
68
  limit: z.number().optional().default(50),
61
69
  });
70
+ const keywordListSchema = z.preprocess((value) => {
71
+ if (typeof value !== "string")
72
+ return value;
73
+ return value
74
+ .split(",")
75
+ .map((keyword) => keyword.trim())
76
+ .filter(Boolean);
77
+ }, z.array(z.string().trim().min(1)).min(1).max(50));
62
78
  export const keywordsSchema = z.object({
63
79
  ...triggerMetadataSchema,
64
- keywords: z.array(z.string()),
80
+ keywords: keywordListSchema,
65
81
  limit: z.number().optional().default(10),
66
82
  });
67
83
  export const listFriendsSchema = z.object({
@@ -94,6 +110,56 @@ export const publicMemorySchema = z.object({
94
110
  ...triggerMetadataSchema,
95
111
  memoryId: z.string().min(1),
96
112
  });
113
+ export const groupContextSchema = z.object({
114
+ ...triggerMetadataSchema,
115
+ });
116
+ export const createGroupSchema = z.object({
117
+ ...triggerMetadataSchema,
118
+ name: z.string().min(1).max(120),
119
+ description: z.string().max(1000).optional(),
120
+ displayName: z.string().min(1).max(120),
121
+ title: z.string().max(160).optional(),
122
+ responsibilitySummary: z.string().max(1000).optional(),
123
+ });
124
+ export const createGroupInviteSchema = z.object({
125
+ ...triggerMetadataSchema,
126
+ expiresInDays: z.number().int().min(1).max(30).optional(),
127
+ maxUses: z.number().int().min(1).max(100).optional(),
128
+ });
129
+ export const joinGroupSchema = z.object({
130
+ ...triggerMetadataSchema,
131
+ code: z.string().min(30),
132
+ displayName: z.string().min(1).max(120),
133
+ title: z.string().max(160).optional(),
134
+ responsibilitySummary: z.string().max(1000).optional(),
135
+ });
136
+ export const prepareGroupPublicationSchema = z.object({
137
+ ...triggerMetadataSchema,
138
+ scope: z.enum(["bootstrap", "since_last_scan", "context", "time_range"]).default("since_last_scan"),
139
+ contextId: z.string().min(1).optional(),
140
+ startAt: z.string().optional(),
141
+ endAt: z.string().optional(),
142
+ lookbackDays: z.number().int().min(1).max(365).optional(),
143
+ limit: z.number().int().min(1).max(50).optional(),
144
+ instruction: z.string().max(500).optional(),
145
+ });
146
+ export const completeGroupPublicationSchema = z.object({
147
+ ...triggerMetadataSchema,
148
+ scanId: z.string().min(1),
149
+ memoryIds: z.array(z.string().min(1)).max(50),
150
+ confirmed: z.literal(true),
151
+ selectionReason: z.string().max(500).optional(),
152
+ });
153
+ export const publishToGroupSchema = z.object({
154
+ ...triggerMetadataSchema,
155
+ memoryId: z.string().min(1),
156
+ });
157
+ export const publishBatchToGroupSchema = z.object({
158
+ ...triggerMetadataSchema,
159
+ memoryIds: z.array(z.string().min(1)).min(1).max(50),
160
+ contextId: z.string().min(1).optional(),
161
+ selectionReason: z.string().max(500).optional(),
162
+ });
97
163
  export const deleteMemorySchema = z.object({
98
164
  memoryId: z.string().min(1),
99
165
  confirmed: z.boolean().optional().default(false),
@@ -108,7 +174,7 @@ export function listToolSpecs(opts = {}) {
108
174
  const currentTime = new Date().toISOString();
109
175
  const map = opts.map?.trim();
110
176
  const updateNotice = opts.updateNotice?.trim();
111
- const recallPlanNote = "Available on every plan: Free includes 10 searches each week, Pro includes 100, and Power includes 250.";
177
+ const recallPlanNote = "Available on every plan: Free includes 100 searches each week, Pro includes 500, and Power includes 2,000.";
112
178
  const searchBillingReplyInstruction = "If search returns an ACTION REQUIRED subscription message, tell the user to start their trial or subscription and include the exact URL from that result verbatim. Do not respond only with \"connect\" or \"upgrade\".";
113
179
  const updateSection = updateNotice ? `\n\nUPDATE NOTICE: ${updateNotice}` : "";
114
180
  const mapSection = map
@@ -204,11 +270,27 @@ export function listToolSpecs(opts = {}) {
204
270
  },
205
271
  {
206
272
  name: canonicalToolNames.keywords,
207
- description: `Search memories based on keywords in keys field. ${recallPlanNote}`,
273
+ description: `Search memories based on keywords in keys field. Pass keywords as valid JSON: preferably an array of quoted strings, for example {"keywords":["flow-lab","flow.html","Rive"],"limit":8}. A comma-separated JSON string is also accepted as a compatibility fallback. Never emit bare comma-separated tokens. ${recallPlanNote}`,
208
274
  inputSchema: {
209
275
  type: "object",
210
276
  properties: {
211
- keywords: { type: "array", items: { type: "string" } },
277
+ keywords: {
278
+ oneOf: [
279
+ {
280
+ type: "array",
281
+ minItems: 1,
282
+ maxItems: 50,
283
+ items: { type: "string", minLength: 1 },
284
+ },
285
+ {
286
+ type: "string",
287
+ minLength: 1,
288
+ description: "Compatibility form: one comma-separated JSON string, such as \"flow-lab, flow.html, Rive\".",
289
+ },
290
+ ],
291
+ description: "Use a JSON array of quoted strings. Do not pass unquoted comma-separated tokens.",
292
+ examples: [["flow-lab", "flow.html", "Rive"]],
293
+ },
212
294
  limit: { type: "number", default: 10 },
213
295
  triggerMessage: {
214
296
  type: "string",
@@ -274,7 +356,7 @@ export function listToolSpecs(opts = {}) {
274
356
  },
275
357
  {
276
358
  name: canonicalToolNames.others,
277
- description: "Search accepted friends' public memories. Returned memories are recorded in the existing memory_views pipeline for the memory owners.",
359
+ description: "Search public memories from accepted friends or people who share your company group. For onboarding and division-of-work questions, call get_group_context first, then use this tool for current evidence. Returned memories are recorded in memory_views for the owners.",
278
360
  inputSchema: {
279
361
  type: "object",
280
362
  properties: {
@@ -282,25 +364,25 @@ export function listToolSpecs(opts = {}) {
282
364
  limit: { type: "number", default: 10 },
283
365
  target: {
284
366
  type: "string",
285
- description: "Accepted-friend user id or exact display name/username. Prefer this for @Name asks.",
367
+ description: "Accessible friend or group-member user id or exact display name. Prefer this for @Name asks.",
286
368
  },
287
369
  ownerUserId: {
288
370
  type: "string",
289
- description: "Optional accepted-friend user id to scope the search to one friend.",
371
+ description: "Optional accessible user id to scope the search to one person.",
290
372
  },
291
373
  ownerName: {
292
374
  type: "string",
293
- description: "Optional accepted-friend display name/username to scope the search to one friend.",
375
+ description: "Optional accessible friend or group-member display name to scope the search.",
294
376
  },
295
377
  targetFriendIds: {
296
378
  type: "array",
297
379
  items: { type: "string" },
298
- description: "Optional accepted-friend user ids to scope the search.",
380
+ description: "Legacy field for optional accessible friend or group-member user ids.",
299
381
  },
300
382
  targetFriendNames: {
301
383
  type: "array",
302
384
  items: { type: "string" },
303
- description: "Optional accepted-friend display names/usernames to scope the search.",
385
+ description: "Legacy field for optional accessible friend or group-member display names.",
304
386
  },
305
387
  recordAccess: {
306
388
  type: "boolean",
@@ -319,7 +401,7 @@ export function listToolSpecs(opts = {}) {
319
401
  },
320
402
  {
321
403
  name: canonicalToolNames.publicMemory,
322
- description: "Fetch one accepted friend's public memory by id. If the caller is not the owner, EchoMem records the access in the existing memory_views pipeline.",
404
+ description: "Fetch one public memory by id when its owner is an accepted friend or shares your company group. If the caller is not the owner, EchoMem records the access in memory_views.",
323
405
  inputSchema: {
324
406
  type: "object",
325
407
  properties: {
@@ -336,6 +418,139 @@ export function listToolSpecs(opts = {}) {
336
418
  required: ["memoryId"],
337
419
  },
338
420
  },
421
+ {
422
+ name: canonicalToolNames.groupContext,
423
+ description: "Get your current company group, its participant directory, declared titles and responsibilities, and published-memory coverage. Use this before answering who works on what or suggesting where a new group member could contribute. Treat declared profile fields as facts and memory-derived work as evidence or inference.",
424
+ inputSchema: {
425
+ type: "object",
426
+ properties: {
427
+ triggerMessage: {
428
+ type: "string",
429
+ description: "Optional user message that caused this group-orientation lookup.",
430
+ },
431
+ triggerMessageRole: { type: "string", default: "user" },
432
+ },
433
+ },
434
+ },
435
+ {
436
+ name: canonicalToolNames.createGroup,
437
+ description: "Create one company memory group for the current user. The MVP allows at most one group per user. Call only after the user explicitly asks to create a group.",
438
+ inputSchema: {
439
+ type: "object",
440
+ properties: {
441
+ name: { type: "string" },
442
+ description: { type: "string" },
443
+ displayName: { type: "string" },
444
+ title: { type: "string" },
445
+ responsibilitySummary: { type: "string" },
446
+ },
447
+ required: ["name", "displayName"],
448
+ },
449
+ },
450
+ {
451
+ name: canonicalToolNames.createGroupInvite,
452
+ description: "Generate a secret, expiring invite code for the current group. Only the group creator may call this in the MVP. Return the code only to the user so they can share it intentionally; never log it.",
453
+ inputSchema: {
454
+ type: "object",
455
+ properties: {
456
+ expiresInDays: { type: "number", default: 7 },
457
+ maxUses: { type: "number", default: 20 },
458
+ },
459
+ },
460
+ },
461
+ {
462
+ 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.",
464
+ inputSchema: {
465
+ type: "object",
466
+ properties: {
467
+ code: { type: "string" },
468
+ displayName: { type: "string" },
469
+ title: { type: "string" },
470
+ responsibilitySummary: { type: "string" },
471
+ },
472
+ required: ["code", "displayName"],
473
+ },
474
+ },
475
+ {
476
+ 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.",
478
+ inputSchema: {
479
+ type: "object",
480
+ properties: {
481
+ scope: { type: "string", enum: ["bootstrap", "since_last_scan", "context", "time_range"], default: "since_last_scan" },
482
+ contextId: { type: "string" },
483
+ startAt: { type: "string" },
484
+ endAt: { type: "string" },
485
+ lookbackDays: { type: "number", default: 30 },
486
+ limit: { type: "number", default: 50 },
487
+ instruction: { type: "string", default: "work-related" },
488
+ },
489
+ },
490
+ },
491
+ {
492
+ name: canonicalToolNames.completeGroupPublication,
493
+ 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.",
494
+ inputSchema: {
495
+ type: "object",
496
+ properties: {
497
+ scanId: { type: "string" },
498
+ memoryIds: { type: "array", maxItems: 50, items: { type: "string" } },
499
+ confirmed: { type: "boolean", const: true },
500
+ selectionReason: { type: "string" },
501
+ },
502
+ required: ["scanId", "memoryIds", "confirmed"],
503
+ },
504
+ },
505
+ {
506
+ name: canonicalToolNames.publishToGroup,
507
+ description: "Publish one exact memory snapshot to your current company group without changing the memory's global is_public or encryption state. Call only after the user explicitly asks to publish or push that memory. The operation is idempotent.",
508
+ inputSchema: {
509
+ type: "object",
510
+ properties: {
511
+ memoryId: {
512
+ type: "string",
513
+ description: "Exact id of a memory owned by the current user.",
514
+ },
515
+ triggerMessage: {
516
+ type: "string",
517
+ description: "Optional user message that explicitly requested publication.",
518
+ },
519
+ triggerMessageRole: { type: "string", default: "user" },
520
+ },
521
+ required: ["memoryId"],
522
+ },
523
+ },
524
+ {
525
+ name: canonicalToolNames.publishBatchToGroup,
526
+ description: "Publish up to 50 exact memory snapshots to your current company group without changing global is_public or the encrypted originals. For a session request, first call get_memories_by_context, select the exact ids that match the user's instruction, then pass those ids with the same contextId. Call only after explicit group-publication intent; report what was selected when the user delegates work-related filtering.",
527
+ inputSchema: {
528
+ type: "object",
529
+ properties: {
530
+ memoryIds: {
531
+ type: "array",
532
+ minItems: 1,
533
+ maxItems: 50,
534
+ items: { type: "string" },
535
+ description: "Exact ids owned by the current user.",
536
+ },
537
+ contextId: {
538
+ type: "string",
539
+ description: "Optional session context that every supplied memory must belong to.",
540
+ },
541
+ selectionReason: {
542
+ type: "string",
543
+ description: "Short explanation to return when the agent selected a subset, such as work-related memories.",
544
+ },
545
+ triggerMessage: {
546
+ type: "string",
547
+ description: "Optional user message that explicitly requested publication.",
548
+ },
549
+ triggerMessageRole: { type: "string", default: "user" },
550
+ },
551
+ required: ["memoryIds"],
552
+ },
553
+ },
339
554
  {
340
555
  name: canonicalToolNames.delete,
341
556
  description: "Delete one of the user's EchoMem memories only after explicit user confirmation. First call with memoryId and confirmed omitted/false to preview the target and receive a confirmationToken; this first call never deletes. Only call again with confirmed=true and that exact confirmationToken after the user explicitly confirms deletion. Deletes the memory row only; raw source_of_truth conversation records are preserved.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@echomem/mcp",
3
- "version": "1.4.21",
3
+ "version": "1.4.23",
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",