Skip to content

fix(mcp): truncate query-tool responses instead of hard-failing#42244

Open
aminghadersohi wants to merge 2 commits into
apache:masterfrom
aminghadersohi:aminghadersohi/mcp-truncate-query-responses
Open

fix(mcp): truncate query-tool responses instead of hard-failing#42244
aminghadersohi wants to merge 2 commits into
apache:masterfrom
aminghadersohi:aminghadersohi/mcp-truncate-query-responses

Conversation

@aminghadersohi

Copy link
Copy Markdown
Contributor

SUMMARY

execute_sql, query_dataset, and get_chart_data raised a hard "Response too large" error whenever their result exceeded the MCP response token limit, even when the response was only a few rows over. This extends ResponseSizeGuardMiddleware to truncate these three tools the same way info tools (get_chart_info, get_dataset_info, etc.) are already truncated, instead of hard-failing:

  • Binary-search the largest row-count prefix that keeps the serialized response under the token limit, returning the truncated result instead of erroring.
  • Handle CSV chart-data exports, where the payload lives in the scalar csv_data field (data=[]) rather than a row list, by truncating that field directly. excel_data is base64-encoded binary and is left untouched, since truncating it would produce a corrupt file.
  • Re-check the truncated response's size before returning it (mirroring the existing info-tool path), so a single row/CSV blob that alone exceeds the limit falls back to the informative size-limit error instead of silently shipping an over-budget response. The bisection reserves headroom for the truncation-note metadata up front so this fallback only triggers when truncation genuinely can't fit.
  • Tailor the truncation-note wording per tool (limit/row_limit parameter vs. SQL LIMIT clause) instead of always suggesting a SQL LIMIT.

BEFORE/AFTER

Before: execute_sql/query_dataset/get_chart_data responses that exceeded the token limit always raised ToolError: Response too large, even if truncating a handful of rows would have brought them under the limit.

After: these tools truncate the row set (or CSV content) and return a partial result with _response_truncated / _truncation_notes set, falling back to the hard error only when truncation genuinely cannot bring the response under the limit.

TESTING INSTRUCTIONS

  • pytest tests/unit_tests/mcp_service/ (3086 passed)
  • ruff check / ruff format --diff / mypy on the changed files

ADDITIONAL INFORMATION

  • Has associated tests
  • Confirm this PR does not include user-facing docs strings changes requiring docs/ updates

execute_sql, query_dataset, and get_chart_data raised a hard "Response
too large" error whenever their result set exceeded the MCP response
token limit, even though the majority of oversized responses are just
a few rows too many. Extend ResponseSizeGuardMiddleware to truncate
these three tools the same way info tools are already truncated:

- Binary-search the largest row-count prefix that keeps the serialized
  response under the token limit, and return that instead of erroring.
- Also handle CSV chart-data exports, where the payload lives in the
  scalar csv_data field (data=[]) rather than a row list. excel_data is
  base64-encoded binary and is left untouched, since truncating it
  would produce a corrupt file.
- Re-check the truncated response's size before returning it (mirroring
  the existing info-tool path), so a single row/CSV blob that alone
  exceeds the limit falls back to the informative size-limit error
  instead of silently shipping an over-budget response. The bisection
  now reserves headroom for the truncation-note metadata up front so
  this fallback only triggers when truncation genuinely can't fit.
- Tailor the truncation-note wording per tool (limit/row_limit
  parameter vs. SQL LIMIT clause) instead of always suggesting SQL.
@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 2d6ea36
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a5e603eb4eaf4000842f8ea
😎 Deploy Preview https://deploy-preview-42244--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 126 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.16%. Comparing base (5903577) to head (2d6ea36).

Files with missing lines Patch % Lines
superset/mcp_service/utils/token_utils.py 0.00% 87 Missing ⚠️
superset/mcp_service/middleware.py 0.00% 39 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42244      +/-   ##
==========================================
+ Coverage   56.01%   65.16%   +9.15%     
==========================================
  Files        2768     2768              
  Lines      156280   156225      -55     
  Branches    35774    35743      -31     
==========================================
+ Hits        87534   101806   +14272     
+ Misses      67917    52453   -15464     
- Partials      829     1966    +1137     
Flag Coverage Δ
hive 38.54% <0.00%> (-0.06%) ⬇️
mysql 57.78% <0.00%> (?)
postgres 57.83% <0.00%> (?)
presto 40.48% <0.00%> (-0.07%) ⬇️
python 59.23% <0.00%> (+18.64%) ⬆️
sqlite 57.44% <0.00%> (?)
unit 100.00% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aminghadersohi
aminghadersohi marked this pull request as ready for review July 20, 2026 17:51
Comment thread superset/mcp_service/middleware.py
@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. In _try_truncate_data_query_response, the token estimation is performed on the truncated payload alone, ignoring the overhead of the ToolResult wrapper that is added back before returning. This can result in a returned object that still exceeds the token_limit.

To resolve this, you should re-estimate the tokens on the final object after rewrapping, or account for the wrapper overhead during the check. Here is the corrected logic for the re-check:

        # ... (after truncation)
        if not was_truncated:
            return None

        # Re-wrap to check total size including wrapper overhead
        final_result = self._rewrap_as_tool_result(truncated, response) if extracted is not None and isinstance(truncated, dict) else truncated
        truncated_tokens = estimate_response_tokens(final_result)
        
        if truncated_tokens > self.token_limit:
            return None
        
        # ... (proceed with logging and return final_result)

Would you like me to check the other comments on this PR and implement fixes for them as well?

superset/mcp_service/middleware.py

# Re-wrap to check total size including wrapper overhead
        final_result = self._rewrap_as_tool_result(truncated, response) if extracted is not None and isinstance(truncated, dict) else truncated
        truncated_tokens = estimate_response_tokens(final_result)
        
        if truncated_tokens > self.token_limit:
            return None

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #f8f8ed

Actionable Suggestions - 1
  • superset/mcp_service/middleware.py - 1
Review Details
  • Files reviewed - 4 · Commit Range: 9d4e317..2d6ea36
    • superset/mcp_service/middleware.py
    • superset/mcp_service/utils/token_utils.py
    • tests/unit_tests/mcp_service/test_middleware.py
    • tests/unit_tests/mcp_service/utils/test_token_utils.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +1398 to +1401
if extracted is not None and isinstance(truncated, dict):
return self._rewrap_as_tool_result(truncated, response)

return truncated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CWE-252: Missing truncation metadata

The _try_truncate_data_query_response method is missing the _response_truncated and _truncation_notes metadata markers that _try_truncate_info_response sets at line 1326-1328. Callers of data-query tools (execute_sql, query_dataset, get_chart_data) will not know truncation occurred, making debugging harder and output inconsistent with info-tool responses. (CWE-252)

Code Review Run #f8f8ed


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants