Skip to content

feat(mcp): add create_dataset tool to register physical tables as datasets - #40340

Merged
aminghadersohi merged 21 commits into
apache:masterfrom
aminghadersohi:aminghadersohi/mcp-create-dataset
Jun 16, 2026
Merged

feat(mcp): add create_dataset tool to register physical tables as datasets#40340
aminghadersohi merged 21 commits into
apache:masterfrom
aminghadersohi:aminghadersohi/mcp-create-dataset

Conversation

@aminghadersohi

Copy link
Copy Markdown
Contributor

Summary

Adds a create_dataset MCP tool that lets callers register an existing physical
database table as a Superset dataset — the programmatic equivalent of
Data → Datasets → + Dataset in the UI.

  • CreateDatasetRequest schemadatabase_id, schema (alias for schema_name
    to avoid the Pydantic v2 BaseModel.schema() clash), table_name, optional catalog
    and owners; whitespace-only values for schema/catalog are normalised to None
  • Pre-checks — verifies the database exists and the caller has table-level access
    before calling the command
  • create_dataset tool — wraps CreateDatasetCommand, uses
    DatasetInvalidError.get_list_classnames() (public API) to classify wrapped validation
    errors; returns DatasetInfo (same shape as get_dataset_info) so the id feeds
    directly into generate_chart or generate_explore_link
  • Typed error handling — distinct error_type values for each failure class:
    DatabaseNotFoundError, AccessDeniedError, DatasetExistsError,
    TableNotFoundError, ValidationError, CreateFailedError, InternalError
  • 11 unit tests — success, owners forwarding, exists/not-found/validation/internal
    errors, missing required fields, full DatasetInfo shape, database not found,
    access denied, no-schema, with-catalog
  • DEFAULT_INSTRUCTIONS updated to list the new tool

Motivation

The MCP service already exposes create_virtual_dataset for SQL-based datasets. This
PR adds the physical-table counterpart so agents can complete the full
"find DB → register table → chart it" workflow without manual UI steps.

Test plan

  • pytest tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.py -x
  • Existing dataset tool tests still pass
  • pre-commit run --files superset/mcp_service/dataset/tool/create_dataset.py superset/mcp_service/dataset/schemas.py

@codecov

codecov Bot commented May 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 34.21053% with 50 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.13%. Comparing base (a5fe47e) to head (035d488).
⚠️ Report is 9 commits behind head on master.

Files with missing lines Patch % Lines
...uperset/mcp_service/dataset/tool/create_dataset.py 20.75% 42 Missing ⚠️
superset/mcp_service/dataset/schemas.py 66.66% 7 Missing ⚠️
superset/mcp_service/app.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #40340      +/-   ##
==========================================
- Coverage   64.14%   64.13%   -0.02%     
==========================================
  Files        2653     2654       +1     
  Lines      143494   143566      +72     
  Branches    33112    33122      +10     
==========================================
+ Hits        92048    92073      +25     
- Misses      49837    49884      +47     
  Partials     1609     1609              
Flag Coverage Δ
hive 39.51% <34.21%> (-0.01%) ⬇️
mysql 58.24% <34.21%> (-0.03%) ⬇️
postgres 58.31% <34.21%> (-0.03%) ⬇️
presto 41.11% <34.21%> (-0.01%) ⬇️
python 59.78% <34.21%> (-0.03%) ⬇️
sqlite 57.93% <34.21%> (-0.03%) ⬇️
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 force-pushed the aminghadersohi/mcp-create-dataset branch from a7720dd to caa3d97 Compare May 22, 2026 01:43
@aminghadersohi
aminghadersohi force-pushed the aminghadersohi/mcp-create-dataset branch from caa3d97 to e220068 Compare May 22, 2026 01:44
@netlify

netlify Bot commented May 22, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

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

