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 5fc06df fix(cluster): honor read-only active task fallbacks (#211)
5fc06df is described below
commit 5fc06dfbeaa8d181c7d38cf3ae2bcff8e90ef28e
Author: Yijia Su <[email protected]>
AuthorDate: Thu Aug 13 15:54:03 2026 +0800
fix(cluster): honor read-only active task fallbacks (#211)
---
CHANGELOG.md | 6 +++++
doris_mcp_server/tools/capability_detector.py | 24 +++++++++++++++++-
doris_mcp_server/tools/domain_catalog.py | 7 ++++--
test/tools/test_capability_detector.py | 30 +++++++++++++++++++++++
test/tools/test_domain_catalog.py | 12 +++++++++
test/utils/test_cluster_runtime.py | 35 +++++++++++++++++++++++++++
6 files changed, 111 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0f8787..e58126b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -65,6 +65,12 @@ under **Unreleased** until a new version is selected and
published.
### Fixed
+- Aligned `doris_cluster.list_active_tasks` capability detection with its
+ read-only execution fallbacks, so restricted Doris accounts can use the
+ `information_schema.active_queries` or process-list source when
+ `SHOW PROC \"/current_queries\"` is unavailable. The public input schema now
+ advertises only the query and compaction task types implemented by the
+ runtime.
- Prevented MetricFlow sidecar processes from inheriting Doris credentials,
bearer tokens, OAuth/JWT secrets, and unrelated MCP Server environment
configuration by launching each provider with a fixed minimal environment.
diff --git a/doris_mcp_server/tools/capability_detector.py
b/doris_mcp_server/tools/capability_detector.py
index 6075399..e9342f4 100644
--- a/doris_mcp_server/tools/capability_detector.py
+++ b/doris_mcp_server/tools/capability_detector.py
@@ -204,10 +204,21 @@ _DOMAIN_PROBES: Mapping[str, tuple[tuple[str, tuple[str,
...]], ...]] = {
(
'SHOW PROC "/current_queries"',
(
- "legacy_task_views_readable",
+ "current_queries_proc_readable",
"unified_task_progress_readable",
),
),
+ (
+ (
+ "SELECT 1 AS active_query_probe "
+ "FROM information_schema.active_queries LIMIT 1"
+ ),
+ ("active_queries_view_readable",),
+ ),
+ (
+ "SHOW FULL PROCESSLIST",
+ ("processlist_readable",),
+ ),
(
(
"SELECT BE_ID, METRIC_NAME "
@@ -1849,6 +1860,16 @@ def _combine_query_evidence_probe(
def _combine_cluster_evidence_probes(
probes: Mapping[str, CapabilityProbeEvidence],
) -> dict[str, CapabilityProbeEvidence]:
+ active_tasks = _combine_any_runtime_probe(
+ "legacy_task_views_readable",
+ probes,
+ (
+ "current_queries_proc_readable",
+ "active_queries_view_readable",
+ "processlist_readable",
+ ),
+ supported_reason="LEGACY_TASK_VIEW_READABLE",
+ )
audit = probes.get("metrics_history_readable")
storage = probes.get("resource_storage_history_readable")
full = (
@@ -1905,6 +1926,7 @@ def _combine_cluster_evidence_probes(
reason_code="PARTITION_CREATION_HISTORY_ONLY",
)
return {
+ active_tasks.probe_id: active_tasks,
full.probe_id: full,
audit_only.probe_id: audit_only,
storage_only.probe_id: storage_only,
diff --git a/doris_mcp_server/tools/domain_catalog.py
b/doris_mcp_server/tools/domain_catalog.py
index 2a83857..56267b4 100644
--- a/doris_mcp_server/tools/domain_catalog.py
+++ b/doris_mcp_server/tools/domain_catalog.py
@@ -1415,10 +1415,13 @@ DOMAIN_DEFINITIONS = (
"doris_cluster",
"list_active_tasks",
"List active tasks",
- "List visible query, load, schema-change, and compaction
tasks.",
+ "List visible active query and compaction tasks.",
_input_schema(
{
- "task_types": _string_array("Task types to include."),
+ "task_types": _string_array(
+ "Task types to include.",
+ enum=("query", "compaction"),
+ ),
"states": _string_array("Task states to include."),
"limit": _integer("Maximum results.", minimum=1),
}
diff --git a/test/tools/test_capability_detector.py
b/test/tools/test_capability_detector.py
index b474b22..187a1ec 100644
--- a/test/tools/test_capability_detector.py
+++ b/test/tools/test_capability_detector.py
@@ -251,6 +251,36 @@ async def
test_cluster_history_keeps_storage_fallback_without_audit_access() ->
assert storage.reason_code == "PARTITION_CREATION_HISTORY_ONLY"
[email protected]
+async def test_cluster_active_tasks_accepts_read_only_query_view_fallback() ->
None:
+ connection = _ProbeConnection()
+ proc_probe = 'SHOW PROC "/current_queries"'
+ connection.failures[proc_probe] = RuntimeError(
+ "Access denied; user lacks ADMIN privilege"
+ )
+ manager = _ProbeConnectionManager(connection)
+ detector = DorisCapabilityDetector(manager) # type: ignore[arg-type]
+ base = await detector.detect_base(
+ None,
+ capability_generation=1,
+ provider_generation="provider.cluster",
+ )
+
+ cluster = await detector.detect_domain(base, "doris_cluster", None)
+
+ assert (
+ cluster.probe("current_queries_proc_readable").status
+ is not CapabilityProbeStatus.SUPPORTED
+ )
+ assert (
+ cluster.probe("active_queries_view_readable").status
+ is CapabilityProbeStatus.SUPPORTED
+ )
+ active_tasks = cluster.probe("legacy_task_views_readable")
+ assert active_tasks.status is CapabilityProbeStatus.SUPPORTED
+ assert active_tasks.reason_code == "LEGACY_TASK_VIEW_READABLE"
+
+
@pytest.mark.asyncio
async def test_lakehouse_probes_derive_target_sensitive_advanced_facets() ->
None:
connection = _ProbeConnection()
diff --git a/test/tools/test_domain_catalog.py
b/test/tools/test_domain_catalog.py
index 8ffa70a..1106be2 100644
--- a/test/tools/test_domain_catalog.py
+++ b/test/tools/test_domain_catalog.py
@@ -131,6 +131,18 @@ def
test_adbc_is_inside_query_and_cluster_has_exactly_eleven_children() -> None:
assert len(cluster.children) == 11
+def test_cluster_active_task_contract_matches_runtime_sources() -> None:
+ child = DORIS_DOMAIN_CATALOG.resolve_child(
+ "doris_cluster",
+ "list_active_tasks",
+ )
+ task_types = _wire_input(child)["properties"]["task_types"]
+
+ assert task_types["items"]["enum"] == ["query", "compaction"]
+ assert "load" not in child.canonical_description.casefold()
+ assert "schema-change" not in child.canonical_description.casefold()
+
+
def test_every_child_uses_the_exact_feature_matrix_contract() -> None:
feature_contracts = {
feature.feature_id: feature.support_contract
diff --git a/test/utils/test_cluster_runtime.py
b/test/utils/test_cluster_runtime.py
index 08cd3b1..b6b8e81 100644
--- a/test/utils/test_cluster_runtime.py
+++ b/test/utils/test_cluster_runtime.py
@@ -178,6 +178,41 @@ async def
test_list_cluster_nodes_normalizes_real_fe_and_be_rows() -> None:
assert manager.calls == ["SHOW FRONTENDS", "SHOW BACKENDS"]
[email protected]
+async def test_active_tasks_falls_back_to_read_only_active_queries_view() ->
None:
+ proc_statement = 'SHOW PROC "/current_queries"'
+ view_statement = "SELECT * FROM information_schema.active_queries"
+ runtime, manager, _ = _runtime(
+ rows={
+ view_statement: [
+ {
+ "QUERY_ID": "query-1",
+ "STATE": "RUNNING",
+ "COMMAND": "Query",
+ }
+ ]
+ },
+ failures={proc_statement: RuntimeError(1105, "access denied")},
+ )
+
+ result = await runtime.list_active_tasks(
+ task_types=["query"],
+ states=None,
+ limit=10,
+ )
+
+ assert result["status"] == "success"
+ assert result["data"]["items"] == [
+ {
+ "query_id": "query-1",
+ "state": "RUNNING",
+ "command": "Query",
+ "task_type": "query",
+ }
+ ]
+ assert manager.calls == [proc_statement, view_statement]
+
+
@pytest.mark.asyncio
async def test_memory_stats_only_returns_observed_metrics() -> None:
runtime, _, _ = _runtime()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]