fix(mcp): fall back to title match when dashboard slug lookup misses - #39567
Conversation
Many imported/example dashboards have empty slug fields, so get_dashboard_info with an agent-guessed slug (e.g. "world-banks-data") silently returned not_found. ModelGetInfoCore now tries an exact case-insensitive title match, then a slugified-title match, before giving up. When multiple titles slugify to the same value, returns ambiguous_identifier listing the candidate ids. The column name is sourced from DAO.title_column (set on DashboardDAO) so other tools can opt in without touching the core. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review Agent Run #3ecdb0Actionable 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 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #39567 +/- ##
==========================================
- Coverage 64.48% 64.44% -0.05%
==========================================
Files 2566 2566
Lines 134046 134043 -3
Branches 31147 31126 -21
==========================================
- Hits 86445 86388 -57
- Misses 46103 46157 +54
Partials 1498 1498
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:
|
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Code Review Agent Run #4012daActionable 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 |
Match the codebase convention used in superset/{cachekeys,databases,
annotation_layers,...}/api.py and drop the redundant method-level
import inside _base_filtered_query.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-filter via title_col.ilike("%word1%word2%...%") so the DB narrows
the candidate set; _slugify only confirms the small remainder.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review Agent Run #34ae6eActionable 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 |
Two issues in the slugified-title fallback flagged in review: 1. The ILIKE prefilter was built from the slugified identifier (apostrophes stripped), but compared against the raw title column — so "World Bank's Data" wouldn't match "%world%banks%data%" because "banks" is not a substring of "bank's". Apply the same apostrophe normalization to the title side via SQL REPLACE so the DB filter stays consistent with the Python `_slugify` confirmation step. 2. Without an ORDER BY, the "first match" returned on a slug collision is whatever the DB happens to yield first — non-deterministic across runs. Order the candidate query by primary key so the lowest-id match wins consistently, and so the warning logs the same id every time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review Agent Run #126679Actionable 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 |
…board-empty-slug-lookup
c24b86d to
b540dda
Compare
|
Bito Automatic Review Skipped – PR Already Merged |
…pache#39567) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pache#39567) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SUMMARY
get_dashboard_infosilently returnederror_type: not_foundwhen an agent passed a slug-like identifier (e.g."world-banks-data") for a dashboard whoseslugfield was empty. Many imported / example dashboards ship with empty slugs, so agents that guess the slug from the dashboard title (the natural thing to do) would hit a dead end.After the normal id / UUID / slug lookups miss,
ModelGetInfoCorenow scans dashboard titles, normalizes each title and the identifier via a shared_slugifyhelper (lowercases, drops apostrophes, collapses non-alphanumerics to hyphens), and returns the first match.If multiple titles slugify to the same value, it logs a warning and returns the first — collisions in real dashboards are rare and the
caller can always disambiguate by id or UUID.
The fallback column is opt-in per entity:
DashboardDAOdeclarestitle_column = "dashboard_title", whichModelGetInfoCorepicks up viagetattr(dao_class, "title_column", None).Other MCP tools can enable the same behavior by setting one attribute on their DAO — no core or tool changes required.
BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
N/A — behavior change is in the MCP service's JSON responses.
Before (master):
{ "error": "DashboardInfo with identifier 'world-banks-data' not found", "error_type": "not_found" }After:
{ "id": 1, "dashboard_title": "World Bank's Data", "slug": "", ... }TESTING INSTRUCTIONS
Find (or import) a dashboard whose slug field is empty but whose title slugifies to something predictable — e.g. the stock world_bank_data.py example ("World Bank's Data").
Call the MCP tool:
get_dashboard_info(request={"identifier": "world-banks-data"})
Expected: returns the dashboard (not error_type: not_found).
Sanity checks that should behave the same as before:
{"identifier": } — resolves by id.
{"identifier": ""} — resolves by UUID.
{"identifier": ""} — resolves by slug when the slug is non-empty.
{"identifier": "definitely-not-a-dashboard"} — still returns error_type: not_found (no over-matching).
Ambiguous case: create two dashboards whose titles slugify to the same value. Call get_dashboard_info with that slug. Expected: returns the first match and the server logs a warning naming all candidate ids.
Unit tests:
pytest tests/unit_tests/mcp_service/test_mcp_core.py \
tests/unit_tests/mcp_service/dashboard/
All 107 pass.
ADDITIONAL INFORMATION