@aminghadersohi
aminghadersohi marked this pull request as ready for review May 26, 2026 18:49
@dosubot dosubot Bot added the data:dataset Related to dataset configurations label May 26, 2026
@aminghadersohi
aminghadersohi removed the request for review from Antonio-RiveroMartnez May 26, 2026 18:51
@netlify

netlify Bot commented May 26, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 035d488
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a26fb639a4ef500081311c4
😎 Deploy Preview https://deploy-preview-40340--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/dataset/schemas.py Outdated
Comment thread superset/mcp_service/dataset/tool/create_dataset.py Outdated
Comment thread superset/mcp_service/dataset/tool/create_dataset.py Outdated
Comment thread superset/mcp_service/dataset/tool/create_dataset.py Outdated
@bito-code-review

bito-code-review Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #ce1551

Actionable Suggestions - 0
Additional Suggestions - 5
  • superset/mcp_service/dataset/schemas.py - 1
    • Missing input validation · Line 327-351
      Add field validators and length constraints to CreateDatasetRequest to match CreateVirtualDatasetRequest patterns. `table_name` lacks `min_length=1` validation, and no validators exist for empty-string inputs unlike `CreateVirtualDatasetRequest` (lines 389-401) which validates `sql` and `dataset_name`. This inconsistency could allow empty strings to reach the backend.
  • superset/mcp_service/dataset/tool/__init__.py - 1
    • Documentation inconsistency with exports · Line 18-27
      app.py lines 137-138 document `create_virtual_dataset` and `query_dataset` as available tools, but removing them from this __init__.py will hide them from MCP clients. If intentional, update app.py documentation to match.
  • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.py - 2
    • Semantic duplication risk · Line 34-69
      Function `_make_mock_dataset` duplicates `create_mock_dataset` (lines 60-100 in `test_dataset_tools.py`). Creating semantic duplication that increases maintenance burden and divergence risk.
    • Missing test coverage · Line 209-233
      Test coverage gap: `DatasetInvalidError` caught at line 124-129 of `create_dataset.py` is not tested. Adding this test ensures complete exception handling coverage.
  • superset/mcp_service/dataset/tool/create_dataset.py - 1
    • Function has too many return statements · Line 46-46
      The function has 7 return statements (limit is 6). Consider refactoring to reduce complexity by consolidating error handling or extracting logic into helper functions.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/mcp_service/app.py - 3
  • superset/mcp_service/dataset/tool/create_dataset.py - 2
  • superset/mcp_service/dataset/tool/__init__.py - 1
    • Removed exports break tool registration · Line 18-21
Review Details
  • Files reviewed - 5 · Commit Range: 19741a9..88326f2
    • superset/mcp_service/app.py
    • superset/mcp_service/dataset/schemas.py
    • superset/mcp_service/dataset/tool/__init__.py
    • superset/mcp_service/dataset/tool/create_dataset.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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

@bito-code-review

bito-code-review Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #25bcce

Actionable Suggestions - 0
Review Details
  • Files reviewed - 5 · Commit Range: 88326f2..3c4396a
    • superset/mcp_service/app.py
    • superset/mcp_service/dataset/tool/__init__.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.py
    • superset/mcp_service/dataset/schemas.py
    • superset/mcp_service/dataset/tool/create_dataset.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

Copy link
Copy Markdown
Contributor Author

Thanks for the bito suggestions. Addressed in the latest commit (4faa3ae):

  • Missing test for DatasetInvalidError -- added test_create_dataset_invalid_error covering that handler path
  • table_name missing min_length=1 -- added, matching the pattern in CreateVirtualDatasetRequest

The other notes (mock duplication with test_dataset_tools.py, function return-count) are style observations that do not affect correctness.

@bito-code-review

