feat(mcp): add list and get tools for saved queries and query history - #40346
Conversation
Review: list/get tools for saved queries and query historyThorough pass over the 4 new MCP tools. Summary: clean, well-tested, correctly secured, and faithful to the existing Verified (all good ✅)
Suggested changes (none blocking)1. 2. 3. 4. Minor test-coverage gaps (optional). No test exercises 5. Notes (consistent with existing code, not PR issues)
Item #1 is the only one I'd suggest addressing before merge; the rest are nits. |
|
Thanks for the thorough review! Addressed in ecec0a0: #1 — Addressed: Dropped #2 — Addressed: Removed both #3 — Addressed: Added #4 — Addressed (partially): Added a #5 — Addressed: Fixed the |
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #40346 +/- ##
==========================================
- Coverage 64.18% 64.14% -0.04%
==========================================
Files 2592 2600 +8
Lines 139271 139675 +404
Branches 32337 32372 +35
==========================================
+ Hits 89385 89594 +209
- Misses 48351 48542 +191
- Partials 1535 1539 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review Agent Run #bd7d73
Actionable Suggestions - 3
-
superset/mcp_service/saved_query/tool/get_saved_query_info.py - 1
- Inconsistent error handling · Line 115-115
-
superset/mcp_service/query/tool/list_queries.py - 1
- CWE-390: Redundant except-Raise · Line 146-156
-
tests/unit_tests/mcp_service/query/tool/test_query_tools.py - 1
- Broad exception catch in test · Line 301-301
Additional Suggestions - 1
-
tests/unit_tests/mcp_service/saved_query/tool/test_saved_query_tools.py - 1
-
Incomplete pagination assertions · Line 258-273Test `test_list_saved_queries_pagination_info` validates pagination metadata but is missing an assertion for the `count` field, which is part of the SavedQueryList schema (line 122 in schemas.py).
Code suggestion
--- a/tests/unit_tests/mcp_service/saved_query/tool/test_saved_query_tools.py +++ b/tests/unit_tests/mcp_service/saved_query/tool/test_saved_query_tools.py @@ -269,6 +269,7 @@ async def test_list_saved_queries_pagination_info(mock_list, mcp_server): ) data = json.loads(result.content[0].text) assert data["total_count"] == 25 + assert data["count"] == 3 assert data["page_size"] == 3 assert data["has_next"] is True assert data["has_previous"] is False
-
Review Details
-
Files reviewed - 17 · Commit Range:
5de1fb7..ecec0a0- superset/mcp_service/app.py
- superset/mcp_service/query/__init__.py
- superset/mcp_service/query/schemas.py
- superset/mcp_service/query/tool/__init__.py
- superset/mcp_service/query/tool/get_query_info.py
- superset/mcp_service/query/tool/list_queries.py
- superset/mcp_service/saved_query/__init__.py
- superset/mcp_service/saved_query/schemas.py
- superset/mcp_service/saved_query/tool/__init__.py
- superset/mcp_service/saved_query/tool/get_saved_query_info.py
- superset/mcp_service/saved_query/tool/list_saved_queries.py
- tests/unit_tests/mcp_service/query/__init__.py
- tests/unit_tests/mcp_service/query/tool/__init__.py
- tests/unit_tests/mcp_service/query/tool/test_query_tools.py
- tests/unit_tests/mcp_service/saved_query/__init__.py
- tests/unit_tests/mcp_service/saved_query/tool/__init__.py
- tests/unit_tests/mcp_service/saved_query/tool/test_saved_query_tools.py
-
Files skipped - 0
-
Tools
- Whispers (Secret Scanner) - ✔︎ Successful
- Detect-secrets (Secret Scanner) - ✔︎ Successful
- MyPy (Static Code Analysis) - ✔︎ Successful
- Astral Ruff (Static Code Analysis) - ✔︎ 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
Code Review Agent Run #892ec7Actionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Implements list_saved_queries, get_saved_query_info, list_queries, and get_query_info MCP tools in new saved_query/ and query/ domains. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ies tools - Drop sql from DEFAULT_QUERY_COLUMNS (25 rows × full SQL bodies is too heavy for default LLM responses; callers use select_columns or get_query_info to access SQL) - Add changed_on to SORTABLE_QUERY_COLUMNS for queries (column is indexed on the model, same treatment as saved queries) - Remove QueryError.create() and SavedQueryError.create() dead code - Fix list_queries instruction text in app.py: use '(1-based pagination)' to match the wording used by all other list tools - Add tests: select_columns field projection and invalid order_column rejection
…ies tools - Narrow exception type in order_column test from Exception to ValueError - Add count assertion to saved query pagination test
53be872 to
8dc800a
Compare
There was a problem hiding this comment.
Code Review Agent Run #30743f
Actionable Suggestions - 1
-
superset/mcp_service/saved_query/schemas.py - 1
- Missing created_by_me/owned_by_me fields · Line 154-226
Additional Suggestions - 1
-
superset/mcp_service/query/tool/list_queries.py - 1
-
Unused function parameter · Line 98-99The `cols` parameter in `_serialize_query` is never used inside the function body. The function signature shows `(obj: object, cols: list[str] | None)` but only `obj` is utilized.
Code suggestion
--- superset/mcp_service/query/tool/list_queries.py +++ superset/mcp_service/query/tool/list_queries.py @@ -95,7 +95,7 @@ async def list_queries( try: from superset.daos.query import QueryDAO - def _serialize_query(obj: object, cols: list[str] | None) -> QueryInfo | None: + def _serialize_query(obj: object) -> QueryInfo | None: return serialize_query_object(obj)
-
Filtered by Review Rules
Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.
-
superset/mcp_service/saved_query/tool/list_saved_queries.py - 1
- Missing created_by_me/owned_by_me params · Line 117-125
-
superset/mcp_service/saved_query/tool/get_saved_query_info.py - 1
- Replace broad exception catch · Line 115-115
Review Details
-
Files reviewed - 17 · Commit Range:
6a254a2..8dc800a- superset/mcp_service/app.py
- superset/mcp_service/query/__init__.py
- superset/mcp_service/query/schemas.py
- superset/mcp_service/query/tool/__init__.py
- superset/mcp_service/query/tool/get_query_info.py
- superset/mcp_service/query/tool/list_queries.py
- superset/mcp_service/saved_query/__init__.py
- superset/mcp_service/saved_query/schemas.py
- superset/mcp_service/saved_query/tool/__init__.py
- superset/mcp_service/saved_query/tool/get_saved_query_info.py
- superset/mcp_service/saved_query/tool/list_saved_queries.py
- tests/unit_tests/mcp_service/query/__init__.py
- tests/unit_tests/mcp_service/query/tool/__init__.py
- tests/unit_tests/mcp_service/query/tool/test_query_tools.py
- tests/unit_tests/mcp_service/saved_query/__init__.py
- tests/unit_tests/mcp_service/saved_query/tool/__init__.py
- tests/unit_tests/mcp_service/saved_query/tool/test_saved_query_tools.py
-
Files skipped - 0
-
Tools
- Whispers (Secret Scanner) - ✔︎ Successful
- Detect-secrets (Secret Scanner) - ✔︎ Successful
- MyPy (Static Code Analysis) - ✔︎ Successful
- Astral Ruff (Static Code Analysis) - ✔︎ 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
…ror response The full error details are already logged via ctx.error() to the server log; return a generic message to the client to avoid exposing internal DB errors.
start_time is a nullable epoch float that is not set for queries inserted outside of normal SQL Lab execution. Sorting by changed_on (always populated) gives stable, deterministic ordering regardless of how the query record was created.
Code Review Agent Run #4fe0faActionable Suggestions - 0Additional Suggestions - 1
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
…ies tools - Fix test_list_queries_default_order_is_start_time_desc → changed_on after default sort column change - Sanitize get_query_info error response: return generic message instead of str(e) to match get_saved_query_info (prevents leaking internal error text) - Add test_get_query_info_internal_error and test_get_saved_query_info_internal_error covering the InternalError exception path in both get-tools - Add test_list_saved_queries_select_columns_projects_fields and test_list_saved_queries_invalid_order_column_raises to match query tool coverage
|
Thanks again for the detailed review! Following up on the remaining items: #4 (test coverage gaps) — Added in commit
#4 (security) — Also fixed #5 (app.py instruction-text inconsistency) — Already resolved in a prior commit; both entries now say |
fastmcp client wraps tool ValueError as ToolError at the protocol level, so pytest.raises(ValueError) never matched. Use ToolError instead.
Code Review Agent Run #06bebeActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
|
A few schema issues worth addressing before merge:
|
- QueryInfo: add executed_sql, catalog, user_id fields - QueryFilter.col: add user_id and start_time as filterable columns - SavedQueryInfo: add catalog and last_run fields - SavedQueryFilter.col: add catalog and created_by_fk as filterable columns - ALL_QUERY_COLUMNS / ALL_SAVED_QUERY_COLUMNS: include new fields - Tests: update filter schema tests to reflect new valid columns, update mock factories with new fields
Use QueryInfo.model_fields.keys() / SavedQueryInfo.model_fields.keys() as the columns_available source in list_queries and list_saved_queries, rather than manually maintained ALL_QUERY_COLUMNS / ALL_SAVED_QUERY_COLUMNS constants. This ensures the advertised columns always exactly match what the response schema can serialize, preventing future drift between the constant and the schema definition. Pattern mirrors the fix applied to list_reports in #40348 per reviewer feedback from richardfogaca.
|
Applied cross-PR feedback from richardfogaca's thorough reviews on #40344 and #40348, same patterns addressed here where applicable. What was applied: columns_available derivation (from #40348 fix): Changed What was analyzed but not applicable:
|
Code Review Agent Run #f4791eActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
SUMMARY
Adds four new MCP tools across two new domains (`saved_query/` and `query/`):
Both domains follow the existing `database/`, `dataset/`, `chart/`, and `dashboard/` patterns: `ModelListCore`/`ModelGetInfoCore` for reuse, Pydantic schemas with field-level serialization context, `@tool` decorators with RBAC, and `event_logger` instrumentation.
Changes made in response to review feedback:
BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
N/A — backend API tools only
TESTING INSTRUCTIONS
```bash
Run unit tests for the new tools
pytest tests/unit_tests/mcp_service/saved_query/
pytest tests/unit_tests/mcp_service/query/
```
ADDITIONAL INFORMATION
Live Test Results
Tested on staging Preset workspace (build ID:
36a56752, MCP server:claude.ai preset stg).list_saved_queriesRequest:
{}Response:
{ "saved_queries": [ {"id": 113, "uuid": "b709065e-d448-472f-9cae-2e81eaad3772", "label": "Eval Saved Query 27May", "db_id": 2, "schema": ""}, {"id": 112, "uuid": "bcd4afa6-dbd8-498f-8b35-1113c0510fdf", "label": "Eval Query 2", "db_id": 2, "schema": ""}, {"id": 111, "uuid": "81ffd197-af0d-458f-acd8-c4c1082381e0", "label": "Eval Saved Query", "db_id": 2, "schema": ""} ], "count": 10, "total_count": 113, "page": 1, "page_size": 10, "total_pages": 12, "has_previous": false, "has_next": true, "columns_requested": ["id", "label", "db_id", "schema", "uuid"], "columns_loaded": ["id", "label", "db_id", "schema", "uuid"], "columns_available": ["id", "uuid", "label", "sql", "db_id", "schema", "catalog", "description", "changed_on", "created_on", "last_run"], "sortable_columns": ["id", "label", "db_id", "schema", "changed_on", "created_on"], "filters_applied": [], "pagination": {"page": 1, "page_size": 10, "total_count": 113, "total_pages": 12, "has_next": true, "has_previous": false}, "timestamp": "2026-05-29T17:13:29.950316Z" }✅ 113 saved queries returned (page 1 of 12), no error.
get_saved_query_infoRequest:
{"identifier": 113}Response:
{ "id": 113, "uuid": "b709065e-d448-472f-9cae-2e81eaad3772", "label": "Eval Saved Query 27May", "sql": "SELECT name, COUNT(*) FROM birth_names GROUP BY name ORDER BY COUNT(*) DESC LIMIT 10", "db_id": 2, "schema": "", "catalog": null, "description": "", "changed_on": "2026-05-28T03:58:45.001945", "created_on": "2026-05-28T03:58:45.001941", "last_run": null }✅ Full saved query detail including SQL returned, no error.
list_queriesRequest:
{}Response:
{ "queries": [ {"id": 832, "status": "success", "start_time": null, "database_id": 2, "schema": "public"}, {"id": 831, "status": "success", "start_time": null, "database_id": 2, "schema": "public"}, {"id": 830, "status": "success", "start_time": null, "database_id": 2, "schema": "public"} ], "count": 25, "total_count": 832, "page": 1, "page_size": 25, "total_pages": 34, "has_previous": false, "has_next": true, "columns_requested": ["id", "status", "start_time", "database_id", "schema"], "columns_loaded": ["id", "status", "start_time", "database_id", "schema"], "columns_available": ["id", "sql", "executed_sql", "status", "start_time", "end_time", "rows", "database_id", "schema", "catalog", "tab_name", "error_message", "client_id", "limit", "progress", "changed_on", "user_id"], "sortable_columns": ["id", "start_time", "end_time", "status", "database_id", "changed_on"], "filters_applied": [], "pagination": {"page": 1, "page_size": 25, "total_count": 832, "total_pages": 34, "has_next": true, "has_previous": false}, "timestamp": "2026-05-29T17:13:32.421422Z" }✅ 832 query history records (page 1 of 34), no error.
get_query_infoRequest:
{"identifier": 832}Response:
{ "id": 832, "sql": "SELECT * FROM birth_names", "executed_sql": null, "status": "success", "start_time": null, "end_time": null, "rows": 75691, "database_id": 2, "schema": "public", "catalog": null, "tab_name": null, "error_message": null, "client_id": "28669fc0fb0", "limit": null, "progress": 100, "changed_on": "2026-05-28T03:56:40.291917", "user_id": 38 }✅ Full query detail including SQL and row count (75,691 rows), no error.