@imenam/mcp-http-gateway 2.0.14 → 2.0.15

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/package.json CHANGED
@@ -1,54 +1,57 @@
1
- {
2
- "name": "@imenam/mcp-http-gateway",
3
- "version": "2.0.14",
4
- "type": "module",
5
- "description": "HTTP gateway that exposes MCP servers as REST routes — bridge between MCP and any HTTP-capable agent",
6
- "main": "server.js",
7
- "bin": {
8
- "mcp-http-gateway": "server.js"
9
- },
10
- "files": [
11
- "server.js",
12
- "mcp-mode.js",
13
- "mcp-endpoint.js",
14
- "auth.js",
15
- "README.md",
16
- "embeddings/",
17
- "startup-exposition.js"
18
- ],
19
- "scripts": {
20
- "start": "node server.js",
21
- "release": "npm version patch && npm publish --access public && git push origin master --follow-tags"
22
- },
23
- "keywords": [
24
- "mcp",
25
- "model-context-protocol",
26
- "gateway",
27
- "http",
28
- "ai",
29
- "agent",
30
- "llm",
31
- "bridge"
32
- ],
33
- "author": "Matthieu Pesnot-Pin <matthieupin@gmail.com>",
34
- "repository": {
35
- "type": "git",
36
- "url": "https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway.git"
37
- },
38
- "bugs": {
39
- "url": "https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway/issues"
40
- },
41
- "homepage": "https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway#readme",
42
- "license": "ISC",
43
- "engines": {
44
- "node": ">=18"
45
- },
46
- "dependencies": {
47
- "@modelcontextprotocol/sdk": "^1.27.1",
48
- "better-sqlite3": "^12.8.0",
49
- "dotenv": "^17.3.1",
50
- "express": "^5.2.1",
51
- "jose": "^6.2.3",
52
- "openai": "^6.31.0"
53
- }
54
- }
1
+ {
2
+ "name": "@imenam/mcp-http-gateway",
3
+ "version": "2.0.15",
4
+ "type": "module",
5
+ "description": "HTTP gateway that exposes MCP servers as REST routes — bridge between MCP and any HTTP-capable agent",
6
+ "main": "server.js",
7
+ "bin": {
8
+ "mcp-http-gateway": "server.js"
9
+ },
10
+ "files": [
11
+ "server.js",
12
+ "mcp-mode.js",
13
+ "mcp-endpoint.js",
14
+ "auth.js",
15
+ "README.md",
16
+ "embeddings/",
17
+ "startup-exposition.js",
18
+ "http-relay.js",
19
+ "gateway-exec.js",
20
+ "gateway-watch.js"
21
+ ],
22
+ "scripts": {
23
+ "start": "node server.js",
24
+ "release": "npm version patch && npm publish --access public && git push origin master --follow-tags"
25
+ },
26
+ "keywords": [
27
+ "mcp",
28
+ "model-context-protocol",
29
+ "gateway",
30
+ "http",
31
+ "ai",
32
+ "agent",
33
+ "llm",
34
+ "bridge"
35
+ ],
36
+ "author": "Matthieu Pesnot-Pin <matthieupin@gmail.com>",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway/issues"
43
+ },
44
+ "homepage": "https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway#readme",
45
+ "license": "ISC",
46
+ "engines": {
47
+ "node": ">=18"
48
+ },
49
+ "dependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.27.1",
51
+ "better-sqlite3": "^12.8.0",
52
+ "dotenv": "^17.3.1",
53
+ "express": "^5.2.1",
54
+ "jose": "^6.2.3",
55
+ "openai": "^6.31.0"
56
+ }
57
+ }
package/server.js CHANGED
@@ -1,7 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import 'dotenv/config';
3
3
 
