Files
superset2/superset/mcp_service/utils/query_utils.py
Amin Ghadersohi 48a73b5d59 fix(mcp): address Codex review findings on semantic layer tools
- BLOCKER: add view.raise_for_access() on all external SemanticView
  paths; use SemanticViewDAO.find_accessible() for list-all to filter
  at SQL level, mirroring DatasourceDAO.build_semantic_view_query()
- MEDIUM: validate requested metrics/dimensions/filters/order_by against
  the resolved datasource in get_table, matching query_dataset behavior;
  extract shared validate_names() to mcp_service/utils/query_utils.py
- MEDIUM: fix N+1 in list_metrics find_all path by using _apply_base_filter
  with subqueryload eager options instead of lazy-loaded find_all()
- NIT: extract _format_columns to format_data_columns() in response_utils.py;
  query_dataset now shares the same implementation
- NIT: SemanticLayerError now extends MCPBaseError for a consistent error
  shape across all MCP tools
- NIT: fix file permissions (644) on new semantic layer Python files
2026-07-01 17:13:05 +00:00

41 lines
1.4 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Shared query validation utilities for MCP tools."""
import difflib
def validate_names(
requested: list[str],
valid: set[str],
kind: str,
) -> list[str]:
"""Return list of error messages for names not found in *valid*.
Includes close-match suggestions when available.
"""
errors: list[str] = []
for name in requested:
if name not in valid:
suggestions = difflib.get_close_matches(name, valid, n=3, cutoff=0.6)
msg = f"Unknown {kind}: '{name}'"
if suggestions:
msg += f". Did you mean: {', '.join(suggestions)}?"
errors.append(msg)
return errors