bito-code-review Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #ce0313

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/mcp_service/dataset/schemas.py - 1
    • Whitespace input not stripped · Line 344-350
      The new `min_length=1` constraint rejects empty strings but accepts whitespace-only strings like `" "` or `" "`. The `schema` field in this same schema is already stripped to `None` in `create_dataset.py:85`, and the codebase explicitly documents at `system/schemas.py` that "min_length=1 alone admits whitespace-only strings" which would produce a LIKE pattern matching every row. Without stripping, a whitespace-only `table_name` will be passed directly to `CreateDatasetCommand`, risking database errors or security issues.
      Code suggestion
      --- superset/mcp_service/dataset/tool/create_dataset.py (line 85) ---
       85: -    schema = request.schema.strip() if request.schema else None
       85: +    schema = request.schema.strip() if request.schema else None
       86: +    table_name = request.table_name.strip()
      --- superset/mcp_service/dataset/tool/create_dataset.py (line 101-104) ---
       101:         dataset_properties: dict[str, object] = {
       102:             "database": request.database_id,
       103: -            "table_name": request.table_name,
       104: +            "table_name": table_name,
       105:         }
Review Details
  • Files reviewed - 2 · Commit Range: 3c4396a..4faa3ae
    • superset/mcp_service/dataset/schemas.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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

Copy link
Copy Markdown
Contributor Author

Thanks for the additional suggestion. Fixed in commit f58a31c: table_name is now stripped of whitespace before being forwarded to CreateDatasetCommand, mirroring the existing schema normalization.

@aminghadersohi
aminghadersohi force-pushed the aminghadersohi/mcp-create-dataset branch from f58a31c to 35c8c7c Compare May 28, 2026 12:58
@bito-code-review

bito-code-review Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #069823

Actionable Suggestions - 0
Additional Suggestions - 3
  • superset/mcp_service/app.py - 2
    • Missing list_databases from tool list · Line 133-134
      `list_databases` is referenced in workflows (line 231) and is imported/registered (line 572), but is missing from the "Available tools" list. Add it to Dataset Management for consistency with other tool listings.
      Code suggestion
      --- a/superset/mcp_service/app.py
      +++ b/superset/mcp_service/app.py
       @@ -132,6 +132,7 @@ Available tools:
       
        Dataset Management:
      - list_datasets: List datasets with advanced filters (1-based pagination)
      +- list_databases: List database connections with advanced filters (1-based pagination)
      - get_dataset_info: Get detailed dataset information by ID (includes columns/metrics)
      - create_dataset: Register a physical table as a dataset against an existing DB connection (requires write access)
      - create_virtual_dataset: Save a SQL query as a virtual dataset for charting (requires write access)
    • Documentation outdated · Line 589-614
      The comments about tool imports triggering transitive warnings are now outdated since tool imports in app.py don't trigger the problematic imports anymore - that happens in flask_singleton.py where initialize_core_mcp_dependencies() is called.
  • superset/mcp_service/dataset/schemas.py - 1
    • Missing max_length constraint · Line 344-349
      The `table_name` field only has `min_length=1` but lacks `max_length`, unlike the comparable `dataset_name` field in `CreateVirtualDatasetRequest` which has `max_length=250`. Per rule [8197], schemas should maintain complete coverage for all supported library properties.
      Code suggestion
      --- a/superset/mcp_service/dataset/schemas.py
      +++ b/superset/mcp_service/dataset/schemas.py
       @@ -344,7 +344,8 @@ class CreateDatasetRequest(BaseModel):
            table_name: Annotated[
                str,
                Field(
      -            min_length=1,
      +            min_length=1,
      +            max_length=250,
                    description="Name of the physical table to register as a dataset",
                ),
            ]
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/mcp_service/app.py - 2
Review Details
  • Files reviewed - 5 · Commit Range: e00f943..35c8c7c
    • superset/mcp_service/app.py
    • superset/mcp_service/dataset/schemas.py
    • superset/mcp_service/dataset/tool/__init__.py
    • superset/mcp_service/dataset/tool/create_dataset.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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

@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 #8742b3

