mirror of
https://github.com/apache/superset.git
synced 2026-05-29 20:29:34 +00:00
fix(mcp): prevent encoding error on tools/list when middleware raises (#40446)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -31,6 +31,7 @@ PATTERNS = {
|
||||
r"^superset/",
|
||||
r"^scripts/",
|
||||
r"^setup\.py",
|
||||
r"^pyproject\.toml$",
|
||||
r"^requirements/.+\.txt",
|
||||
r"^.pylintrc",
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user