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."
+        )

Reply via email to