@imenam/mcp-messenger 1.0.1 → 1.0.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/.env.example CHANGED
@@ -15,7 +15,7 @@ MESSENGER_PORT=3600
15
15
 
16
16
  # Jeton exigé sur TOUTE requête entrante (API, /health, console web).
17
17
  # OBLIGATOIRE : sans MESSENGER_TOKEN ni MESSENGER_AGENT_TOKENS, le serveur refuse
18
- # de démarrer. Générer par exemple avec : node -e "console.log(crypto.randomUUID())"
18
+ # de démarrer. Générer avec : npx @imenam/mcp-messenger --generate-token
19
19
  MESSENGER_TOKEN=change-me
20
20
 
21
21
  # Alternative plus stricte : un jeton par agent (prioritaire sur MESSENGER_TOKEN).
package/README.md CHANGED
@@ -37,7 +37,10 @@ identifiée par une adresse opaque du type `box_9f2c4a01b3d7e5f8` — voir
37
37
  ### 1. Lancer la boîte aux lettres
38
38
 
39
39
  ```bash
40
- MESSENGER_TOKEN=$(node -e "console.log(crypto.randomUUID())") npx @imenam/mcp-messenger --server
40
+ # Un jeton, à conserver : il sera demandé au serveur, aux agents et à la console.
41
+ npx @imenam/mcp-messenger --generate-token
42
+
43
+ MESSENGER_TOKEN=<le-jeton> npx @imenam/mcp-messenger --server
41
44
  # → listening on http://127.0.0.1:3600 (loopback only)
42
45
  # → auth: shared token — required on every request
43
46
  # → web console: http://127.0.0.1:3600/ui/?token=<MESSENGER_ADMIN_TOKEN>
@@ -93,7 +96,9 @@ la session MCP démarre quand même et l'enregistrement se fera au premier appel
93
96
 
94
97
  L'agent A appelle `send_message` avec `to: ["coder"]`, l'agent B voit le message via
95
98
  `list_messages` puis `read_message`, et répond avec `reply_to` pour rester dans le fil.
96
- `wait_for_message` permet d'attendre une réponse sans faire tourner une boucle d'appels.
99
+ `wait_for_message` permet d'attendre une réponse sans faire tourner une boucle d'appels : il ne
100
+ rend la main que sur un message arrivé *après* le début de l'attente, les non-lus déjà en boîte
101
+ ne l'interrompent pas.
97
102
 
98
103
  ---
99
104
 
@@ -114,7 +119,7 @@ L'agent A appelle `send_message` avec `to: ["coder"]`, l'agent B voit le message
114
119
  | `delete_messages` | Corbeille, ou suppression définitive (`permanent: true`) |
115
120
  | `list_folders` | Dossiers avec totaux et non-lus |
116
121
  | `create_folder` / `delete_folder` | Organiser l'espace comme une messagerie |
117
- | `wait_for_message` | Attente longue (jusqu'à 300 s) jusqu'à l'arrivée d'un message |
122
+ | `wait_for_message` | Attente longue (jusqu'à 300 s) jusqu'à l'arrivée d'un message *postérieur* à l'appel |
118
123
 
119
124
  **Dossiers système** — toujours présents, non supprimables : `inbox`, `sent`, `archive`,
120
125
  `trash`. Les dossiers personnalisés sont propres à chaque boîte.
@@ -203,7 +208,7 @@ Toutes les variables sont documentées dans [`.env.example`](.env.example). L'es
203
208
  | `MESSENGER_PORT` | `3600` | Port d'écoute |
204
209
  | `MESSENGER_HOST` | `127.0.0.1` (figé) | Non configurable ; une valeur non-loopback fait échouer le démarrage |
205
210
  | `MESSENGER_DB_PATH` | `./data/messenger.db` | Fichier SQLite |
206
- | `MESSENGER_TOKEN` | — | **Obligatoire** (sauf si `MESSENGER_AGENT_TOKENS`) : jeton exigé sur toute requête |
211
+ | `MESSENGER_TOKEN` | — | **Obligatoire** (sauf si `MESSENGER_AGENT_TOKENS`) : jeton exigé sur toute requête. `--generate-token` en produit un |
207
212
  | `MESSENGER_AGENT_TOKENS` | — | Un jeton par agent : `planner:tok-a,coder:tok-b` |
208
213
  | `MESSENGER_ADMIN_TOKEN` | `MESSENGER_TOKEN` | Jeton de la console ; obligatoire si seul `MESSENGER_AGENT_TOKENS` est défini |
209
214
  | `MESSENGER_RATE_LIMIT_MAX` | `600` | Requêtes par IP et par fenêtre |
@@ -278,6 +283,7 @@ cet agent lorsque `MESSENGER_AGENT_TOKENS` est utilisé.
278
283
 
279
284
  ```bash
280
285
  npm install
286
+ npm run generate-token # jeton aléatoire (32 octets, base64url)
281
287
  npm run build # tsc + copie des fichiers de la console web
282
288
  npm test # build puis suite Vitest complète
283
289
  npm run test:watch
@@ -303,7 +309,7 @@ test/ # store, config, API HTTP, outils MCP, binaire réel
303
309
 
304
310
  ### Tests
305
311
 
306
- 81 tests couvrant : le stockage et l'isolation des boîtes, la validation de configuration,
312
+ 86 tests couvrant : le stockage et l'isolation des boîtes, la validation de configuration,
307
313
  l'API HTTP et son authentification (refus systématique sans jeton, jeton partagé, jetons par
308
314
  agent, jeton de console distinct), les outils MCP de bout en bout (deux agents qui
309
315
  conversent à travers un vrai serveur), l'allocation des boîtes par session — deux processus
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Génère un jeton aléatoire cryptographiquement sûr.
3
+ *
4
+ * L'encodage `base64url` est choisi pour que le jeton passe tel quel dans l'URL de la
5
+ * console web (`/ui/?token=…`) : ni `+`, ni `/`, ni `=` à échapper.
6
+ */
7
+ export declare function generateToken(bytes?: number): string;
@@ -0,0 +1,12 @@
1
+ import { randomBytes } from "node:crypto";
2
+ /** 32 octets : hors de portée d'une attaque par force brute, même sans rate limiting. */
3
+ const DEFAULT_BYTES = 32;
4
+ /**
5
+ * Génère un jeton aléatoire cryptographiquement sûr.
6
+ *
7
+ * L'encodage `base64url` est choisi pour que le jeton passe tel quel dans l'URL de la
8
+ * console web (`/ui/?token=…`) : ni `+`, ni `/`, ni `=` à échapper.
9
+ */
10
+ export function generateToken(bytes = DEFAULT_BYTES) {
11
+ return randomBytes(bytes).toString("base64url");
12
+ }
package/dist/src/index.js CHANGED
@@ -10,6 +10,8 @@ Usage:
10
10
  mcp-messenger --server Start the mailbox server (HTTP API + web console).
11
11
  mcp-messenger --claude-setup-mcp [--force]
12
12
  Write the messenger entry into ./.mcp.json.
13
+ mcp-messenger --generate-token
14
+ Print a fresh random token for MESSENGER_TOKEN.
13
15
  mcp-messenger --version Print the version.
14
16
  mcp-messenger --help Print this help.
15
17
 
@@ -28,6 +30,21 @@ async function main() {
28
30
  process.stdout.write(`${APP_VERSION}\n`);
29
31
  return;
30
32
  }
33
+ // Le jeton part seul sur stdout : la commande reste utilisable dans un pipe
34
+ // (`mcp-messenger --generate-token > token.txt`), les conseils vont sur stderr.
35
+ if (args.includes("--generate-token")) {
36
+ const { generateToken } = await import("./cli/generate-token.js");
37
+ process.stdout.write(`${generateToken()}\n`);
38
+ process.stderr.write([
39
+ "",
40
+ "Ce jeton doit être identique aux trois endroits suivants :",
41
+ " 1. le serveur : MESSENGER_TOKEN",
42
+ " 2. chaque agent : MESSENGER_TOKEN dans son .mcp.json",
43
+ " 3. la console web : http://127.0.0.1:3600/ui/?token=<jeton>",
44
+ "",
45
+ ].join("\n"));
46
+ return;
47
+ }
31
48
  // Traité avant toute initialisation : la commande écrit dans le répertoire de
32
49
  // l'utilisateur, elle n'ouvre ni base ni serveur.
33
50
  if (args.includes("--claude-setup-mcp")) {
@@ -168,7 +168,7 @@ export const TOOLS = [
168
168
  },
169
169
  {
170
170
  name: "wait_for_message",
171
- description: "Block until a new message lands in the mailbox, or until the timeout expires. Use it to wait for another agent's answer instead of polling list_messages.",
171
+ description: "Block until a message arrives AFTER this call starts, or until the timeout expires. Messages already in the mailbox — even unread ones — do not end the wait. Use it to wait for another agent's answer instead of polling list_messages.",
172
172
  inputSchema: {
173
173
  type: "object",
174
174
  properties: {
@@ -186,16 +186,15 @@ export function createApp({ store, config }) {
186
186
  api.delete("/folders/:name", (req, res) => {
187
187
  res.json({ moved: store.deleteFolder(req.agentId, String(req.params.name)) });
188
188
  });
189
- // Attente longue : la réponse part dès qu'un message arrive, sinon au bout du délai.
189
+ // Attente longue : seuls les messages arrivés APRÈS le début de l'attente réveillent
190
+ // l'appelant ; les non-lus déjà présents sont ignorés (sinon l'outil rendrait la main
191
+ // immédiatement tant que la boîte n'est pas vidée).
190
192
  api.get("/wait", (req, res) => {
191
193
  const { timeout_seconds, folder } = waitQuerySchema.parse(req.query);
192
194
  const agentId = req.agentId;
193
195
  const timeoutSeconds = Math.min(timeout_seconds ?? 30, config.maxWaitSeconds);
194
- const pending = store.listMessages(agentId, { folder: folder ?? "inbox", unread_only: true, limit: 1 });
195
- const first = pending[0];
196
- if (first) {
197
- res.json({ timed_out: false, message: store.getMessage(agentId, first.message_id, false) });
198
- return;
196
+ if (folder && !store.listFolders(agentId).some((f) => f.name === folder)) {
197
+ throw new MessengerError(`unknown folder: ${folder}`, 404);
199
198
  }
200
199
  const done = (payload) => {
201
200
  clearTimeout(timer);
@@ -1 +1 @@
1
- export declare const APP_VERSION = "1.0.1";
1
+ export declare const APP_VERSION = "1.0.2";
@@ -1,2 +1,2 @@
1
1
  // Généré par scripts/generate-version.js — ne pas modifier à la main.
2
- export const APP_VERSION = "1.0.1";
2
+ export const APP_VERSION = "1.0.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imenam/mcp-messenger",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "main": "dist/src/index.js",
5
5
  "scripts": {
6
6
  "test": "npm run build && vitest run",
@@ -11,7 +11,8 @@
11
11
  "release": "npm version patch && npm publish && git push --follow-tags",
12
12
  "typecheck": "tsc --noEmit",
13
13
  "test:watch": "vitest",
14
- "test:coverage": "vitest run --coverage"
14
+ "test:coverage": "vitest run --coverage",
15
+ "generate-token": "node dist/src/index.js --generate-token"
15
16
  },
16
17
  "keywords": [
17
18
  "mcp",