From 94e4e997d3ac08a6b1944c164a16ac9d8ae25a48 Mon Sep 17 00:00:00 2001 From: Ms6RB Date: Thu, 26 Mar 2026 01:51:46 +0200 Subject: [PATCH] fix(mcp): k8s_enumerate even distribution + load_skill summary paragraphs - k8s_enumerate: distribute max_urls evenly across namespaces instead of truncating first namespaces. Remove cross-product from short_forms. - load_skill: summary_only now returns title + first paragraph (up to 500 chars) instead of just the # heading line. Co-Authored-By: Claude Opus 4.6 (1M context) --- strix-mcp/src/strix_mcp/tools.py | 21 +++++++++++++++----- strix-mcp/src/strix_mcp/tools_analysis.py | 24 +++++++++-------------- strix-mcp/tests/test_tools.py | 5 +++-- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/strix-mcp/src/strix_mcp/tools.py b/strix-mcp/src/strix_mcp/tools.py index ba78c449..71dfb3c9 100644 --- a/strix-mcp/src/strix_mcp/tools.py +++ b/strix-mcp/src/strix_mcp/tools.py @@ -489,11 +489,22 @@ def register_tools(mcp: FastMCP, sandbox: SandboxManager) -> None: result["failed_skills"] = failed if summary_only: - # Return just names and first-line descriptions - result["skill_summaries"] = { - name: content.split("\n", 1)[0][:200] - for name, content in loaded_content.items() - } + # Return title + first non-empty paragraph for context + summaries: dict[str, str] = {} + for name, content in loaded_content.items(): + lines = content.strip().splitlines() + summary_parts: list[str] = [] + for line in lines: + stripped = line.strip() + if not stripped: + if summary_parts and not summary_parts[-1].startswith("#"): + break # end of first paragraph + continue + summary_parts.append(stripped) + if len(summary_parts) >= 4: + break + summaries[name] = " ".join(summary_parts)[:500] + result["skill_summaries"] = summaries else: # Apply max_content_length: truncate largest skills first total_len = sum(len(c) for c in loaded_content.values()) diff --git a/strix-mcp/src/strix_mcp/tools_analysis.py b/strix-mcp/src/strix_mcp/tools_analysis.py index aa66a87f..2f5dd318 100644 --- a/strix-mcp/src/strix_mcp/tools_analysis.py +++ b/strix-mcp/src/strix_mcp/tools_analysis.py @@ -1304,28 +1304,22 @@ def register_analysis_tools(mcp: FastMCP, sandbox: SandboxManager) -> None: total += 1 by_namespace[ns] = urls - # Also generate short-form names for targets that resolve short names + # Also generate short-form names (service only, no namespace cross-product) short_forms: list[str] = [] for svc in service_ports: short_forms.append(f"{scheme}://{svc}") - for ns in ns_list: - short_forms.append(f"{scheme}://{svc}.{ns}") - # Cap output + # Cap output — distribute evenly across namespaces omitted = 0 if total > max_urls: + per_ns = max(max_urls // len(by_namespace), 1) + new_total = 0 for ns in by_namespace: - if total <= max_urls: - break - excess = total - max_urls - if excess >= len(by_namespace[ns]): - total -= len(by_namespace[ns]) - omitted += len(by_namespace[ns]) - by_namespace[ns] = [] - else: - by_namespace[ns] = by_namespace[ns][:-excess] - omitted += excess - total -= excess + if len(by_namespace[ns]) > per_ns: + omitted += len(by_namespace[ns]) - per_ns + by_namespace[ns] = by_namespace[ns][:per_ns] + new_total += len(by_namespace[ns]) + total = new_total result: dict[str, Any] = { "total_urls": total, diff --git a/strix-mcp/tests/test_tools.py b/strix-mcp/tests/test_tools.py index 203d4ba3..8361d1e9 100644 --- a/strix-mcp/tests/test_tools.py +++ b/strix-mcp/tests/test_tools.py @@ -546,9 +546,10 @@ class TestLoadSkillTool: assert "skill_summaries" in result assert "idor" in result["skill_summaries"] assert "xss" in result["skill_summaries"] - # Summaries should be short strings (first line) + # Summaries should include title + first paragraph (up to 500 chars) for summary in result["skill_summaries"].values(): - assert len(summary) <= 200 + assert len(summary) <= 500 + assert len(summary) > 10 # not just empty @pytest.mark.asyncio async def test_max_content_length_no_truncation_when_under(self, mcp_no_scan):