MCP Service in Apache Superset#
Overview#
The Model Context Protocol (MCP) service (superset/mcp_service/) enables AI assistants (e.g., Claude) to programmatically interact with Apache Superset — creating dashboards, generating charts, executing SQL, and querying metadata. It is built on FastMCP and runs as a standalone sidecar process alongside the main Superset webserver.
The service is optional: fastmcp is listed only in requirements/development.in and as a pyproject.toml extra, so Superset runs normally without it and no database migrations are required.
Architecture#
The service lives entirely under superset/mcp_service/ and follows a domain-driven layout:
superset/mcp_service/
├── __main__.py # stdio-mode entry point
├── app.py # FastMCP factory (registers all tools on import)
├── server.py # HTTP/streamable-http server runner
├── mcp_config.py # Centralized config, reads from Flask app.config
├── flask_singleton.py # Module-level Flask app singleton
├── auth.py # JWT user resolution
├── middleware.py # Middleware stack (logging, errors, permissions, rate limit)
├── mcp_core.py # Reusable core classes
├── chart/ # 8 chart tools + validation pipeline + prompts + resources
├── dashboard/ # 5 dashboard tools
├── dataset/ # 3 dataset tools
├── sql_lab/ # 2 SQL Lab tools
├── explore/ # 1 explore tool
├── system/ # 2 system tools + prompts + resources
├── screenshot/ # Pooled Selenium WebDriver for chart previews
└── utils/ # cache, error builder, permissions, retry, URL utils
Key architectural patterns:
- All tools register via
@mcp.tool/@mcp.prompt/@mcp.resourcedecorators;app.pytriggers registration by importing each domain module. ModelListCore,ModelGetInfoCore, andModelGetAvailableFiltersCoreinmcp_core.pyprovide generic, reusable list/get/filter logic shared across all domains.- A 5-layer validation pipeline guards
generate_chart: schema (Pydantic) → business logic → dataset compatibility → Superset compatibility → runtime (cardinality, type suggestion). flask_singleton.pyholds a module-level Flask app instance so MCP tools can invoke Superset's SQLAlchemy models and FAB security manager without a running webserver.
Middleware stack (enforced on every tool call via mcp_auth_hook):
LoggingMiddleware— audit logs via Superset's event loggerGlobalErrorHandlerMiddleware— normalizes errors to LLM-friendly messagesFieldPermissionsMiddleware— filters response fields by JWT scopes (dashboard: published/certified/favorite; chart: certified/favorite; dataset: schema/table_name)RateLimitMiddleware— in-memory for dev, Redis for productionPrivateToolMiddleware— blocks tools tagged private
Tools, Prompts, and Resources#
21 tools across 6 domains :
| Domain | Tools |
|---|---|
| Chart | list_charts, get_chart_info, get_chart_available_filters, get_chart_data, get_chart_preview, generate_chart, update_chart, update_chart_preview |
| Dashboard | list_dashboards, get_dashboard_info, get_dashboard_available_filters, generate_dashboard, add_chart_to_existing_dashboard |
| Dataset | list_datasets, get_dataset_info, get_dataset_available_filters |
| SQL Lab | execute_sql (max 5 min / 10k rows), open_sql_lab_with_context |
| Explore | generate_explore_link |
| System | get_superset_instance_info, health_check |
2 prompts: superset_quickstart (onboarding), create_chart_guided (step-by-step chart wizard).
2 resources: superset://chart/templates (chart config patterns), superset://instance/metadata (capabilities and feature flags).
CLI & Running the Service#
The CLI entry point is superset/cli/mcp.py, registered as superset mcp run.
# HTTP mode (streamable-http transport), default port 5008
superset mcp run --host 0.0.0.0 --port 5008 --debug
# stdio mode (for Claude Desktop / local MCP clients)
python -m superset.mcp_service
Transport is selected via the FASTMCP_TRANSPORT environment variable (stdio or streamable-http). In stdio mode, __main__.py suppresses Flask initialization output to keep the MCP protocol stream clean.
Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"superset": {
"command": "python",
"args": ["-m", "superset.mcp_service"],
"env": { "SUPERSET_CONFIG_PATH": "/path/to/superset_config.py" }
}
}
}
Configuration#
All MCP settings are defined in superset/mcp_service/mcp_config.py and overridable via superset_config.py. Flask's app.config is checked first, so user overrides always take precedence.
| Key | Default | Purpose |
|---|---|---|
MCP_DEV_USERNAME | "admin" | Fallback user for all tool calls (dev/single-tenant) |
MCP_SERVICE_HOST | "localhost" | Bind address |
MCP_SERVICE_PORT | 5008 | Bind port |
MCP_AUTH_ENABLED | False | Enable JWT authentication |
MCP_JWT_PUBLIC_KEY | — | RS256 public key for token verification |
MCP_JWT_ALGORITHM | "RS256" | JWT signing algorithm |
SUPERSET_WEBSERVER_ADDRESS | "http://localhost:9001" | Used for URL generation |
WEBDRIVER_BASEURL | "http://localhost:9001/" | For screenshot generation |
Authentication & Known Limitations#
Design intent: auth.py extracts the calling user from a JWT Bearer token and sets g.user for the Flask context. The mcp_auth_hook decorator wraps every tool to enforce this.
Current limitation (as of 6.1.0): MCP routes run under a FastMCP ASGI sub-app mounted on Flask. Flask's @before_request JWT middleware only fires for routes in Flask's own URL map, so /mcp/* requests bypass it entirely — g.user is never populated from the token.
Workaround: Set MCP_DEV_USERNAME=<username> in superset_config.py. All tool calls execute as that single delegated account. Per-request access-token authentication is planned for a future release.
⚠️
MCP_DEV_USERNAMEcollapses row-level security, dataset permissions, and audit trails to a single principal. Use a dedicated FAB role with minimum necessary grants.
Patching auth.py and mcp_config.py from the master branch is a documented workaround for Keycloak/SSO setups on 6.1.0.
Deployment#
Standalone process: Run superset mcp run --host 0.0.0.0 --port 5008 in a separate container or process. The /health HTTP endpoint works for liveness and readiness probes.
Kubernetes/Helm: Native Helm chart support is a community feature request (not yet merged). The proposed supersetMcp block mirrors the existing supersetWebsockets and supersetCeleryFlower patterns.
DevContainer: .devcontainer/with-mcp/devcontainer.json extends the base devcontainer with fastmcp, Selenium, and Chrome for local development including screenshot generation.
Key Source Files#
| File | Purpose |
|---|---|
superset/cli/mcp.py | superset mcp run CLI command |
superset/mcp_service/app.py | FastMCP factory, tool/prompt/resource registration |
superset/mcp_service/mcp_config.py | Configuration keys and defaults |
superset/mcp_service/auth.py | JWT user resolution and mcp_auth_hook decorator |
superset/mcp_service/middleware.py | Full middleware stack |
superset/mcp_service/mcp_core.py | ModelListCore, ModelGetInfoCore, ModelGetAvailableFiltersCore |
superset/mcp_service/chart/validation/pipeline.py | 5-layer chart validation pipeline |
superset/mcp_service/screenshot/webdriver_pool.py | Pooled Selenium WebDriver for chart previews |
superset/mcp_service/README.md | Quickstart guide |
Open Issues & Future Work#
- Per-request JWT auth: Not yet in 6.1.0; requires ASGI auth middleware on the FastMCP mount or a Flask→ASGI bridge.
- Granular caching controls:
MCP_CACHE_CONFIGcurrently exposes only TTL andexcluded_tools. Per-operationenabled/included_tools/max_item_sizecontrols are tracked in Discussion #42198. - Helm chart support: First-class Kubernetes deployment templates tracked in Discussion #41441.