aminghadersohi opened a new pull request, #44694:
URL: https://github.com/apache/superset/pull/44694
### SUMMARY
Adds `get_catalog`, a read-only MCP tool. It returns a small, paged catalog
of the databases, datasets, charts, or dashboards the **calling user** can see.
Assistants often need an inventory ("what dashboards are there?") without
calling the full list tools again and again. The list tools also return heavy
fields for this purpose, such as dataset SQL, `extra`, and metrics.
**Contract**
- Request (wrapped in `request`, like other tools): `asset_type` ∈
{`databases`, `datasets`, `charts`, `dashboards`}; `page_size` (default 50, max
100); `cursor` (opaque, optional); `search` (case-insensitive name substring,
optional).
- Response: `{asset_type, items: [{id, uuid, name, description, changed_on,
url}], next_cursor, restricted, truncated, message}`.
**Design and security properties**
- **Same visibility as the list tools.** Rows are read through `DAO.list`,
which always applies the model's `base_filter` (`DatabaseFilter`,
`DatasourceFilter`, `ChartFilter`, `DashboardAccessFilter`). Soft-deleted rows
are excluded by the global listener.
- **Per-asset-type RBAC.** Each asset type checks `can_read` on its class
(`Database`, `Dataset`, `Chart`, `Dashboard`) plus token scopes, following the
`get_schema` pattern. A denial raises the generic permission error.
- **`privacy.py` honored.** Users without data-model metadata access get
`restricted: true` with no items for `databases` and `datasets`, rather than an
error. Charts and dashboards still list, because they expose no data-model
fields.
- **Strict field allowlist.** Only `id`, `uuid`, the name column,
`description`, `changed_on`, and `slug` (dashboards, used for the link) are
selected from the database. The response models use `extra="forbid"`. SQL,
`extra`, template params, metric expressions, connection info, owners, and row
data are never loaded or returned.
- **Live, per call, no caching.** Nothing is stored. The tool is added to a
new `ALWAYS_EXCLUDED_TOOLS` set that `_build_caching_settings` always merges
into the response-cache exclusions, even if an operator enables the shared
response cache. A revoked grant therefore takes effect on the next call.
- **Bounded output.**
- `page_size` must be at most 100 (a validation error otherwise).
- Names are capped at 256 characters and descriptions at 500.
- Each response is kept at or under 32 KiB of JSON by moving trailing
items to the next page; `truncated` reports this.
- **Stable pagination.** `next_cursor` is an opaque keyset cursor (last seen
id). It is tied to the asset type and search term, and a mismatched or
malformed cursor is rejected. Pages stay complete and free of duplicates when
assets are added or removed between reads. The cursor grants no access: every
page is filtered again for the current caller.
- **Dataset scope.** In deployments that configure
`MCP_DATASET_ROLE_ALLOWLIST`, the tool is not in the dataset-scoped tool list,
so it is refused. This fails closed.
Chart→dataset relationships are intentionally left out, so no possibly stale
relationship data is returned.
### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
N/A (new MCP tool).
### TESTING INSTRUCTIONS
```bash
pytest tests/unit_tests/mcp_service/catalog
pytest tests/unit_tests/mcp_service
```
`tests/unit_tests/mcp_service/catalog/tool/test_get_catalog.py` has 40
tests. They run the real DAO base filters on in-memory SQLite, with RBAC
enabled, and simulate roles at the security-manager grant lookups. Coverage:
- **Roles:**
- Admin sees every asset.
- A Gamma-like user with a datasource-level grant on one table sees only
that table's dataset, charts, and dashboards.
- A user without data-model access gets a `restricted` page for databases
and datasets, and still gets filtered charts and dashboards.
- A missing class permission is denied.
- **Revocation:** a grant removed between two calls disappears on the second
call, for every asset type. Losing data-model access flips the page to
`restricted`.
- **Cross-user isolation:** an admin read never affects a following
restricted read. A cursor issued to an admin gives another user nothing extra.
- **Allowlist:** SQL, `extra`, template params, metric expressions, chart
params, dashboard JSON, and the SQLAlchemy URI are planted with marker values,
and none of them appear in the output. Item keys are a subset of the allowlist,
and unknown request or response fields are rejected.
- **Bounds:**
- `page_size` values of 0, 101, and 1000 are rejected, including over MCP.
- 150 datasets with 5,000-character descriptions stay at or under 32 KiB
per page, with none lost or duplicated.
- Cursor pages over 250+ rows stay complete and in order when a row is
deleted and another added mid-pagination.
- Tampered or mismatched cursors are rejected.
Mutation checks were run locally. Each of the following makes the
corresponding tests fail:
- bypassing the DAO base filter
- adding a per-process page cache
- dropping the privacy check
- dropping the RBAC check
- dropping the byte bound
The tool inventory budget, registration metadata, and caching tests are
updated.
### ADDITIONAL INFORMATION
- [ ] Has associated issue:
- [ ] Required feature flags:
- [ ] Changes UI
- [ ] Includes DB Migration (follow approval process in
[SIP-59](https://github.com/apache/superset/issues/13351))
- [ ] Migration is atomic, supports rollback & is backwards-compatible
- [ ] Confirm DB migration upgrade and downgrade tested
- [ ] Runtime estimates and downtime expectations provided
- [x] Introduces new feature or API
- [ ] Removes existing feature or API
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]