Skip to content

feat(mcp): add list and get tools for saved queries and query history#40346

Open
aminghadersohi wants to merge 8 commits into
masterfrom
amin/mcp-list-queries
Open

feat(mcp): add list and get tools for saved queries and query history#40346
aminghadersohi wants to merge 8 commits into
masterfrom
amin/mcp-list-queries

Conversation

@aminghadersohi
Copy link
Copy Markdown
Contributor

@aminghadersohi aminghadersohi commented May 22, 2026

SUMMARY

Adds four new MCP tools across two new domains (`saved_query/` and `query/`):

  • `list_saved_queries` — List saved SQL queries owned by the current user with filtering (label, db_id, schema), search, and pagination
  • `get_saved_query_info` — Get saved query details by numeric ID or UUID
  • `list_queries` — List SQL query history with filtering (status, database_id, schema), defaulting to most-recent-first (ordered by `start_time` desc, page_size 25)
  • `get_query_info` — Get query history details by numeric ID

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:

  • Added `sql` to the search columns for both `list_queries` and `list_saved_queries` so free-text search scans the SQL body
  • `get_saved_query_info` error responses now return a generic message (`"Failed to get saved query info"`) rather than raw exception text; full details are logged server-side only via `ctx.error()`

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

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

@gabotorresruiz
Copy link
Copy Markdown
Contributor

Review: list/get tools for saved queries and query history

Thorough pass over the 4 new MCP tools. Summary: clean, well-tested, correctly secured, and faithful to the existing list_datasets / get_dataset_info pattern. Approve with minor comments — nothing blocking.

Verified (all good ✅)

  • RBAC / row-level security is correctly enforced. QueryDAO.base_filter = QueryFilter restricts results to Query.user_id == current_user unless the user has can_access_all_queries; SavedQueryDAO.base_filter = SavedQueryFilter restricts to created_by == current_user. BaseDAO.list and find_by_id (default skip_base_filter=False) both apply these, so the new tools cannot leak another user's queries. The docstrings ("...or all queries for admins") match the actual filter behavior.
  • class_permission_name="Query" / "SavedQuery" match the REST API view names exactly, and method_permission_name defaults to read → gates on can_read. Consistent with other read tools.
  • Schema fields map to real columns: Query has start_time/end_time (Numeric, epoch seconds), changed_on, schema, status, database_id. Query has no uuid, so get_query_info correctly accepts numeric ID only, while get_saved_query_info correctly supports ID or UUID.
  • Tests cover the meaningful paths: basic list, filters, search, empty results, pagination, not-found, default page size (25), default ordering (start_time desc), and the search+filters mutual-exclusion validator.

Suggested changes (none blocking)

1. sql in DEFAULT_QUERY_COLUMNS + page_size=25 produces heavy default payloads. DEFAULT_QUERY_COLUMNS includes sql and DEFAULT_QUERY_PAGE_SIZE = 25, so a bare list_queries() returns 25 full SQL bodies. This contrasts with DEFAULT_SAVED_QUERY_COLUMNS (no sql) and DEFAULT_DATASET_COLUMNS, whose comment explicitly says "Minimal defaults for reduced token usage." For an LLM-facing tool, consider dropping sql from the query default columns — callers can request it via select_columns or use get_query_info.

2. QueryError.create() / SavedQueryError.create() are dead code. Both classmethods are defined but never called: the get-tools build QueryError(...) directly, ModelGetInfoCore builds error_schema(...) directly, and the list-tools re-raise. Either drop them or use them in the except handlers for consistency.

3. changed_on is selectable but not sortable for queries. It is in ALL_QUERY_COLUMNS but not SORTABLE_QUERY_COLUMNS, despite Query having an indexed changed_on column (ti_user_id_changed_on). Minor asymmetry — consider adding it to sortable, or leave as-is since start_time is the natural recency sort.

4. Minor test-coverage gaps (optional). No test exercises select_columns projection (the context-based field filtering is a core feature), rejection of a non-sortable order_column, or the InternalError exception path in the get-tools.

5. app.py instruction-text inconsistency (trivial). The list_saved_queries line says "(1-based pagination)" while list_queries says "(most recent first)". Both are 1-based; cosmetic only.

Notes (consistent with existing code, not PR issues)

  • List tools annotate -> QueryList | QueryError but actually return a dict from model_dump() and re-raise on error rather than returning QueryError. This mirrors list_datasets, so it's an established pattern.
  • error_type="InternalError" (PascalCase) vs "not_found" (snake_case) inconsistency is pre-existing (get_dataset_info does the same).
  • populate_by_name=True in the schemas is unnecessary (no aliases) but harmless and matches the dataset schema.

Item #1 is the only one I'd suggest addressing before merge; the rest are nits.

@github-actions github-actions Bot removed the api Related to the REST API label May 22, 2026
@aminghadersohi
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review! Addressed in ecec0a0:

#1 — Addressed: Dropped sql from DEFAULT_QUERY_COLUMNS. A bare list_queries() no longer returns full SQL bodies by default; callers use select_columns or get_query_info.

#2 — Addressed: Removed both QueryError.create() and SavedQueryError.create() dead classmethods.

#3 — Addressed: Added changed_on to SORTABLE_QUERY_COLUMNS for queries. The column is indexed (ti_user_id_changed_on) and the DAO supports it, same treatment as saved queries.

#4 — Addressed (partially): Added a select_columns projection test and an invalid order_column rejection test. Skipped the InternalError path since the list tools re-raise and that path is already covered by the middleware tests.

#5 — Addressed: Fixed the list_queries entry in app.py to say "(1-based pagination)" consistent with all other list tools.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 22, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 8dc800a
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a164e922ad65e0008915285
😎 Deploy Preview https://deploy-preview-40346--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.

Comment thread superset/mcp_service/saved_query/tool/get_saved_query_info.py
@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

❌ Patch coverage is 67.58621% with 94 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.18%. Comparing base (09a94fa) to head (ccd5c69).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
superset/mcp_service/query/tool/list_queries.py 33.33% 20 Missing ⚠️
...mcp_service/saved_query/tool/list_saved_queries.py 33.33% 20 Missing ⚠️
superset/mcp_service/query/schemas.py 84.61% 13 Missing and 1 partial ⚠️
superset/mcp_service/saved_query/schemas.py 83.52% 13 Missing and 1 partial ⚠️
superset/mcp_service/query/tool/get_query_info.py 43.47% 13 Missing ⚠️
...p_service/saved_query/tool/get_saved_query_info.py 43.47% 13 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           master   #40346    +/-   ##
========================================
  Coverage   64.18%   64.18%            
========================================
  Files        2592     2600     +8     
  Lines      139271   139588   +317     
  Branches    32337    32358    +21     
========================================
+ Hits        89385    89589   +204     
- Misses      48351    48460   +109     
- Partials     1535     1539     +4     
Flag Coverage Δ
hive 39.33% <67.58%> (+0.12%) ⬆️
mysql 58.74% <67.58%> (+0.02%) ⬆️
postgres 58.82% <67.58%> (+0.02%) ⬆️
presto 41.01% <67.58%> (+0.11%) ⬆️
python 60.37% <67.58%> (+0.01%) ⬆️
sqlite 58.47% <67.58%> (+0.02%) ⬆️
unit 100.00% <ø> (ø)

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

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review Agent Run #bd7d73

Actionable Suggestions - 3
  • superset/mcp_service/saved_query/tool/get_saved_query_info.py - 1
  • superset/mcp_service/query/tool/list_queries.py - 1
  • tests/unit_tests/mcp_service/query/tool/test_query_tools.py - 1
Additional Suggestions - 1
  • tests/unit_tests/mcp_service/saved_query/tool/test_saved_query_tools.py - 1
    • Incomplete pagination assertions · Line 258-273
      Test `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

AI Code Review powered by Bito Logo

Comment thread superset/mcp_service/saved_query/tool/get_saved_query_info.py
Comment thread superset/mcp_service/query/tool/list_queries.py
Comment thread tests/unit_tests/mcp_service/query/tool/test_query_tools.py Outdated
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented May 26, 2026

Code Review Agent Run #892ec7

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: ecec0a0..53be872
    • tests/unit_tests/mcp_service/query/tool/test_query_tools.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

AI Code Review powered by Bito Logo

aminghadersohi and others added 4 commits May 27, 2026 01:53
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
@aminghadersohi aminghadersohi force-pushed the amin/mcp-list-queries branch from 53be872 to 8dc800a Compare May 27, 2026 01:53
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review Agent Run #30743f

Actionable Suggestions - 1
  • superset/mcp_service/saved_query/schemas.py - 1
Additional Suggestions - 1
  • superset/mcp_service/query/tool/list_queries.py - 1
    • Unused function parameter · Line 98-99
      The `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
  • superset/mcp_service/saved_query/tool/get_saved_query_info.py - 1
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

AI Code Review powered by Bito Logo

Comment thread superset/mcp_service/saved_query/schemas.py
…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.
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented May 27, 2026

Code Review Agent Run #4fe0fa

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/mcp_service/saved_query/tool/get_saved_query_info.py - 1
    • Consistency: error message lost · Line 126-126
      This change removes exception details from the error response, making get_saved_query_info inconsistent with similar tools like get_dataset_info (line 166) and get_database_info that include `{str(e)}` in their SavedQueryError/DatasetError/DatabaseError messages. Users debugging issues won't have access to the underlying exception details.
      Code suggestion
      --- a/superset/mcp_service/saved_query/tool/get_saved_query_info.py
      +++ b/superset/mcp_service/saved_query/tool/get_saved_query_info.py
       @@ -125,5 +125,5 @@ async def get_saved_query_info(
            return SavedQueryError(
      -            error="Failed to get saved query info",
      +            error=f"Failed to get saved query info: {str(e)}",
                    error_type="InternalError",
                    timestamp=datetime.now(timezone.utc),
                )
Review Details
  • Files reviewed - 2 · Commit Range: 8dc800a..fdff27f
    • superset/mcp_service/saved_query/tool/get_saved_query_info.py
    • superset/mcp_service/query/tool/list_queries.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

AI Code Review powered by Bito Logo

…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
@aminghadersohi
Copy link
Copy Markdown
Contributor Author

Thanks again for the detailed review! Following up on the remaining items:

#4 (test coverage gaps) — Added in commit 163012b52f:

  • test_list_queries_select_columns_projects_fields — already existed from a prior pass ✓
  • test_list_queries_invalid_order_column_raises — already existed ✓
  • test_list_saved_queries_select_columns_projects_fieldsadded
  • test_list_saved_queries_invalid_order_column_raisesadded
  • test_get_query_info_internal_erroradded (InternalError path, generic message)
  • test_get_saved_query_info_internal_erroradded (InternalError path, generic message)

#4 (security) — Also fixed get_query_info to return a generic error message instead of str(e), matching the get_saved_query_info fix from 2c742bb4fa.

#5 (app.py instruction-text inconsistency) — Already resolved in a prior commit; both entries now say (1-based pagination).

fastmcp client wraps tool ValueError as ToolError at the protocol level,
so pytest.raises(ValueError) never matched. Use ToolError instead.
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented May 27, 2026

Code Review Agent Run #06bebe

Actionable Suggestions - 0
Review Details
  • Files reviewed - 3 · Commit Range: fdff27f..ccd5c69
    • superset/mcp_service/query/tool/get_query_info.py
    • tests/unit_tests/mcp_service/query/tool/test_query_tools.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

AI Code Review powered by Bito Logo

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