@imenam/mcp-http-gateway 2.0.20 → 2.0.21

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
@@ -111,6 +111,14 @@ npm start
111
111
 
112
112
  Le gateway connecte chaque MCP configuré et démarre le serveur HTTP. Les MCPs en échec au démarrage sont ignorés sans bloquer les autres.
113
113
 
114
+ Le `stderr` de chaque MCP est recopié sur celui du gateway, ligne par ligne, préfixé par le nom du MCP sous forme de chaîne JSON :
115
+
116
+ ```
117
+ [mcp "Ticket PXP"] [ProxyClient] Mount path resolved to /jf5gh9dqct/ticket-manager (requested: /ticket-manager)
118
+ ```
119
+
120
+ Un superviseur qui lit le journal du gateway sait ainsi quel MCP a écrit chaque ligne.
121
+
114
122
  ## Rechargement à chaud
115
123
 
116
124
  Avec `GATEWAY_ADMIN_TOKEN` défini, le gateway relit `mcp-servers.json` sur demande, sans redémarrer :
@@ -556,11 +564,13 @@ La convention, côté MCP :
556
564
  jeton du gateway, route, outil, arguments, point de reprise. Il rend à l'agent, à la place
557
565
  de la réponse du MCP, l'appel `Monitor` à faire :
558
566
  ```text
559
- command: "<node>" "<…>/mcp-http-gateway/server.js" --watch "<…>/watch/<id>.json"
567
+ command: npx -y @imenam/mcp-http-gateway@<version du relais> --watch "<…>/watch/<id>.json"
560
568
  timeout_ms: 1800000
561
569
  ```
562
- Le jeton ne figure jamais dans la commande, donc jamais dans la conversation.
563
- 4. Le veilleur (`server.js --watch <fichier>`) appelle l'outil d'attente avec ses arguments,
570
+ Le jeton ne figure jamais dans la commande, donc jamais dans la conversation. La commande
571
+ lance le veilleur par npx, dans la version exacte du relais : elle ne dépend d'aucun chemin
572
+ du cache de npx.
573
+ 4. Le veilleur (`mcp-http-gateway --watch <fichier>`) appelle l'outil d'attente avec ses arguments,
564
574
  plus `cursor` et `timeout_seconds: 50`. L'outil répond, dans `_meta["gateway/events"]` :
565
575
  ```json
566
576
  { "cursor": "58", "lines": ["[telegram] new post 42: … — read it with get_post"] }
package/gateway-watch.js CHANGED
@@ -51,7 +51,8 @@ const BACKOFF_MAX_MS = 30_000;
51
51
  /** Échecs consécutifs au-delà desquels l'agent est prévenu que la veille est interrompue. */
52
52
  const FAILURES_BEFORE_NOTICE = 3;
53
53
 
54
- const SERVER_SCRIPT = path.join(path.dirname(fileURLToPath(import.meta.url)), 'server.js');
54
+ /** Nom et version de ce paquet, qui fixent la commande du veilleur. */
55
+ const PACKAGE = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'package.json'), 'utf8'));
55
56
 
56
57
  export function watchDir() {
57
58
  const override = process.env.GATEWAY_WATCH_DIR?.trim();
@@ -123,10 +124,10 @@ function activeWatcher(file) {
123
124
  return presence && typeof presence.pid === 'number' && processExists(presence.pid) ? presence : null;
124
125
  }
125
126
 
126
- /** Commande du veilleur : le même Node et le même paquet que ce relais. */
127
+ /** Commande du veilleur : ce paquet, lancé par npx dans la version exacte de ce relais. */
127
128
  export function watchCommand(file) {
128
129
  const quote = (value) => `"${value.replace(/\\/g, '/')}"`;
129
- return `${quote(process.execPath)} ${quote(SERVER_SCRIPT)} --watch ${quote(file)}`;
130
+ return `npx -y ${PACKAGE.name}@${PACKAGE.version} --watch ${quote(file)}`;
130
131
  }
131
132
 
132
133
  /** Fichiers de veille écrits par ce relais, retirés à son arrêt. */
package/mcp-registry.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import crypto from 'crypto';
2
+ import readline from 'readline';
2
3
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
4
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
4
5
  import { buildProxyTools, overlongProxyNames, parseExposeFlag } from './startup-exposition.js';
@@ -44,12 +45,27 @@ function buildChildEnv(cfgEnv = {}) {
44
45
  return { ...safeEnv, ...cfgEnv };
45
46
  }
46
47
 
48
+ /** Préfixe des lignes de stderr d'un MCP : `[mcp "<nom>"] `, le nom en chaîne JSON. */
49
+ export function stderrPrefix(name) {
50
+ return `[mcp ${JSON.stringify(name)}] `;
51
+ }
52
+
53
+ /** Recopie le stderr d'un MCP sur celui du gateway, ligne par ligne, chacune préfixée par le nom du MCP. */
54
+ function relayStderr(name, stream) {
55
+ const prefix = stderrPrefix(name);
56
+ readline.createInterface({ input: stream, crlfDelay: Infinity }).on('line', (line) => {
57
+ process.stderr.write(`${prefix}${line}\n`);
58
+ });
59
+ }
60
+
47
61
  async function connectMcp(name, cfg) {
48
62
  const transport = new StdioClientTransport({
49
63
  command: cfg.command,
50
64
  args: cfg.args ?? [],
51
65
  env: buildChildEnv(cfg.env),
66
+ stderr: 'pipe',
52
67
  });
68
+ relayStderr(name, transport.stderr);
53
69
  const client = new Client({ name: 'mcp-gateway', version: '1.0.0' }, { capabilities: {} });
54
70
  await client.connect(transport);
55
71
  console.log(`[gateway] Connected: ${name} → ${cfg.env?.MCP_SERVER_ROUTE}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imenam/mcp-http-gateway",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "type": "module",
5
5
  "description": "HTTP gateway that exposes MCP servers as REST routes — bridge between MCP and any HTTP-capable agent",
6
6
  "main": "server.js",