4
- if (process.argv.includes('--mcp')) {
4
+ if (process.argv.includes('--watch')) {
5
+ // Veilleur lancé par l'agent via Monitor (voir gateway-watch.js) : il n'ouvre ni serveur ni relais.
6
+ const { runWatchMode } = await import('./gateway-watch.js');
7
+ await runWatchMode(process.argv[process.argv.indexOf('--watch') + 1]).catch((err) => {
8
+ console.error(`[gateway-watch] ${err.message}`);
9
+ process.exit(1);
10
+ });
11
+ } else if (process.argv.includes('--mcp')) {
5
12
  await import('./mcp-mode.js');
6
13
  // Process stays alive: the MCP transport keeps stdin open until the client disconnects
7
14
  }
@@ -16,6 +23,7 @@ import { getEmbedding, cosineSimilarity } from './embeddings/vectorizer.js';
16
23
  import { registerMcpEndpoint } from './mcp-endpoint.js';
17
24
  import { buildProxyTools, overlongProxyNames, parseExposeFlag } from './startup-exposition.js';
18
25
  import { createMcpTokenVerifier } from './auth.js';
26
+ import { HTTP_RELAY_PORT_KEY, parseRelayPort, relayPrefix, isRelayPath, createHttpRelay } from './http-relay.js';
19
27
  import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
20
28
  import { rateLimit } from 'express-rate-limit';
21
29
 
@@ -118,7 +126,12 @@ async function main() {
118
126
  // contenu d'un fichier encodé en base64 (cf. l'inlining du relais dans mcp-mode.js),
119
127
  // qui repartirait sinon en « 413 Payload Too Large » sans explication.
120
128
  const BODY_LIMIT = process.env.GATEWAY_BODY_LIMIT ?? '32mb';
121
- app.use(express.json({ type: '*/*', limit: BODY_LIMIT }));
129
+ const jsonParser = express.json({ type: '*/*', limit: BODY_LIMIT });
130
+ // Les routes de relais HTTP transmettent leur corps en flux, sans le lire ni le borner.
131
+ const relayPrefixes = new Set();
132
+ app.use((req, res, next) =>
133
+ isRelayPath(relayPrefixes, req.path) ? next() : jsonParser(req, res, next)
134
+ );
122
135
 
123
136
  const globalWindowMin = parseInt(process.env.RATE_LIMIT_GLOBAL_WINDOW_MINUTES ?? '15', 10);
124
137
  const globalMax = parseInt(process.env.RATE_LIMIT_GLOBAL_MAX ?? '300', 10);
@@ -201,12 +214,25 @@ async function main() {
201
214
  console.warn(`[gateway] "${name}": invalid EXPOSE_AT_AGENT_STARTUP="${rawExpose}" (expected true | false) — not pre-exposed`);
202
215
  }
203
216
 
217
+ const rawRelayPort = cfg.env?.[HTTP_RELAY_PORT_KEY];
218
+ const { port: relayPort, valid: relayPortValid } = parseRelayPort(rawRelayPort);
219
+ if (!relayPortValid) {
220
+ console.error(`[gateway] Skipping "${name}": invalid ${HTTP_RELAY_PORT_KEY}="${rawRelayPort}" (expected a port 1-65535)`);
221
+ continue;
222
+ }
223
+
204
224
  try {
205
225
  const client = await connectMcp(name, cfg);
206
226
  const { tools } = await client.listTools();
207
227
  clients.push(client);
208
- registry.push({ name, route, description: cfg.description ?? null, tools, client, exposeAtStartup });
228
+ registry.push({ name, route, description: cfg.description ?? null, tools, client, exposeAtStartup, relayPort });
209
229
  registerMcpRoutes(app, route, client, name);
230
+ if (relayPort) {
231
+ const prefix = relayPrefix(route);
232
+ relayPrefixes.add(prefix);
233
+ app.use(prefix, createHttpRelay({ name, port: relayPort }));
234
+ console.log(`[gateway] HTTP relay: ${prefix}/* → 127.0.0.1:${relayPort} (${name})`);
235
+ }
210
236
  for (const tool of tools) {
211
237
  if (!tool.description) continue;
212
238
  getOrComputeToolEmbedding(name, tool, getEmbedding).catch(err =>
@@ -281,8 +307,9 @@ async function main() {
281
307
  '',
282
308
  '## Serveurs',
283
309
  '',
284
- ...registry.map(({ name, route, description }) =>
285
- `### ${name}\n- **Route** : \`${route}\`\n- **Description** : ${description ?? 'Non renseignée'}`
310
+ ...registry.map(({ name, route, description, relayPort }) =>
311
+ `### ${name}\n- **Route** : \`${route}\`\n- **Description** : ${description ?? 'Non renseignée'}` +
312
+ (relayPort ? `\n- **Relais HTTP** : \`${relayPrefix(route)}/*\`` : '')
286
313
  ),
287
314
  ];
288
315
  res.type('text/plain').send(lines.join('\n'));
@@ -309,7 +336,7 @@ async function main() {
309
336
  process.on('SIGINT', shutdown);
310
337
  }
311
338
 
312
- if (!process.argv.includes('--mcp')) {
339
+ if (!process.argv.includes('--mcp') && !process.argv.includes('--watch')) {
313
340
  main().catch(err => {
314
341
  console.error('[gateway] Fatal error:', err.message);
315
342
  process.exit(1);