@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/README.md +663 -512
- package/gateway-exec.js +162 -0
- package/gateway-watch.js +348 -0
- package/http-relay.js +93 -0
- package/mcp-mode.js +722 -647
- package/package.json +57 -54
- package/server.js +33 -6
package/package.json
CHANGED
|
@@ -1,54 +1,57 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@imenam/mcp-http-gateway",
|
|
3
|
-
"version": "2.0.
|
|
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
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"url": "https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway
|
|
40
|
-
},
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
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('--
|
|
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
|
-
|
|
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);
|