Actionable Suggestions - 1
  • superset/mcp_service/dataset/tool/create_dataset.py - 1
Additional Suggestions - 4
  • superset/mcp_service/app.py - 3
    • Critical operational guardrails removed · Line 319-363
      The diff removes a 'CRITICAL RULES' section containing important operational guardrails: (1) never fabricate URLs, (2) use correct dashboard tools, (3) use update_chart for modifications, (4) use exact parameter names. Removing operational guardrails from LLM instructions increases risk of incorrect behavior.
    • Incomplete capability description · Line 143-143
      The simplified description 'Get a visual preview of a chart with image URL' omits that previews can also return ASCII text, Explore URLs, table data, or Vega-Lite specs. Users may not discover these capabilities without the full description.
    • Tool grouping ambiguity · Line 133-133
      The `list_databases` and `get_database_info` entries appear at the same indentation level as 'Dataset Management:' header, creating visual ambiguity about section boundaries. The original structure had a clear 'Database Connections' section.
  • superset/mcp_service/dataset/tool/create_dataset.py - 1
    • Misleading comment text · Line 84-86
      The comment at line 84 says 'treat blank schema as None' but this is default Python behavior when a falsy value is used in a conditional. The code correctly handles schema=None but the comment appears misleading.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.py - 3
  • superset/mcp_service/app.py - 4
Review Details
  • Files reviewed - 5 · Commit Range: 3125cb0..f7aea7b
    • superset/mcp_service/app.py
    • superset/mcp_service/dataset/schemas.py
    • superset/mcp_service/dataset/tool/__init__.py
    • superset/mcp_service/dataset/tool/create_dataset.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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/dataset/tool/create_dataset.py

@richardfogaca richardfogaca 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.

Posting on Richard's behalf - this is his PR reviewer agent. Forward any pushback to him and he'll loop me back in.

Left a few notes below - the main themes are startup/registration safety, physical-table access checks, and keeping the new typed error contract reachable. All line numbers verified against HEAD f7aea7b927.

Functional - worth addressing before merge

  • superset/mcp_service/app.py:540

    This removes the initialize_core_mcp_dependencies() call before the file imports all tool modules. Those modules use @tool from superset_core.mcp.decorators, whose default implementation raises NotImplementedError until injection runs, so direct/standalone imports of superset.mcp_service.app can fail before tools register.

    WDYT - could we keep the initialization before the tool imports, or otherwise prove every app.py entrypoint initializes the core MCP decorators first?

  • superset/mcp_service/dataset/tool/create_dataset.py:112

    The physical-table path goes straight into CreateDatasetCommand, but that command only calls security_manager.raise_for_access(...) for the sql/virtual-dataset path. For physical tables, a user with can_write on Dataset but no database/catalog/schema/table access can still ask MCP to register and fetch metadata for an arbitrary table.

    Could we add an explicit raise_for_access(database=database, table=Table(...)) pre-check for this tool and cover the access-denied case?

  • superset/mcp_service/dataset/tool/create_dataset.py:126

    These DatasetExistsValidationError / TableNotFoundValidationError branches look unreachable with the real command. CreateDatasetCommand.validate() collects those validation errors and raises a single DatasetInvalidError(exceptions=...), so duplicate and missing-table cases will currently return generic ValidationError instead of the advertised DatasetExistsError / TableNotFoundError.

    WDYT - should this inspect exc.get_list_classnames() inside the DatasetInvalidError branch instead of catching the nested validation classes directly?

  • superset/mcp_service/dataset/schemas.py:336

    CreateDatasetRequest accepts database_id, schema, table_name, and owners, but not catalog. That means MCP cannot register physical tables outside the default catalog even though the underlying command supports catalog and the PR summary says the tool accepts it.

    Could we add catalog here and pass it through with the same blank-string normalization as schema?

@aminghadersohi

Copy link
Copy Markdown
Contributor Author

