From d0a6cc1a0baa744d11293c06f2e26f2e5702de1d Mon Sep 17 00:00:00 2001 From: Arthur Ria Date: Tue, 25 Aug 2026 10:35:49 +0200 Subject: [PATCH] =?UTF-8?q?L3.1b=20:=20plafonne=20la=20taille=20de=20r?= =?UTF-8?q?=C3=A9ponse=20de=20search=5Flogs=20(MAX=5FLOG=5FSEARCH=5FCHARS)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit max_results (50) borne le nombre de résultats mais pas le volume : les context_lines multiplient la taille, et la réponse aux seuls défauts atteignait 52-56 000 caractères — rejetée par le client MCP. Le tool écarte désormais des résultats ENTIERS (jamais coupés au milieu de leur contexte) jusqu'à passer sous le plafond, et le signale : truncated, returned/omitted, hint actionnable (affiner keyword, réduire context_lines, baisser max_results). Plafond : MAX_LOG_SEARCH_CHARS, variable d'environnement avec défaut 25 000 — même style de lecture que MAX_QUERY_ROWS/QUERY_TIMEOUT (parseInt(process.env.X) || défaut), documentée dans CLAUDE.md (réglages partagés). 25 000 correspond à l'ordre de grandeur cible du lot (~20-25 000 chars) et se mesure sur content[0].text, la taille qui fait foi côté protocole. Les défauts max_results=50 et context_lines=2 sont inchangés : le correctif est le bornage signalé, pas un changement de comportement par défaut. Mesures via le protocole (LIMAGRAIN, content[0].text) : - {"keyword":"Error"} : avant 52 162 chars / 50 résultats ; après 24 164 chars, returned 16, omitted 34, truncated true, hint présent. - {"keyword":"Error","max_results":3} : 4 855 chars, 3/3, pas de troncature signalée. - mot-clé sans occurrence : 112 chars, 0 résultat, pas de truncated. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 4 ++++ src/tools/log-tools.js | 47 ++++++++++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6686729..f182e18 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -146,6 +146,10 @@ explicite (D9). Par défaut `false`. **`LOGS_PATH`** accepte le placeholder `{host}`, substitué par le host du profil actif à chaque appel. +**`MAX_LOG_SEARCH_CHARS`** (défaut 25 000) : plafond en caractères de la réponse +de `search_logs` — au-delà, des résultats entiers sont écartés et signalés +(`truncated`, D24). + **Au runtime.** `profile-manager` est un singleton d'état global. Les services s'abonnent via `onSwitch()` pour invalider ce qui dépend du tenant : diff --git a/src/tools/log-tools.js b/src/tools/log-tools.js index e265946..c151347 100644 --- a/src/tools/log-tools.js +++ b/src/tools/log-tools.js @@ -182,22 +182,39 @@ async function searchLogs(args) { const result = await logService.searchLogs(keyword, max_results, context_lines); + // Garde-fou de taille (L3.1) : max_results borne le nombre de résultats, + // pas le volume — les context_lines multiplient la taille (55 954 chars + // mesurés avec les seuls défauts, rejetés par le client MCP). Au-delà du + // plafond on écarte des résultats ENTIERS (jamais coupés au milieu de + // leur contexte) et on le signale : truncated + omitted + hint. + const cap = parseInt(process.env.MAX_LOG_SEARCH_CHARS) || 25000; + + const buildText = (kept) => { + const omitted = result.results.length - kept.length; + const payload = { + success: true, + keyword: result.keyword, + totalResults: result.totalResults, + returned: kept.length, + }; + if (omitted > 0) { + payload.truncated = true; + payload.omitted = omitted; + payload.hint = `Plafond de taille de réponse atteint (${cap} caractères) : ${kept.length} résultat(s) renvoyé(s) sur ${result.totalResults}, ${omitted} écarté(s). Affinez le keyword, réduisez context_lines ou baissez max_results.`; + } + payload.results = kept; + return JSON.stringify(payload, null, 2); + }; + + let kept = result.results.slice(); + let text = buildText(kept); + while (text.length > cap && kept.length > 0) { + kept.pop(); + text = buildText(kept); + } + return { - content: [ - { - type: 'text', - text: JSON.stringify( - { - success: true, - keyword: result.keyword, - totalResults: result.totalResults, - results: result.results, - }, - null, - 2 - ), - }, - ], + content: [{ type: 'text', text }], }; } catch (err) { return {