aminghadersohi opened a new pull request, #45231:
URL: https://github.com/apache/superset/pull/45231
### SUMMARY
`MCP_DATASET_ROLE_ALLOWLIST` lets an operator narrow MCP's dataset-scoped
tools (`query_dataset`, `get_table`, etc.) to a curated set of registered
dataset UUIDs, keyed by role name. That mapping has to be a static dict in
`superset_config.py`, validated once at startup.
That works for a single-tenant install where roles and dataset UUIDs are
fixed. It can't express per-tenant policy for a deployment where one MCP
process serves many workspaces, each with its own roles and dataset UUIDs —
there's no single static dict that covers all of them.
This PR adds a second, optional form: `MCP_DATASET_ROLE_ALLOWLIST` can now
be a callable instead of a dict. The callable receives the current request's
user and returns the allowed dataset UUIDs (or `None`) for that request,
resolved on every call that needs routing enforcement rather than once at
startup. An operator can back it with whatever per-tenant lookup their
deployment needs (e.g. resolving the user's workspace and returning that
workspace's allowed dataset UUIDs).
The existing static-mapping form, its startup validation, and its role-union
semantics are unchanged.
**Fail-closed behavior:** the callable form fails closed — if it raises,
returns `None`, or returns something that can't be parsed as dataset UUIDs,
that request is denied every dataset. This is deliberately different from a
bare `None` *config* at the top level, which disables routing constraints
entirely: once an operator has opted into dynamic routing, a per-request
resolution failure (e.g. an unresolvable tenant) must never silently fall back
to the full, unrestricted tool surface.
Before:
```python
MCP_DATASET_ROLE_ALLOWLIST: dict[str, list[str]] | None = None
```
After, also accepts a callable:
```python
def resolve_dataset_scope(user):
workspace = lookup_workspace_for(user) # cheap, in-process lookup only
return workspace.allowed_dataset_uuids if workspace else None
MCP_DATASET_ROLE_ALLOWLIST = resolve_dataset_scope
```
### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
N/A — backend configuration only, no UI change.
### TESTING INSTRUCTIONS
- `pytest tests/unit_tests/mcp_service/test_dataset_scope.py` — adds
coverage for the callable form: per-request evaluation (called on every lookup,
not cached), UUID-object and string entries, an empty result denying all
datasets, and the fail-closed paths (raises, returns `None`, returns a
non-iterable, returns an unparsable UUID, returns a bare string).
- Existing dict-form tests are unchanged and still pass.
- `pre-commit run` (ruff, ruff-format, mypy, pylint) passes on the touched
files.
### 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
--
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]