Thanks @richardfogaca for the thorough review! Here is what was addressed in commit dea3f47285:

  1. app.py:540 — Initialization order ✅ Restored initialize_core_mcp_dependencies() in app.py before the tool imports. The call was accidentally removed; without it a standalone import superset.mcp_service.app would hit NotImplementedError when the @tool stub fires. Back with a comment explaining the contract.

  2. create_dataset.py:112 — Physical-table access check — You are right that a user with Dataset.write but no table-level access can register arbitrary tables via MCP. The same gap exists in the REST API (POST /api/v1/dataset/) — CreateDatasetCommand.validate() only calls raise_for_access for the SQL/virtual path, not the physical-table path. Adding the check only in the MCP layer would create inconsistency. Tracking as a follow-up to CreateDatasetCommand.validate() so the REST API and MCP are fixed together.

  3. create_dataset.py:126 — Unreachable exception handlers ✅ You are correct — CreateDatasetCommand.validate() always wraps DatasetExistsValidationError and TableNotFoundValidationError into a single DatasetInvalidError(exceptions=[...]) and never raises them directly. The handlers now inspect exc.get_list_classnames() inside the DatasetInvalidError branch so the typed DatasetExistsError / TableNotFoundError responses are actually reachable. Tests updated to use DatasetInvalidError(exceptions=[...]) matching real command behavior.

  4. schemas.py:336 — Missing catalog field ✅ Added catalog: str | None = None to CreateDatasetRequest with the same blank-string normalization as schema, passed through to CreateDatasetCommand properties.

@bito-code-review

bito-code-review Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #3856ee

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/mcp_service/app.py - 1
Review Details
  • Files reviewed - 4 · Commit Range: f7aea7b..dea3f47
    • superset/mcp_service/app.py
    • superset/mcp_service/dataset/schemas.py
    • superset/mcp_service/dataset/tool/create_dataset.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review, Richard! Addressing each point:

1. app.py:540 — init order ✅ Fixed in dea3f47285

initialize_core_mcp_dependencies() is now called immediately after mcp = create_mcp_app() and before any tool module imports. The @tool stub raises NotImplementedError until injection runs, so this ordering is enforced by the comment block and commit message.

2. create_dataset.py:112 — physical-table access check ✅ Fixed in b44e9b9065 (just pushed)

Added an explicit pre-check before CreateDatasetCommand:

database = DatabaseDAO.find_by_id(request.database_id)
if database is None:
    return DatasetError.create(error=..., error_type="DatabaseNotFoundError")

table_obj = Table(table_name, schema, catalog)
try:
    security_manager.raise_for_access(database=database, table=table_obj)
except SupersetSecurityException as exc:
    return DatasetError.create(error=str(exc), error_type="AccessDeniedError")

This mirrors the check CreateDatasetCommand.validate() already does for the SQL/virtual path. New test coverage added for both the not-found and access-denied paths.

3. create_dataset.py:126 — unreachable DatasetExistsValidationError/TableNotFoundValidationError branches ✅ Fixed in dea3f47285

The two direct except handlers were removed. The DatasetInvalidError branch now calls exc.get_list_classnames() and maps to DatasetExistsError / TableNotFoundError / ValidationError accordingly. Tests added in test_create_dataset_already_exists and test_create_dataset_table_not_found.

4. schemas.py:336 — missing catalog field ✅ Fixed in dea3f47285

CreateDatasetRequest now includes catalog: str | None = None with the same blank-string normalization as schema. It's forwarded to CreateDatasetCommand when non-null.

@bito-code-review

bito-code-review Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #2d7129

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/mcp_service/dataset/tool/create_dataset.py - 1
    • Function signature type mismatch · Line 43-43
      The helper `_classify_invalid_error` should accept a `CommandInvalidError` parameter instead of `object`. Update its signature to `exc: CommandInvalidError` (imported from `superset.commands.exceptions`) and remove the `# type: ignore[attr-defined]` comments on `get_list_classnames()` and `normalized_messages()`.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.py - 1
