fix(mcp): prevent encoding errors and fix tool bugs on MCP client transports (#38786)

This commit is contained in:
Amin Ghadersohi
2026-03-24 05:41:24 -04:00
committed by GitHub
parent 7222327992
commit ed3c5280a9
11 changed files with 193 additions and 31 deletions

View File

@@ -28,6 +28,7 @@ import logging
from decimal import Decimal
from typing import Any
import pandas as pd
from fastmcp import Context
from superset_core.mcp.decorators import tool, ToolAnnotations
from superset_core.queries.types import (
@@ -176,6 +177,46 @@ def _sanitize_row_values(rows: list[dict[str, Any]]) -> None:
row[key] = str(value)
def _data_to_statement_data(data: Any) -> StatementData:
"""Convert statement data (DataFrame, list, dict, bytes) to StatementData.
When results come from cache, data may be a dict/list/bytes instead of
a pandas DataFrame. This function handles all cases defensively.
"""
from superset.utils import json as json_utils
if isinstance(data, list):
rows_data = data
elif isinstance(data, dict):
rows_data = data.get("data", [data])
if not isinstance(rows_data, list):
rows_data = [rows_data]
elif isinstance(data, pd.DataFrame):
rows_data = data.to_dict(orient="records")
_sanitize_row_values(rows_data)
return StatementData(
rows=rows_data,
columns=[
ColumnInfo(name=col, type=str(data[col].dtype)) for col in data.columns
],
)
elif isinstance(data, bytes):
try:
decoded = json_utils.loads(data)
rows_data = decoded if isinstance(decoded, list) else [decoded]
except (ValueError, UnicodeDecodeError):
rows_data = []
else:
rows_data = [{"value": str(data)}]
_sanitize_row_values(rows_data)
col_names = list(rows_data[0].keys()) if rows_data else []
return StatementData(
rows=rows_data,
columns=[ColumnInfo(name=col, type="object") for col in col_names],
)
def _convert_to_response(result: QueryResult) -> ExecuteSqlResponse:
"""Convert QueryResult to ExecuteSqlResponse."""
if result.status != QueryStatus.SUCCESS:
@@ -193,15 +234,7 @@ def _convert_to_response(result: QueryResult) -> ExecuteSqlResponse:
for stmt in result.statements:
stmt_data: StatementData | None = None
if stmt.data is not None:
df = stmt.data
rows_data = df.to_dict(orient="records")
_sanitize_row_values(rows_data)
stmt_data = StatementData(
rows=rows_data,
columns=[
ColumnInfo(name=col, type=str(df[col].dtype)) for col in df.columns
],
)
stmt_data = _data_to_statement_data(stmt.data)
data_bearing_count += 1
statements.append(