@imenam/mcp-messenger 1.0.10 → 1.0.11

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.
@@ -55,14 +55,14 @@ export const TOOLS = [
55
55
  },
56
56
  {
57
57
  name: "list_threads",
58
- description: "List the conversations visible from this mailbox, most recently active first, with their name, participants and unread count. This is the readable map of the ongoing work: prefer it over list_messages when you want to know what is being discussed rather than what arrived last. Set with_agent to see everything exchanged with one mailbox. A thread with no name falls back to the subject of its opening message — name it with rename_thread.",
58
+ description: "List the conversations visible from this mailbox, most recently active first, with their name, participants and unread count. This is the readable map of the ongoing work: prefer it over list_messages when you want to know what is being discussed rather than what arrived last. Set with_mailbox to see everything exchanged with one correspondent. A thread with no name falls back to the subject of its opening message — name it with rename_thread.",
59
59
  inputSchema: {
60
60
  type: "object",
61
61
  properties: {
62
62
  unread_only: { type: "boolean", description: "Keep only threads with unread messages." },
63
- with_agent: {
63
+ with_mailbox: {
64
64
  type: "string",
65
- description: "Mailbox address of a correspondent: keep only the threads shared with that mailbox. This is how you review what you have exchanged with someone — a conversation is a thread, not a stack of messages.",
65
+ description: "A correspondent's mailbox address, as listed by list_agents: keep only the threads shared with that mailbox. This is how you review what you have exchanged with someone — a conversation is a thread, not a stack of messages.",
66
66
  },
67
67
  search: { type: "string", description: "Free text matched against thread name, description and subject." },
68
68
  limit: { type: "number", description: "1-100, defaults to 25." },
@@ -72,7 +72,7 @@ export const TOOLS = [
72
72
  },
73
73
  {
74
74
  name: "list_agents",
75
- description: "List the other agents known to the mailbox server, grouped by agent name — one group per project, one line per session mailbox inside it, most recently active first. An agent name is shared by every session of the same project, so a group with several lines means several live sessions, each with its own address. Your own mailbox is never listed — you cannot write to yourself. Use it to find the address of the agent you want to write to; pass agent_name to expand a group whose sessions were not all listed.",
75
+ description: "List the mailboxes known to the server, grouped by agent name — one group per project, one line per session mailbox inside it, most recently active first. An agent name is shared by every session of the same project, so a group with several lines means several live sessions, each with its own mailbox address. Your own mailbox is never listed — you cannot write to yourself. Use it to find the mailbox address you want to write to; pass agent_name to expand a group whose sessions were not all listed.",
76
76
  inputSchema: {
77
77
  type: "object",
78
78
  properties: {
@@ -86,11 +86,11 @@ export const TOOLS = [
86
86
  },
87
87
  {
88
88
  name: "send_message",
89
- description: "Send a message to one agent, identified by its mailbox address. A message has a single recipient: to reach several agents, send each one its own message. Set reply_to to answer an existing message and keep the thread together. Never write a mailbox address in the body: the server attaches the sender and the recipient to every message, so an address copied by hand adds nothing and misleads as soon as it is wrong.",
89
+ description: "Send a message to one mailbox, identified by its address. A message has a single recipient: to reach several correspondents, send each one its own message. Set reply_to to answer an existing message and keep the thread together. Never write a mailbox address in the body: the server attaches the sender and the recipient to every message, so an address copied by hand adds nothing and misleads as soon as it is wrong.",
90
90
  inputSchema: {
91
91
  type: "object",
92
92
  properties: {
93
- to: { type: "string", description: "Recipient agent id (mailbox address). Exactly one recipient." },
93
+ to: { type: "string", description: "Recipient mailbox address, as listed by list_agents. Exactly one recipient." },
94
94
  subject: { type: "string", description: "Short subject line." },
95
95
  body: {
96
96
  type: "string",
@@ -110,13 +110,13 @@ export const TOOLS = [
110
110
  },
111
111
  {
112
112
  name: "list_messages",
113
- description: "List messages in this agent's mailbox, newest first, with a short preview. Filter by folder, unread state, sender, thread or free text.",
113
+ description: "List messages in this mailbox, newest first, with a short preview. Filter by folder, unread state, sender, thread or free text.",
114
114
  inputSchema: {
115
115
  type: "object",
116
116
  properties: {
117
117
  folder: { type: "string", description: FOLDER_HINT + " Defaults to inbox." },
118
118
  unread_only: { type: "boolean", description: "Keep only unread messages." },
119
- from: { type: "string", description: "Only messages sent by this agent id." },
119
+ from: { type: "string", description: "Only messages sent by this mailbox address." },
120
120
  thread_id: { type: "string", description: "Only messages of this thread." },
121
121
  search: { type: "string", description: "Free text matched against subject and body." },
122
122
  limit: { type: "number", description: "1-100, defaults to 25." },
@@ -140,7 +140,7 @@ export const TOOLS = [
140
140
  },
141
141
  {
142
142
  name: "get_thread",
143
- description: "Read every message of a thread visible to this agent, oldest first — the full conversation.",
143
+ description: "Read every message of a thread visible to this mailbox, oldest first — the full conversation.",
144
144
  inputSchema: {
145
145
  type: "object",
146
146
  properties: { thread_id: { type: "string", description: "Thread id shown on any message." } },
@@ -177,7 +177,7 @@ export const TOOLS = [
177
177
  },
178
178
  {
179
179
  name: "delete_messages",
180
- description: "Send messages to the trash folder, or erase them from this mailbox for good when permanent is true. Other recipients keep their own copy.",
180
+ description: "Send messages to the trash folder, or erase them from this mailbox for good when permanent is true. The other party keeps its own copy in its own mailbox.",
181
181
  inputSchema: {
182
182
  type: "object",
183
183
  properties: {
@@ -289,6 +289,10 @@ const schemas = {
289
289
  }),
290
290
  list_threads: z.object({
291
291
  unread_only: z.boolean().optional(),
292
+ with_mailbox: z.string().trim().min(1).optional(),
293
+ // Ancien nom du même filtre, plus publié mais toujours accepté : une session l'a pu
294
+ // apprendre avant la mise à jour. Sans cette entrée, zod l'écarterait en silence et
295
+ // l'agent recevrait tous ses fils en croyant les avoir filtrés.
292
296
  with_agent: z.string().trim().min(1).optional(),
293
297
  search: z.string().trim().min(1).optional(),
294
298
  limit: z.number().int().optional(),
@@ -386,8 +390,17 @@ export async function callTool(client, name, rawArgs) {
386
390
  }
387
391
  case "list_threads": {
388
392
  const input = schemas.list_threads.parse(args);
389
- const threads = await client.listThreads(input);
390
- const scope = input.with_agent ? ` with ${input.with_agent}` : "";
393
+ // Une adresse désigne une boîte : les outils disent `with_mailbox`. Le protocole HTTP
394
+ // et le stockage gardent `with_agent`, du nom de la colonne, comme `thread_title`
395
+ // garde son nom face au `thread_name` des outils.
396
+ const correspondent = input.with_mailbox ?? input.with_agent;
397
+ const threads = await client.listThreads({
398
+ unread_only: input.unread_only,
399
+ search: input.search,
400
+ limit: input.limit,
401
+ with_agent: correspondent,
402
+ });
403
+ const scope = correspondent ? ` with ${correspondent}` : "";
391
404
  if (threads.length === 0)
392
405
  return `No thread in this mailbox${scope} matching these filters.`;
393
406
  return [`${threads.length} thread(s)${scope}:`, "", ...threads.map(formatThread)].join("\n");
@@ -396,7 +409,7 @@ export async function callTool(client, name, rawArgs) {
396
409
  const input = schemas.list_agents.parse(args);
397
410
  const agents = await client.listAgents();
398
411
  if (agents.length === 0)
399
- return "No other agent on this server yet.";
412
+ return "No other mailbox on this server yet.";
400
413
  const groups = groupByAgentName(agents);
401
414
  return input.agent_name ? formatAgentGroup(groups, input.agent_name) : formatDirectory(groups);
402
415
  }
@@ -561,7 +574,7 @@ function groupByAgentName(agents) {
561
574
  const groups = new Map();
562
575
  for (const agent of agents) {
563
576
  const name = agent.agent_name?.trim() ?? "";
564
- const key = name ? name.toLowerCase() : `${agent.id}`;
577
+ const key = name ? name.toLowerCase() : `\u0000${agent.id}`;
565
578
  const group = groups.get(key);
566
579
  if (group) {
567
580
  group.mailboxes.push(agent);
@@ -597,8 +610,8 @@ function formatMailboxLine(agent) {
597
610
  const DIRECTORY_FOOTER = [
598
611
  "Addresses identify sessions; agent names do not. Several mailboxes above may carry the same",
599
612
  "agent name as yours — they are other sessions of the same project, never you: your own mailbox",
600
- "is never listed here. Always write to an address taken from this list, never to a name.",
601
- "To see what you have already exchanged with one of them, call list_threads with its address in with_agent.",
613
+ "is never listed here. Always write to a mailbox address taken from this list, never to a name.",
614
+ "To see what you have already exchanged with one of them, call list_threads with its address in with_mailbox.",
602
615
  ];
603
616
  function formatDirectory(groups) {
604
617
  const mailboxes = groups.reduce((total, group) => total + group.mailboxes.length, 0);
@@ -79,6 +79,14 @@ const waitQuerySchema = z.object({
79
79
  watcher: z.enum(["true", "1"]).optional(),
80
80
  });
81
81
  const loginSchema = z.object({ token: z.string().min(1).max(1024) });
82
+ const adminThreadsSchema = z.object({
83
+ search: z.string().trim().min(1).optional(),
84
+ agent: z.string().trim().min(1).optional(),
85
+ unread_only: z.enum(["true", "1"]).optional(),
86
+ priority: z.enum(PRIORITIES).optional(),
87
+ limit: z.coerce.number().int().min(1).max(100).optional(),
88
+ offset: z.coerce.number().int().min(0).optional(),
89
+ });
82
90
  const adminListSchema = z.object({
83
91
  search: z.string().trim().min(1).optional(),
84
92
  agent: z.string().trim().min(1).optional(),
@@ -162,6 +170,20 @@ export function createApp({ store, config }) {
162
170
  admin.get("/messages/:id", (req, res) => {
163
171
  res.json(store.getMessageAdmin(String(req.params.id)));
164
172
  });
173
+ // Déclarée avant `/threads/:id` : la console lit d'abord les fils, le détail ensuite.
174
+ admin.get("/threads", (req, res) => {
175
+ const query = adminThreadsSchema.parse(req.query);
176
+ res.json({
177
+ threads: store.listAllThreadsAdmin({
178
+ search: query.search,
179
+ agent: query.agent,
180
+ unread_only: query.unread_only !== undefined,
181
+ priority: query.priority,
182
+ limit: query.limit,
183
+ offset: query.offset,
184
+ }),
185
+ });
186
+ });
165
187
  admin.get("/threads/:id", (req, res) => {
166
188
  const threadId = String(req.params.id);
167
189
  const messages = store.listThreadAdmin(threadId);