Review Details
  • Files reviewed - 2 · Commit Range: dea3f47..b44e9b9
    • superset/mcp_service/dataset/tool/create_dataset.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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
aminghadersohi force-pushed the aminghadersohi/mcp-create-dataset branch from b44e9b9 to 691fd6b Compare June 1, 2026 17:27
@bito-code-review

bito-code-review Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #cab258

Actionable Suggestions - 0
Review Details
  • Files reviewed - 5 · Commit Range: fd9842a..bd5ee96
    • superset/mcp_service/app.py
    • superset/mcp_service/dataset/schemas.py
    • superset/mcp_service/dataset/tool/__init__.py
    • superset/mcp_service/dataset/tool/create_dataset.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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

@richardfogaca richardfogaca 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.

Posting on Richard's behalf — this is his PR reviewer agent. Forward any pushback to him and he'll loop me back in.

Left one validation note below; otherwise the tool path looks consistent with the existing dataset command access checks.

Functional — small validation gap

  • superset/mcp_service/dataset/schemas.py:373

    _normalize_optional_str() returns None for any non-string schema or catalog input. Since create_dataset() only forwards those fields when they are not None, a malformed request like {"schema": 123} or {"catalog": {"name": "hive"}} is silently treated as if the namespace was omitted, which can register the table against the default schema/catalog instead of failing request validation.

    WDYT — could this return the original non-string value so Pydantic rejects it, or explicitly raise a validation error while still normalizing blank strings to None?

@bito-code-review

bito-code-review Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d360cd

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: bd5ee96..fc3d500
    • superset/mcp_service/dataset/schemas.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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 21 commits June 8, 2026 17:22
…asets

Adds create_dataset MCP tool that wraps POST /api/v1/dataset/ so skills and
agents can register an existing physical table as a Superset dataset without
manual UI interaction. Returns DatasetInfo (same shape as get_dataset_info)
so the resulting dataset_id feeds directly into generate_chart.

- CreateDatasetRequest schema (database_id, schema, table_name, owners?)
- Tool file with typed error handling (exists/not-found/validation/internal)
- Registered in dataset/tool/__init__.py and app.py
- DEFAULT_INSTRUCTIONS updated to list create_dataset
- Unit tests covering success, owners, error cases, and full DatasetInfo shape
- schemas.py: restore full apache/master version and add CreateDatasetRequest
  (previous cherry-pick used an older shorter version missing helper functions
  _sanitize_dataset_info_for_llm_context, _humanize_timestamp, etc.)
- create_dataset.py: remove parse_request decorator (not in apache/master yet)
…and is a lazy import

CreateDatasetCommand is imported inside the function body, so patching at
superset.mcp_service.dataset.tool.create_dataset.CreateDatasetCommand fails
with AttributeError. Patch at the source module instead.

Also fix data["schema_name"] assertions: DatasetInfo.model_serializer renames
the field to "schema" in the serialized output.
…ataset

Restores tool imports that were accidentally dropped from app.py:
create_virtual_dataset, query_dataset, get_chart_sql, get_chart_type_schema,
get_database_info, list_databases, save_sql_query. Exports create_virtual_dataset
and query_dataset from dataset/tool/__init__.py. Fixes KeyError in test_create_dataset
by setting is_favorite=None on the mock dataset to avoid Pydantic bool|None
validation errors from MagicMock auto-attributes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…decorator, normalize whitespace

- CreateDatasetRequest.schema is now str | None (default None) so databases
  without schema namespaces (e.g. SQLite) can register tables without error
- create_dataset switches from @mcp.tool/@mcp_auth_hook to the standard @tool
  decorator from superset_core.mcp.decorators, adding Dataset write RBAC and
  ToolAnnotations consistent with create_virtual_dataset
- Blank/whitespace-only schema values are normalized to None before forwarding
  to CreateDatasetCommand, avoiding spurious table-not-found failures
