-
Notifications
You must be signed in to change notification settings - Fork 17.9k
fix(mcp): add created_by_fk filter, field validators, and deduplicate defaults for database tools #39113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(mcp): add created_by_fk filter, field validators, and deduplicate defaults for database tools #39113
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -43,15 +43,6 @@ | |||||
|
|
||||||
| logger = logging.getLogger(__name__) | ||||||
|
|
||||||
| # Minimal defaults for reduced token usage - users can request more via select_columns | ||||||
| DEFAULT_DATABASE_COLUMNS = [ | ||||||
| "id", | ||||||
| "database_name", | ||||||
| "backend", | ||||||
| "expose_in_sqllab", | ||||||
| "changed_on_humanized", | ||||||
| ] | ||||||
|
|
||||||
|
|
||||||
| @tool( | ||||||
| tags=["core"], | ||||||
|
|
@@ -100,6 +91,7 @@ async def list_databases(request: ListDatabasesRequest, ctx: Context) -> Databas | |||||
| try: | ||||||
| from superset.daos.database import DatabaseDAO | ||||||
| from superset.mcp_service.common.schema_discovery import ( | ||||||
| DATABASE_DEFAULT_COLUMNS, | ||||||
| DATABASE_SORTABLE_COLUMNS, | ||||||
| get_all_column_names, | ||||||
| get_database_columns, | ||||||
|
|
@@ -120,7 +112,7 @@ def _serialize_database( | |||||
| output_schema=DatabaseInfo, | ||||||
| item_serializer=_serialize_database, | ||||||
| filter_type=DatabaseFilter, | ||||||
| default_columns=DEFAULT_DATABASE_COLUMNS, | ||||||
| default_columns=DATABASE_DEFAULT_COLUMNS, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Severity Level: Major
|
||||||
| default_columns=DATABASE_DEFAULT_COLUMNS, | |
| default_columns=[*DATABASE_DEFAULT_COLUMNS, "changed_on"], |
Steps of Reproduction ✅
1. Start Superset with MCP service enabled and call the `list_databases` FastMCP tool
without providing `select_columns` (default usage), which hits `list_databases()` in
`superset/mcp_service/database/tool/list_databases.py:56-63`.
2. Inside `list_databases`, a `ModelListCore` is constructed at
`superset/mcp_service/database/tool/list_databases.py:110-121` with
`default_columns=DATABASE_DEFAULT_COLUMNS` (`list_databases.py:115`), where
`DATABASE_DEFAULT_COLUMNS` is defined as `["id", "database_name", "backend",
"expose_in_sqllab", "changed_on_humanized"]` in
`superset/mcp_service/common/schema_discovery.py:61-67`.
3. Because `ListDatabasesRequest.select_columns` has a default empty list
(`superset/mcp_service/database/schemas.py:125-132`), `ModelListCore.run_tool`
(`superset/mcp_service/mcp_core.py:83-91`) treats it as falsy and sets `columns_to_load =
self.default_columns`, then calls `DatabaseDAO.list(..., columns=columns_to_load)` at
`mcp_core.py:93-102`; `BaseDAO.list` (`superset/daos/base.py:25-49,74-86`) builds a
SQLAlchemy query selecting only real column attributes (`id`, `database_name`, `backend`,
`expose_in_sqllab`) and returns Row objects that do NOT include `changed_on`.
4. Each Row is passed to `serialize_database_object` via `_serialize_database` at
`list_databases.py:103-107`, and `serialize_database_object`
(`superset/mcp_service/database/schemas.py:233-268`) calls `getattr(database,
"changed_on", None)` and `_humanize_timestamp(getattr(database, "changed_on", None))`;
since the Row lacks `changed_on`, `getattr` returns `None`, `_humanize_timestamp` returns
`None`, and both `changed_on` and `changed_on_humanized` end up as `None`. The final
response is produced by `result.model_dump(context={"select_columns": columns_to_filter})`
at `list_databases.py:145-154`, and `DatabaseInfo._filter_fields_by_context`
(`database/schemas.py:61-76`) filters fields to the requested columns, which now include
`"changed_on_humanized"` but not `"changed_on"`, so the client observes
`changed_on_humanized: null` for every database in the default `list_databases` response.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset/mcp_service/database/tool/list_databases.py
**Line:** 115:115
**Comment:**
*Logic Error: `DATABASE_DEFAULT_COLUMNS` includes `changed_on_humanized` but not `changed_on`; since the serializer computes humanized time from `changed_on`, default responses will produce `changed_on_humanized=None` for row-based DAO results. Ensure `changed_on` is also loaded when using default columns.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new parsing behavior for
filtersandselect_columns(JSON-string / CSV inputs) and the newly supportedcreated_by_fk/changed_by_fkfilter columns aren’t covered by unit tests. Please add tests (e.g., intests/unit_tests/mcp_service/database/tool/test_database_tools.py) to verify: (1) JSON-stringfiltersis accepted and converted intoDatabaseFiltermodels, (2) JSON-string and comma-separatedselect_columnsare accepted, and (3)created_by_fkfiltering flows through toDatabaseDAO.list(assert the DAO is called with aColumnOperatorwherecol == 'created_by_fk').