This is an automated email from the ASF dual-hosted git repository.
aminghadersohi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 952a6f3a239 fix(mcp): prevent encoding error on tools/list when
middleware raises (#40446)
952a6f3a239 is described below
commit 952a6f3a239acd4b9f8fd066131dda2d0887c0fb
Author: Amin Ghadersohi <[email protected]>
AuthorDate: Tue May 26 09:50:31 2026 -0700
fix(mcp): prevent encoding error on tools/list when middleware raises
(#40446)
---
requirements/development.txt | 2 +-
scripts/change_detector.py | 1 +
superset/mcp_service/middleware.py | 9 ++++++-
.../mcp_service/test_middleware_logging.py | 28 ++++++++++++++++++++++
4 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/requirements/development.txt b/requirements/development.txt
index 8bb622c6b88..d2ae61417a6 100644
--- a/requirements/development.txt
+++ b/requirements/development.txt
@@ -219,7 +219,7 @@ docstring-parser==0.17.0
# via cyclopts
docutils==0.22.2
# via rich-rst
-duckdb==1.4.2
+duckdb==1.5.3
# via
# apache-superset
# duckdb-engine
diff --git a/scripts/change_detector.py b/scripts/change_detector.py
index 9a0e1fc8501..8fda027f855 100755
--- a/scripts/change_detector.py
+++ b/scripts/change_detector.py
@@ -31,6 +31,7 @@ PATTERNS = {
r"^superset/",
r"^scripts/",
r"^setup\.py",
+ r"^pyproject\.toml$",
r"^requirements/.+\.txt",
r"^.pylintrc",
],
diff --git a/superset/mcp_service/middleware.py
b/superset/mcp_service/middleware.py
index 632eed5b813..844b5ff59d0 100644
--- a/superset/mcp_service/middleware.py
+++ b/superset/mcp_service/middleware.py
@@ -388,7 +388,14 @@ class StructuredContentStripperMiddleware(Middleware):
context: MiddlewareContext[mt.ListToolsRequest],
call_next: CallNext[mt.ListToolsRequest, Sequence[Tool]],
) -> Sequence[Tool]:
- tools = await call_next(context)
+ try:
+ tools = await call_next(context)
+ except Exception:
+ # ToolError raised by inner middleware (e.g.
GlobalErrorHandlerMiddleware)
+ # cannot be encoded by the MCP SDK in a tools/list response — it
expects a
+ # list, not an error object — causing "encoding without a string
argument".
+ # Return an empty list; GlobalErrorHandlerMiddleware already
logged it.
+ return []
return [
t.model_copy(update={"output_schema": None})
if t.output_schema is not None
diff --git a/tests/unit_tests/mcp_service/test_middleware_logging.py
b/tests/unit_tests/mcp_service/test_middleware_logging.py
index 19487810bbf..3f81dc3ae9b 100644
--- a/tests/unit_tests/mcp_service/test_middleware_logging.py
+++ b/tests/unit_tests/mcp_service/test_middleware_logging.py
@@ -435,3 +435,31 @@ class TestMiddlewareChainOrder:
assert result.meta is not None
assert "mcp_call_id" in result.meta
assert len(result.meta["mcp_call_id"]) == 32
+
+ @pytest.mark.asyncio
+ async def test_list_tools_exception_returns_empty_list(self):
+ """Exception during tools/list returns [] instead of causing encoding
error.
+
+ ToolError raised by GlobalErrorHandlerMiddleware cannot be encoded
+ by the MCP SDK in a tools/list response, producing "encoding without
+ a string argument". StructuredContentStripperMiddleware.on_list_tools
+ must catch it and return an empty list.
+ """
+ from superset.mcp_service.server import build_middleware_list
+
+ middleware_list = build_middleware_list()
+
+ async def failing_list_tools(context: Any) -> Any:
+ raise ValueError("auth failed")
+
+ chain = failing_list_tools
+ for mw in reversed(middleware_list):
+ chain = partial(mw, call_next=chain)
+
+ ctx = _make_context(method="tools/list", name="")
+ result = await chain(ctx)
+
+ assert result == [], (
+ "on_list_tools must return [] on exception — "
+ "ToolError cannot be encoded in a tools/list response."
+ )