- Unexpected exceptions now re-raise (middleware handles them) instead of
  being swallowed into an InternalError response; test updated accordingly
- Uses DatasetError.create() factory and event_logger/ctx instrumentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add test_create_dataset_invalid_error to cover the DatasetInvalidError
  handler in create_dataset (previously untested path)
- Add min_length=1 to CreateDatasetRequest.table_name to reject empty
  strings at the schema layer, consistent with CreateVirtualDatasetRequest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors schema normalization — table_name is now stripped before being
forwarded to CreateDatasetCommand, preventing whitespace-only strings
from reaching the database layer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…achable handlers, add catalog

- Restore initialize_core_mcp_dependencies() in app.py before tool imports.
  The PR accidentally removed the explicit call; without it, standalone imports
  of app.py could fail because @tool stubs raise NotImplementedError until
  the host implementation is injected.

- Fix unreachable DatasetExistsValidationError / TableNotFoundValidationError
  handlers: CreateDatasetCommand.validate() always wraps these into a single
  DatasetInvalidError(exceptions=[...]) and never raises them directly.
  Now inspects exc.get_list_classnames() inside the DatasetInvalidError branch
  to return the typed DatasetExistsError / TableNotFoundError responses.
  Update tests to use DatasetInvalidError(exceptions=[...]) matching real behavior.

- Add catalog field to CreateDatasetRequest (str | None, default None) with
  the same blank-string normalization as schema, and pass it through to
  CreateDatasetCommand properties. Add test_create_dataset_with_catalog.
Before forwarding to CreateDatasetCommand, look up the database by id
and call security_manager.raise_for_access(database, table) to enforce
table-level access. CreateDatasetCommand only checks access for virtual
(SQL) datasets, leaving physical-table registration ungated.

Returns DatabaseNotFoundError when the database_id is invalid, and
AccessDeniedError when the caller lacks table access. Extracts
_classify_invalid_error helper to reduce cyclomatic complexity within
the tool function. Adds test coverage for both new error paths.
- B1/B2: Move inline imports in create_dataset.py and tests to module top-level
- H1: Replace str(exc) in AccessDenied/CreateFailed with static messages; log detail server-side
- H2: Add MCP_DISABLED_TOOLS to config.py; use config["KEY"] instead of config.get() in app.py
- H3: Add circular-import justification comments to inline imports in app.py
- H4: Replace f-string database ID in DatabaseNotFoundError with static "Database not found"
- M1: Narrow broad except Exception to except (json.JSONDecodeError, TypeError) in schemas.py
- M3: Remove redundant inline datetime import inside DatasetError.create(); use top-level import
- M4: Log only type(exc).__name__ to ctx.error for unexpected exceptions; use logger.exception
- M5: Add -> None return type annotations to all 11 test methods
- M6: Add inline comment explaining deferred sub-type classification in _classify_invalid_error
- TC1: Add test for table_name whitespace normalization
- TC2: Add test for blank schema normalization to None
- TC3: Add test for serialize_dataset_object returning None (SerializationError path)
- TC4: Assert static "Access denied" message in test_create_dataset_access_denied

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use proper type instead of object to remove type: ignore[attr-defined]
comments — CommandInvalidError already defines get_list_classnames()
and normalized_messages().
Four root causes:

1. config["KEY"] -> config.get("KEY", default) in app.py: tests mock
   config.get() but bracket access bypassed the mock, causing KeyError
   or returning MagicMock instead of string for APP_NAME/MCP_DISABLED_TOOLS.

