This is an automated email from the ASF dual-hosted git repository.
FreeOnePlus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-mcp-server.git
The following commit(s) were added to refs/heads/master by this push:
new 2d0f56e fix: bound Flat-mode capability manifests (#183)
2d0f56e is described below
commit 2d0f56e3fffb61bfd9eeba877a50b66cbce3f865
Author: Yijia Su <[email protected]>
AuthorDate: Fri Jul 31 22:23:48 2026 +0800
fix: bound Flat-mode capability manifests (#183)
---
CHANGELOG.md | 3 ++
doris_mcp_server/tools/capability_registry.py | 28 +++++++++++---
test/integration/test_real_doris_transports.py | 39 +++++++++++++++++++
test/tools/test_capability_registry.py | 46 +++++++++++++++++++++++
test/tools/test_capability_runtime_integration.py | 2 -
5 files changed, 111 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df7693e..c8ce2a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -152,6 +152,9 @@ under **Unreleased** until a new version is selected and
published.
- Excluded explicitly dead Doris components from active version gating while
preserving them in node inventory, and kept live runtime manifests within
the 16 KiB domain budget.
+- Limited public detected-version evidence to each child's actual Doris
+ component scope, preventing real Cluster manifests from overflowing the
+ 16 KiB budget and breaking Flat-mode `tools/list`.
- Preserved service availability after malformed requests, unknown methods,
header mismatches, unsupported versions, and missing capabilities.
- Kept static-token Doris pools usable after repeated query timeouts, and
diff --git a/doris_mcp_server/tools/capability_registry.py
b/doris_mcp_server/tools/capability_registry.py
index 7e1b09a..6071530 100644
--- a/doris_mcp_server/tools/capability_registry.py
+++ b/doris_mcp_server/tools/capability_registry.py
@@ -235,7 +235,11 @@ class CapabilityEvaluator:
child: ChildToolDefinition,
auth_context: Any | None,
) -> Availability:
- detected_versions = _detected_versions(snapshot)
+ feature = self._matrix.get_feature(domain.name, child.name)
+ detected_versions = _detected_versions(
+ snapshot,
+ feature.version_scope,
+ )
if not self._bound_handlers.is_bound(domain.name, child.name):
return Availability(
status=AvailabilityStatus.UNKNOWN,
@@ -248,7 +252,6 @@ class CapabilityEvaluator:
),
)
- feature = self._matrix.get_feature(domain.name, child.name)
if (
snapshot.mixed_versions
and feature.version_scope
@@ -890,6 +893,7 @@ def _unknown_snapshot(
def _detected_versions(
snapshot: DorisCapabilitySnapshot,
+ scope: CapabilityVersionScope,
) -> dict[str, tuple[str, ...]]:
def values(versions: tuple[Any, ...]) -> tuple[str, ...]:
return tuple(
@@ -904,11 +908,25 @@ def _detected_versions(
master = values((snapshot.version_vector.master_fe,))
followers = values(snapshot.version_vector.follower_fes)
backends = values(snapshot.version_vector.backends)
- if master:
+ include_master = scope in {
+ CapabilityVersionScope.MASTER_FE,
+ CapabilityVersionScope.ALL_FE,
+ CapabilityVersionScope.ALL_COMPONENTS,
+ CapabilityVersionScope.PROVIDER_WITH_MASTER_FE_BASELINE,
+ }
+ include_followers = scope in {
+ CapabilityVersionScope.ALL_FE,
+ CapabilityVersionScope.ALL_COMPONENTS,
+ }
+ include_backends = scope in {
+ CapabilityVersionScope.ALL_BE,
+ CapabilityVersionScope.ALL_COMPONENTS,
+ }
+ if include_master and master:
detected["master_fe"] = master
- if followers:
+ if include_followers and followers:
detected["follower_fe"] = followers
- if backends:
+ if include_backends and backends:
detected["be"] = backends
return detected
diff --git a/test/integration/test_real_doris_transports.py
b/test/integration/test_real_doris_transports.py
index eb20e44..6037279 100644
--- a/test/integration/test_real_doris_transports.py
+++ b/test/integration/test_real_doris_transports.py
@@ -1262,6 +1262,45 @@ async def test_real_doris_tool_regression_paths(
assert recovered_payload["data"][0]["recovered"] == 1
[email protected]("transport", ["http", "stdio"])
+async def test_real_doris_flat_tool_list_stays_bounded_and_callable(
+ transport: str,
+) -> None:
+ settings = _real_doris_settings()
+ environment = _server_environment(
+ settings,
+ user=settings.user,
+ password=settings.password,
+ )
+ environment["MCP_TOOL_EXPOSURE_MODE"] = "flat"
+
+ async with _transport_client(
+ transport,
+ environment,
+ read_timeout_seconds=60,
+ ) as client:
+ tools = {
+ tool.name: tool
+ for tool in (await client.list_tools(cache_mode="bypass")).tools
+ }
+ assert len(tools) == 47
+ assert "doris_query_execute_query" in tools
+
+ result = await client.call_tool(
+ "doris_query_execute_query",
+ {
+ "sql": "SELECT @@version_comment",
+ "max_rows": 1,
+ },
+ )
+ assert result.is_error is False
+ assert isinstance(result.structured_content, dict)
+ assert "4.0.5-rc01" in json.dumps(
+ result.structured_content,
+ ensure_ascii=False,
+ )
+
+
@pytest.mark.skipif(
os.getenv("DORIS_REAL_HTTP_INTEGRATION") != "1",
reason="set DORIS_REAL_HTTP_INTEGRATION=1 with independent FE/BE HTTP
endpoints",
diff --git a/test/tools/test_capability_registry.py
b/test/tools/test_capability_registry.py
index f807089..c373975 100644
--- a/test/tools/test_capability_registry.py
+++ b/test/tools/test_capability_registry.py
@@ -196,6 +196,52 @@ def
test_evaluator_requires_version_probes_handler_and_call_permission() -> None
assert allowed.callable is True
[email protected](
+ ("domain_name", "child_name", "expected_components"),
+ (
+ ("doris_governance", "list_udfs", {"master_fe"}),
+ (
+ "doris_governance",
+ "get_lineage_capability_status",
+ {"master_fe", "follower_fe"},
+ ),
+ ("doris_cluster", "get_cache_status", {"be"}),
+ (
+ "doris_cluster",
+ "get_cluster_overview",
+ {"master_fe", "follower_fe", "be"},
+ ),
+ ("doris_query", "get_adbc_connection_info", {"master_fe"}),
+ ),
+)
+def test_evaluator_exposes_only_versions_relevant_to_feature_scope(
+ domain_name: str,
+ child_name: str,
+ expected_components: set[str],
+) -> None:
+ evaluator = CapabilityEvaluator(
+ matrix=DORIS_FEATURE_MATRIX,
+ bound_handlers=_BoundHandlers(
+ f"{domain_name}.{child_name}"
+ ), # type: ignore[arg-type]
+ )
+ domain = DORIS_DOMAIN_CATALOG.resolve_domain(domain_name)
+ child = DORIS_DOMAIN_CATALOG.resolve_child(
+ domain_name,
+ child_name,
+ )
+
+ availability = evaluator.evaluate(
+ snapshot=_snapshot(),
+ providers=CapabilityProviderRegistry({}).snapshot(),
+ domain=domain,
+ child=child,
+ auth_context=None,
+ )
+
+ assert set(availability.detected_versions) == expected_components
+
+
def test_compaction_prefers_native_tracker_and_uses_legacy_on_405() -> None:
evaluator = CapabilityEvaluator(
matrix=DORIS_FEATURE_MATRIX,
diff --git a/test/tools/test_capability_runtime_integration.py
b/test/tools/test_capability_runtime_integration.py
index 1285275..4bbb741 100644
--- a/test/tools/test_capability_runtime_integration.py
+++ b/test/tools/test_capability_runtime_integration.py
@@ -140,7 +140,6 @@ async def
test_default_manager_detects_caches_and_dispatches() -> None:
assert execute_child.availability.callable is True
assert execute_child.availability.detected_versions == {
"master_fe": ("4.0.5",),
- "be": ("4.0.5",),
}
assert first.manifest_version == second.manifest_version
assert result.mode == "result"
@@ -151,4 +150,3 @@ async def
test_default_manager_detects_caches_and_dispatches() -> None:
"EXPLAIN SELECT 1"
) == 1
manager._exec_query_tool.assert_awaited_once()
-
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]