@imenam/mcp-http-gateway 2.0.19 → 2.0.20
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 +4 -0
- package/package.json +1 -1
- package/server.js +20 -15
package/README.md
CHANGED
|
@@ -55,6 +55,10 @@ GATEWAY_CONFIG_DIR=./config
|
|
|
55
55
|
| `GATEWAY_ADMIN_TOKEN` | — | Active `POST /admin/reload` et en est le jeton. Absent, la route n'existe pas. Voir [Rechargement à chaud](#rechargement-à-chaud). |
|
|
56
56
|
| `GATEWAY_SCOPE_SECRET` | — | Impose un périmètre d'accès signé sur chaque requête. Absent, toute requête voit tous les MCP et tous les outils. Voir [Périmètre d'accès](#périmètre-daccès). |
|
|
57
57
|
| `GATEWAY_BODY_LIMIT` | `32mb` | Taille maximale du corps d'une requête. Le défaut d'express (100 ko) ne suffit pas dès qu'un appel d'outil porte le contenu d'un fichier en base64 ; au-delà de cette limite, la réponse est un `413`. Ne s'applique pas aux routes de [relais HTTP](#relais-http-mcp_http_relay_port). |
|
|
58
|
+
| `RATE_LIMIT_GLOBAL_MAX` / `RATE_LIMIT_GLOBAL_WINDOW_MINUTES` | `1000` / `1` | Requêtes acceptées par IP sur la fenêtre, toutes routes confondues ; au-delà, la réponse est un `429`. `0` désactive la limite. |
|
|
59
|
+
| `RATE_LIMIT_SEARCH_MAX` / `RATE_LIMIT_SEARCH_WINDOW_MINUTES` | `30` / `1` | Même principe pour `/tools/search`, en plus de la limite globale. `0` désactive la limite. |
|
|
60
|
+
|
|
61
|
+
Derrière un frontal qui authentifie déjà les appelants et borne leur fréquence, comme mcp-gateway-manager, les deux limites se mettent à `0`.
|
|
58
62
|
|
|
59
63
|
### `mcp-servers.json`
|
|
60
64
|
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -198,28 +198,33 @@ async function main() {
|
|
|
198
198
|
isRelayPath(registry.relayPrefixes, req.path) ? next() : jsonParser(req, res, next)
|
|
199
199
|
);
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
const
|
|
201
|
+
// Une limite à 0 désactive le compteur correspondant.
|
|
202
|
+
const globalWindowMin = parseInt(process.env.RATE_LIMIT_GLOBAL_WINDOW_MINUTES ?? '1', 10);
|
|
203
|
+
const globalMax = parseInt(process.env.RATE_LIMIT_GLOBAL_MAX ?? '1000', 10);
|
|
203
204
|
const searchWindowMin = parseInt(process.env.RATE_LIMIT_SEARCH_WINDOW_MINUTES ?? '1', 10);
|
|
204
205
|
const searchMax = parseInt(process.env.RATE_LIMIT_SEARCH_MAX ?? '30', 10);
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
windowMs:
|
|
208
|
-
|
|
209
|
-
standardHeaders: 'draft-8',
|
|
210
|
-
legacyHeaders: false,
|
|
211
|
-
message: { error: 'Too Many Requests' },
|
|
212
|
-
}));
|
|
213
|
-
console.log(`[gateway] Rate limit — global: ${globalMax} req/${globalWindowMin} min/IP`);
|
|
214
|
-
|
|
215
|
-
const searchLimiter = rateLimit({
|
|
216
|
-
windowMs: searchWindowMin * 60_000,
|
|
217
|
-
max: searchMax,
|
|
207
|
+
const limiter = (windowMin, max) => rateLimit({
|
|
208
|
+
windowMs: windowMin * 60_000,
|
|
209
|
+
limit: max,
|
|
218
210
|
standardHeaders: 'draft-8',
|
|
219
211
|
legacyHeaders: false,
|
|
220
212
|
message: { error: 'Too Many Requests' },
|
|
221
213
|
});
|
|
222
|
-
|
|
214
|
+
|
|
215
|
+
if (globalMax > 0) {
|
|
216
|
+
app.use(limiter(globalWindowMin, globalMax));
|
|
217
|
+
console.log(`[gateway] Rate limit — global: ${globalMax} req/${globalWindowMin} min/IP`);
|
|
218
|
+
} else {
|
|
219
|
+
console.log('[gateway] Rate limit — global: disabled');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const searchLimiter = searchMax > 0
|
|
223
|
+
? limiter(searchWindowMin, searchMax)
|
|
224
|
+
: (req, res, next) => next();
|
|
225
|
+
console.log(searchMax > 0
|
|
226
|
+
? `[gateway] Rate limit — /tools/search: ${searchMax} req/${searchWindowMin} min/IP`
|
|
227
|
+
: '[gateway] Rate limit — /tools/search: disabled');
|
|
223
228
|
|
|
224
229
|
const verifier = createMcpTokenVerifier();
|
|
225
230
|
if (verifier) {
|