2. Moved imports in create_dataset.py broke @patch("superset.commands...
   .CreateDatasetCommand") — lazy imports (inside the function body)
   pick up the active mock; top-level imports bind before the patch runs.
   Fixed by patching superset.mcp_service.dataset.tool.create_dataset.*
   in all relevant tests. Same fix applied to serialize_dataset_object.

3. Pydantic v2 'schema' field naming conflict: BaseModel.schema is a
   deprecated classmethod; when the field is omitted from the request
   Pydantic does not store None in instance.__dict__, so attribute lookup
   returns the classmethod (a function) instead of None. Fixed by using
   request.model_dump() to access 'schema' and 'catalog' field values.

4. DatasetError.error field_validator wrapped error strings in
   UNTRUSTED-CONTENT tags, breaking assertions like == "Access denied".
   DatasetError carries system-generated messages, not user-controlled
   data, so the sanitization was incorrect. Validator removed.
Two test failures in CI:

1. test_create_dataset_database_not_found:
   model_dump() for a Pydantic v2 model with a field named 'schema' (which
   shadows the deprecated BaseModel.schema classmethod) may still return the
   classmethod object via internal getattr, making the truthy check pass and
   .strip() fail with AttributeError. Switch to isinstance(x, str) guard for
   both schema and catalog normalization.

2. test_error_responses_sanitize_prompt_facing_error_text[DatasetError]:
   DatasetError was missing the @field_validator("error") that ChartError and
   DashboardError both have, so prompt-facing error text was returned unsanitized
   instead of wrapped in LLM context delimiters.
…nitization

DatasetError now wraps error strings in LLM context delimiters (matching
DashboardError/ChartError). Update the two tests that checked exact error
strings to use substring containment so they pass regardless of delimiter
wrapping.
…taset

Empty-string schema/catalog values (e.g. schema="") were passing
isinstance(x, str) but evaluating to "" after .strip(), which is not
None — so the 'if schema is not None' guard added them to the command
properties dict.  Use `or None` after strip so blank strings collapse
to None and are excluded from the command properties entirely.

Fixes test_create_dataset_blank_schema_normalized_to_none.
…k catalog test

- Switch DatasetCreateFailedError handler from logger.error (loses traceback)
  to logger.exception so the full stack trace is captured on failure.
- Add test_create_dataset_blank_catalog_normalized_to_none to cover the
  symmetrical case with blank catalog (parallel to blank schema test).
…rror test; rename test

- Switch all @patch("superset.mcp_service.dataset.tool.create_dataset.X")
  to @patch.object(create_dataset_module, "X") using importlib.import_module.
  The tool package __init__.py exports create_dataset as a function, which
  shadows the module name for getattr-based resolution used by mock.patch in
  Python <3.12.  importlib.import_module resolves via sys.modules and always
  returns the module object, matching the pattern used in test_query_dataset.py.
- Add test_create_dataset_create_failed_error covering the DatasetCreateFailedError
  path that returns CreateFailedError (all other error paths were already tested).
- Rename test_create_dataset_serialization_error to
  test_create_dataset_when_serialize_returns_none — the test mocks the function
  to return None rather than raising an exception, so the old name was misleading.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass non-string values through the optional-namespace validator unchanged so
Pydantic's type validation rejects them, instead of silently coercing a
malformed value (int, dict) to None and registering against the default
namespace.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aminghadersohi
aminghadersohi force-pushed the aminghadersohi/mcp-create-dataset branch from fc3d500 to 035d488 Compare June 8, 2026 17:26
@bito-code-review

bito-code-review Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #466da2

Actionable Suggestions - 0
Review Details
  • Files reviewed - 5 · Commit Range: 976b2a9..035d488
    • superset/mcp_service/app.py
    • superset/mcp_service/dataset/schemas.py
    • superset/mcp_service/dataset/tool/__init__.py
    • superset/mcp_service/dataset/tool/create_dataset.py
    • tests/unit_tests/mcp_service/dataset/tool/test_create_dataset.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

@richardfogaca richardfogaca 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.

LGTM

@aminghadersohi
aminghadersohi merged commit 7cb4990 into apache:master Jun 16, 2026
58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data:dataset Related to dataset configurations size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants