Skip to content

feat(mcp): add list_reports and get_report_info tools#1

Open
hbrooks wants to merge 14 commits into
masterfrom
demo/pr-40348
Open

feat(mcp): add list_reports and get_report_info tools#1
hbrooks wants to merge 14 commits into
masterfrom
demo/pr-40348

Conversation

@hbrooks

@hbrooks hbrooks commented May 28, 2026

Copy link
Copy Markdown

Originally PR apache#40348 in apache/superset by @aminghadersohi

aminghadersohi and others added 14 commits May 26, 2026 17:25
Adds list_reports and get_report_info MCP tools under a new
superset/mcp_service/report/ domain, following the canonical database
domain pattern. Includes unit tests and app.py registration.
- Register report model type in get_schema (Fix #1): add _get_report_schema_core
  factory + "report" entry in _SCHEMA_CORE_FACTORIES; ModelType now includes "report"
- Add OwnedByMeMixin/CreatedByMeMixin to ListReportsRequest (Fix #2)
- DRY up list_reports.py column constants (Fix #3): import REPORT_* constants and
  get_report_columns from schema_discovery; pass created_by_me/owned_by_me to run_tool
- Extend test coverage (Fix #6): humanized timestamp fields, invalid order_column
  guard, owned_by_me/created_by_me DAO filter injection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tools

- Add ReportListCore subclass in list_reports.py that overrides filter
  injection to use owners.id (instead of generic owner) and calls the
  DAO with filters= kwarg (instead of column_operators=) so tests can
  assert on the kwarg by name
- Extract _call_dao_list hook in ModelListCore so subclasses can change
  the DAO kwarg name without duplicating run_tool
- Add owners.id to SELF_REFERENCING_FILTER_COLUMNS so it is excluded
  from filters_applied in responses

Fixes: test_list_reports_owned_by_me_passed_to_dao,
       test_list_reports_created_by_me_passed_to_dao
created_by_fk was removed from SELF_REFERENCING_FILTER_COLUMNS so it
now appears in filters_applied, but ReportFilter.col Literal didn't
include it, causing pydantic validation error in list_reports responses.
…e ColumnOperator for filters_applied

- ReportFilter.col Literal no longer includes created_by_fk; callers
  must use the created_by_me flag instead (internal-only filter)
- ReportList.filters_applied typed as List[ColumnOperator] so that
  internally-injected ColumnOperator instances (e.g. created_by_fk from
  created_by_me=True) pass pydantic validation without coercion errors
…rning to exception handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… self-lookup filters

- Remove _call_dao_list override that used filters= kwarg (BaseDAO.list
  expects column_operators=), eliminating the runtime TypeError on every
  list_reports call
- Fix _prepend_self_lookup_filters to use a single created_by_fk_or_owner
  OR filter when both created_by_me and owned_by_me are set, replacing the
  two AND filters that produced intersection instead of union semantics
- Add apply_column_operators override to ReportScheduleDAO to handle the
  owners.id M2M filter and created_by_fk_or_owner OR filter via
  report_schedule_user join (mirrors DashboardDAO pattern)
- Add created_by_fk to ReportFilter.col so get_schema and list_reports
  advertise the same set of filterable columns
Update test_list_reports_owned_by_me_passed_to_dao and
test_list_reports_created_by_me_passed_to_dao to check column_operators=
kwarg instead of filters=, matching the BaseDAO.list contract now that
the _call_dao_list override is removed. Apply ruff 0.9.7 formatting.
Adds tests for the exception path in get_report_info (InternalError response),
ReportError.create() classmethod, _humanize_timestamp with naive/None input,
and serialize_report_object with None input.
Replace get_all_column_names(get_report_columns()) with
list(ReportInfo.model_fields.keys()) so that columns_available only
advertises columns that list_reports actually serializes, not every
field on the ReportSchedule SQLAlchemy model. Fields like timezone,
sql, email_subject are ORM-only and would silently return {} if
requested via select_columns.
get_schema(model_type='report') was deriving select_columns from the
full ReportSchedule SQLAlchemy model via get_report_columns(), advertising
fields like timezone, sql, and email_subject that ReportInfo cannot
serialize. Clients following that schema received empty report entries {}.

Add get_report_info_columns() which builds ColumnMetadata from
ReportInfo.model_fields — the same source already used by list_reports
for columns_available. Switch _get_report_schema_core() to use the new
function so get_schema output matches what list_reports can return.

Add a regression test that requests all serializable columns and asserts
each appears in the response and that columns_available matches exactly.
Add REPORT_FILTER_COLUMNS constant (the ReportFilter.col whitelist) and
an include_filter_columns allowlist parameter to ModelGetSchemaCore.
Pass include_filter_columns=REPORT_FILTER_COLUMNS to _get_report_schema_core
so that get_schema(model_type="report") only advertises filter columns that
list_reports actually accepts, preventing clients from discovering and then
being rejected for ORM-only filters like timezone, sql, email_subject, etc.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants