CLAUDE.md
superset/mcp_service/CLAUDE.mdCLAUDE.md
Quality
64/100
Scores the file, not the repository.Length
2,906 words
79 headings · 20 code blocksRepository
74k
— · pushed 0 days agoLast changed
2 days ago
First indexed 2 days ago.1# MCP Service - LLM Agent Guide23This guide helps LLM agents understand the Superset MCP (Model Context Protocol) service architecture and development conventions.45## CRITICAL: Apache License Headers67**EVERY Python file in the MCP service MUST have the Apache Software Foundation license header.**89This includes:10- All `.py` files (tool files, schemas, __init__.py files, etc.)11- **NEVER remove existing license headers during refactoring or edits**12- **ALWAYS add license headers when creating new files**13- **ALWAYS verify license headers are present after editing files**1415If you see a file without a license header, ADD IT IMMEDIATELY. If you accidentally remove one during editing, ADD IT BACK.1617Use this exact template at the top of EVERY Python file:1819```python20# Licensed to the Apache Software Foundation (ASF) under one21# or more contributor license agreements. See the NOTICE file22# distributed with this work for additional information23# regarding copyright ownership. The ASF licenses this file24# to you under the Apache License, Version 2.0 (the25# "License"); you may not use this file except in compliance26# with the License. You may obtain a copy of the License at27#28# http://www.apache.org/licenses/LICENSE-2.029#30# Unless required by applicable law or agreed to in writing,31# software distributed under the License is distributed on an32# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY33# KIND, either express or implied. See the License for the34# specific language governing permissions and limitations35# under the License.36```3738**Note**: LLM instruction files like `CLAUDE.md`, `AGENTS.md`, etc. are excluded from this requirement (listed in `.rat-excludes`) to avoid token overhead, but ALL other Python files require it.3940## Architecture Overview4142The MCP service provides programmatic access to Superset via the Model Context Protocol, allowing AI assistants to interact with dashboards, charts, datasets, databases, SQL Lab, and instance metadata.4344### Key Components4546```47superset/mcp_service/48├── app.py # FastMCP app factory and tool registration49├── auth.py # Authentication, authorization, and RBAC50├── mcp_config.py # Default configuration51├── mcp_core.py # Reusable core classes for tools52├── flask_singleton.py # Flask app singleton for MCP context53├── middleware.py # FastMCP middleware (logging, errors, size guards)54├── server.py # Server startup (streamable-http, multi-pod)55├── jwt_verifier.py # JWT token validation56├── chart/ # Chart tools, schemas, prompts, resources57│ ├── schemas.py58│ ├── chart_utils.py59│ ├── preview_utils.py60│ ├── validation.py61│ ├── tool/62│ ├── prompts/63│ └── resources/64├── dashboard/ # Dashboard tools and schemas65│ ├── schemas.py66│ └── tool/67├── dataset/ # Dataset tools and schemas68│ ├── schemas.py69│ └── tool/70├── explore/ # Explore link generation71│ ├── schemas.py72│ └── tool/73├── sql_lab/ # SQL Lab tools (execute, save, open)74│ ├── schemas.py75│ └── tool/76├── system/ # System tools (health, instance info, schema)77│ ├── schemas.py78│ ├── tool/79│ ├── prompts/80│ └── resources/81├── common/ # Shared error schemas82├── commands/ # MCP-specific command classes83└── utils/ # Utilities (URL, schema parsing, error builders)84```8586### Dependency Injection Architecture8788The `@tool` and `@prompt` decorators are defined as stubs in the `superset-core` package (`superset_core.mcp.decorators`). At startup, `app.py` calls `initialize_core_mcp_dependencies()` which replaces these stubs with concrete implementations that register tools/prompts with the FastMCP instance. This avoids circular imports between `superset_core` and `superset`.8990**Startup flow**:911. `app.py` creates the FastMCP `mcp` instance922. `initialize_core_mcp_dependencies()` injects the real decorator implementations933. Tool/prompt/resource imports at the bottom of `app.py` trigger registration944. `server.py` adds middleware and starts the transport9596## Critical Convention: Tool, Prompt, and Resource Registration9798**IMPORTANT**: When creating new MCP tools, prompts, or resources, you MUST add their imports to `app.py` for auto-registration. Do NOT add them to `server.py` - that approach doesn't work properly.99100### How to Add a New Tool1011021. **Create the tool file** in the appropriate directory (e.g., `chart/tool/my_new_tool.py`)1032. **Decorate with `@tool`** using the decorator from `superset_core.mcp.decorators`1043. **Export from the module's `__init__.py`** (e.g., `chart/tool/__init__.py`)1054. **Add import to `app.py`** at the bottom of the file where other tools are imported106107**Example (read-only tool)**:108```python109# superset/mcp_service/chart/tool/my_new_tool.py110from fastmcp import Context111from superset_core.mcp.decorators import tool, ToolAnnotations112113from superset.extensions import event_logger114115@tool(116 tags=["core"],117 class_permission_name="Chart",118 annotations=ToolAnnotations(119 title="My new tool",120 readOnlyHint=True,121 destructiveHint=False,122 ),123)124async def my_new_tool(request: MyRequest, ctx: Context) -> MyResponse:125 """Tool description for LLMs."""126 await ctx.info("Doing something: param=%s" % (request.param,))127 with event_logger.log_context(action="mcp.my_new_tool"):128 result = do_something()129 return MyResponse(data=result)130```131132**Example (mutating tool)**:133```python134@tool(135 tags=["mutate"],136 class_permission_name="Chart",137 method_permission_name="write",138 annotations=ToolAnnotations(139 title="Create something",140 readOnlyHint=False,141 destructiveHint=False,142 ),143)144async def create_something(request: CreateRequest, ctx: Context) -> CreateResponse:145 """Creates a new resource."""146 ...147```148149**Then add to app.py**:150```python151# superset/mcp_service/app.py (at the bottom, after initialize_core_mcp_dependencies())152from superset.mcp_service.chart.tool import ( # noqa: F401, E402153 get_chart_info,154 list_charts,155 my_new_tool, # ADD YOUR TOOL HERE156)157```158159**Why this matters**: Tools register automatically on import via the `@tool` decorator. The import MUST be in `app.py` at the bottom (after `initialize_core_mcp_dependencies()` is called). DO NOT add imports to `server.py`.160161### How to Add a New Prompt1621631. **Create the prompt file** in the appropriate directory (e.g., `chart/prompts/my_new_prompt.py`)1642. **Decorate with `@prompt`** from `superset_core.mcp.decorators`1653. **Add import to module's `__init__.py`** (e.g., `chart/prompts/__init__.py`)1664. **Ensure module is imported in `app.py`**167168**Example**:169```python170# superset/mcp_service/chart/prompts/my_new_prompt.py171from superset_core.mcp.decorators import prompt172173@prompt("my_new_prompt")174async def my_new_prompt_handler(175 chart_type: str = "auto", business_goal: str = "exploration"176) -> str:177 """Interactive prompt for doing something."""178 return "Prompt instructions here..."179```180181### How to Add a New Resource182183Resources use direct FastMCP decorators and **must include `@mcp_auth_hook`** for authentication:184185```python186# superset/mcp_service/chart/resources/my_new_resource.py187from superset.mcp_service.app import mcp188from superset.mcp_service.auth import mcp_auth_hook # REQUIRED for resources189190@mcp.resource("superset://chart/my_resource")191@mcp_auth_hook # Always add this decorator to resources192def get_my_resource() -> str:193 """Resource description for LLMs."""194 return "Resource data here..."195```196197## Tool Development Patterns198199### 1. Tool Decorator Parameters200201The `@tool` decorator from `superset_core.mcp.decorators` accepts:202203- **`tags`**: List of tags (e.g., `["core"]`, `["mutate"]`). Default: `[]`204- **`class_permission_name`**: FAB permission class (e.g., `"Chart"`, `"Dashboard"`). Default: `None`205- **`method_permission_name`**: Permission action (e.g., `"read"`, `"write"`). Default: Auto — `"write"` if `"mutate"` in tags, else `"read"`206- **`protect`**: Enable authentication wrapping. Default: `True`207- **`annotations`**: MCP `ToolAnnotations` object. Default: `None`208209**ToolAnnotations** (from `superset_core.mcp.decorators`):210```python211annotations=ToolAnnotations(212 title="Human-readable title",213 readOnlyHint=True, # Whether tool only reads data214 destructiveHint=False, # Whether tool has destructive side effects215)216```217218### 2. Use Core Classes for Reusability219220The `mcp_core.py` module provides reusable patterns:221222- **`ModelListCore`**: For listing resources with filtering, search, and pagination223 - Used by: `list_charts`, `list_dashboards`, `list_datasets`, `list_databases`224- **`ModelGetInfoCore`**: For getting resource details by ID, UUID, or slug225 - Used by: `get_chart_info`, `get_dashboard_info`, `get_dataset_info`, `get_database_info`226- **`ModelGetSchemaCore`**: For schema discovery (columns, filters, sortable columns)227 - Used by: `get_schema`228- **`InstanceInfoCore`**: For instance statistics and metadata229 - Used by: `get_instance_info`230231### 3. Authentication and RBAC232233Authentication is handled automatically by the `@tool` decorator (via `mcp_auth_hook` internally). RBAC permission checking uses `class_permission_name` and `method_permission_name`.234235```python236from superset_core.mcp.decorators import tool, ToolAnnotations237238# Authentication + RBAC enabled (default)239@tool(240 class_permission_name="Chart", # Checks user has Chart access241)242async def my_tool(request: MyRequest, ctx: Context) -> MyResponse:243 # g.user is set automatically before this runs244 ...245246# Public tool (no auth) - use sparingly, and add the tool name to247# ALLOWED_UNPROTECTED in app.py (e.g. generate_bug_report)248@tool(protect=False)249async def public_status(ctx: Context) -> dict:250 return {"status": "healthy"}251```252253Note: `health_check` is a protected, authenticated tool (`@tool(tags=["core"], ...)`,254no `protect=False`) — it is not an example of a public tool.255256**Authentication priority order** (in `auth.py`):2571. JWT context (per-request ContextVar from FastMCP). Also resolves a verified258 embedded **guest token** to a `GuestUser` when `MCP_EMBEDDED_GUEST_AUTH_ENABLED`259 + `EMBEDDED_SUPERSET` are on (a guest is never downgraded to a lower priority).2602. API Key authentication (via FAB SecurityManager)2613. `MCP_DEV_USERNAME` config (development only)2624. `g.user` fallback (set by external middleware)263264Guest tokens are verified by `GuestTokenVerifier` (in the `CompositeTokenVerifier`,265before the JWT verifier) using the shared core `GUEST_TOKEN_JWT_*` config, then266built into a `GuestUser` in `_resolve_user_from_jwt_context`. See `SECURITY.md`.267268**`@mcp_auth_hook`** is only used directly on **resources** — tools get auth wrapping from `@tool(protect=True)`.269270### 4. Use Pydantic Schemas271272**All tool inputs and outputs must be Pydantic models**. Place schemas in `{module}/schemas.py`.273274```python275from pydantic import BaseModel, ConfigDict, Field276277class MyToolRequest(BaseModel):278 model_config = ConfigDict(populate_by_name=True)279280 param: str = Field(..., description="Parameter description for LLMs")281 optional_param: str | None = Field(None, description="Optional parameter")282283class MyToolResponse(BaseModel):284 result: str = Field(..., description="Result description")285 error: str | None = Field(None, description="Error message if failed")286```287288### 5. Follow the DAO Pattern289290**Use Superset's DAO (Data Access Object) layer** instead of direct database queries:291292```python293from superset.daos.dashboard import DashboardDAO294295# GOOD: Use DAO296dashboard = DashboardDAO.find_by_id(dashboard_id)297298# BAD: Don't query directly299dashboard = db.session.query(Dashboard).filter_by(id=dashboard_id).first()300```301302### 6. Python Type Hints (Python 3.10+ Style)303304**CRITICAL**: Always use modern Python 3.10+ union syntax for type hints.305306```python307# GOOD - Modern Python 3.10+ syntax308from typing import Any309310from pydantic import BaseModel, Field311312class MySchema(BaseModel):313 name: str | None = Field(None, description="Optional name")314 tags: list[str] = Field(default_factory=list)315 metadata: dict[str, Any] = Field(default_factory=dict)316317def my_function(318 id: int,319 filters: list[str] | None = None,320) -> MySchema | None:321 pass322323# BAD - Old-style (DO NOT USE)324from typing import Optional, List, Dict325name: Optional[str] # Wrong! Use str | None326tags: List[str] # Wrong! Use list[str]327```328329### 7. Event Logger Instrumentation330331**All tool operations should use `event_logger`** for observability:332333```python334from superset.extensions import event_logger335336@tool(...)337async def my_tool(request: MyRequest, ctx: Context) -> MyResponse:338 with event_logger.log_context(action="mcp.my_tool.step_name"):339 result = do_something()340 return MyResponse(data=result)341```342343### 8. Context Logging344345Use the FastMCP `Context` object for structured logging within tools:346347```python348async def my_tool(request: MyRequest, ctx: Context) -> MyResponse:349 await ctx.info("Starting: param=%s" % (request.param,))350 await ctx.debug("Details: keys=%s" % (sorted(request.model_dump().keys()),))351 await ctx.warning("Something unexpected: %s" % (warning_msg,))352 await ctx.error("Failed: %s" % (str(exc),))353 await ctx.report_progress(1, 5, "Step 1 of 5")354```355356### 9. Error Handling357358**Pattern**: Catch specific exceptions for known failure modes, use broad `Exception` only as the outermost safety net that re-raises:359360```python361from superset.commands.dataset.exceptions import DatasetInvalidError, DatasetCreateFailedError362363@tool(...)364async def my_tool(request: MyRequest, ctx: Context) -> MyResponse:365 try:366 # Specific exception handling for known failure modes367 with event_logger.log_context(action="mcp.my_tool"):368 result = SomeCommand(properties).run()369 return MyResponse(data=result)370371 except DatasetInvalidError as exc:372 # Return structured error response (don't raise)373 await ctx.error("Validation failed: %s" % (exc.normalized_messages(),))374 return MyResponse(error=str(exc.normalized_messages()))375376 except DatasetCreateFailedError as exc:377 await ctx.error("Creation failed: %s" % (str(exc),))378 return MyResponse(error=f"Failed: {exc}")379380 except Exception as exc:381 # Outermost safety net: log and re-raise (middleware handles it)382 await ctx.error("Unexpected: %s: %s" % (type(exc).__name__, str(exc)))383 raise384```385386### 10. Dataset Validation for Chart Tools387388All chart-related tools must validate that the chart's dataset is accessible:389390```python391from superset.mcp_service.chart.chart_utils import validate_chart_dataset392393validation_result = validate_chart_dataset(chart, check_access=True)394if not validation_result.is_valid:395 await ctx.warning("Dataset not accessible: %s" % (validation_result.error,))396 return ChartError(397 error=validation_result.error or "Chart's dataset is not accessible",398 error_type="DatasetNotAccessible",399 )400```401402Used by: `get_chart_info`, `get_chart_preview`, `get_chart_data`, `generate_chart`403404### 11. Compile Check for Chart Creation405406When creating, saving, or previewing charts, run schema validation (Tier 1)407and optionally a compile check (Tier 2) before persisting or caching.408``validate_and_compile`` glues both together; tools with tight SLAs409(``generate_explore_link``, ``update_chart_preview``) opt out of Tier 2.410411```python412from superset.mcp_service.chart.compile import validate_and_compile413414result = validate_and_compile(415 config, form_data, dataset, run_compile_check=True416)417if not result.success:418 # ``result.error_obj`` is a ``ChartGenerationError`` with fuzzy-match419 # suggestions ("did you mean sum_boys?") so the LLM can self-correct.420 ...421```422423The lower-level ``_compile_chart(form_data, dataset_id)`` is still exported424for callers that have already done their own schema validation.425426### 12. Flexible Input Parsing427428`ModelListCore` handles JSON string vs. native object parsing automatically via utilities in `superset.mcp_service.utils.schema_utils`:429430- `parse_json_or_passthrough(value, param_name)` - JSON string or dict431- `parse_json_or_list(value, param_name)` - JSON array, list, or comma-separated string432- `parse_json_or_model(value, model_class, param_name)` - JSON string or dict to Pydantic model433- `parse_json_or_model_list(value, model_class, param_name)` - JSON array to list of Pydantic models434435These are used internally by `ModelListCore` for `filters` and `select_columns`. Individual tools using core classes do NOT need to add parsing logic.436437## Middleware438439The MCP service uses FastMCP middleware (registered in `server.py`):440441- **`LoggingMiddleware`**: Logs tool calls with duration, entity IDs, sanitizes sensitive data442- **`GlobalErrorHandlerMiddleware`**: Catches unhandled exceptions, converts to ToolError443- **`StructuredContentStripperMiddleware`**: Strips structuredContent from responses (Claude.ai compatibility)444- **`ResponseSizeGuardMiddleware`**: Prevents oversized responses from crashing clients445- **`ResponseCachingMiddleware`**: Optional response caching (in-memory by default, Redis when store enabled)446447Middleware is applied in `server.py` and should NOT be modified in individual tools.448449## Configuration450451Default configuration is in `mcp_config.py`. Override in `superset_config.py`:452453```python454# Authentication455MCP_DEV_USERNAME = None # Fallback username for dev mode456MCP_AUTH_ENABLED = False # Enable JWT/API key auth457MCP_AUTH_FACTORY = None # Custom auth factory function458MCP_JWT_PUBLIC_KEY = None459MCP_JWT_SECRET = None460MCP_JWKS_URI = None461MCP_USER_RESOLVER = None # Custom function to extract username from JWT462463# RBAC464MCP_RBAC_ENABLED = True # Enable permission checking (default: True)465466# Embedded guest auth (opt-in; requires the EMBEDDED_SUPERSET feature flag).467# Reuses core GUEST_TOKEN_JWT_* config — no MCP-specific guest secret/audience.468MCP_EMBEDDED_GUEST_AUTH_ENABLED = False469# Default-deny: the ONLY tools a guest may call (everything else is denied).470MCP_GUEST_ALLOWED_TOOLS = {471 "get_dashboard_info", "get_dashboard_layout", "list_dashboards",472 "list_charts", "get_chart_info", "get_chart_data", "get_chart_preview",473}474# Principal-agnostic extension point: given the current user, return an allow-list475# (only these tools are callable) or None if unrestricted. Defaults to restricting476# embedded guests to MCP_GUEST_ALLOWED_TOOLS; override to add other restricted477# principals without touching the enforcement path.478MCP_RESTRICTED_TOOL_POLICY = None # Callable[[user], frozenset[str] | None]479480481# Response Caching (optional, uses in-memory store by default; Redis when MCP_STORE_CONFIG enabled)482MCP_CACHE_CONFIG = {483 "enabled": False,484 "list_tools_ttl": 300,485 "call_tool_ttl": 3600,486 "excluded_tools": ["execute_sql", "generate_dashboard"], # add tools to exclude487}488489# Multi-pod Storage (optional, requires Redis)490MCP_STORE_CONFIG = {491 "enabled": False,492 "CACHE_REDIS_URL": None,493 "event_store_ttl": 3600,494}495```496497## Testing Conventions498499### Test Organization500501Tests mirror the MCP service module structure:502```503tests/unit_tests/mcp_service/504├── conftest.py # Global fixtures (disable_mcp_rbac)505├── chart/506│ ├── test_chart_utils.py507│ ├── test_chart_schemas.py508│ └── tool/509│ ├── test_list_charts.py510│ ├── test_generate_chart.py511│ └── ...512├── dashboard/tool/513├── dataset/tool/514├── sql_lab/tool/515├── system/tool/516├── test_auth_*.py # Auth/RBAC tests517└── test_middleware*.py # Middleware tests518```519520### Async Tool Tests (primary pattern)521522```python523from unittest.mock import MagicMock, patch524import pytest525from fastmcp import Client526527from superset.mcp_service.app import mcp528from superset.utils import json529530@pytest.fixture531def mcp_server():532 return mcp533534@pytest.mark.asyncio535async def test_my_tool_success(mcp_server):536 mock_obj = MagicMock()537 mock_obj.id = 1538 mock_obj.name = "test"539540 with patch("superset.daos.chart.ChartDAO.find_by_id", return_value=mock_obj):541 async with Client(mcp_server) as client:542 result = await client.call_tool(543 "my_tool", {"request": {"id": 1}}544 )545 data = json.loads(result.content[0].text)546547 assert data["id"] == 1548```549550### Key Testing Patterns551552- **RBAC is disabled globally** via `conftest.py` autouse fixture (`MCP_RBAC_ENABLED = False`)553- **RBAC tests** are separate in `test_auth_rbac.py` with their own `enable_mcp_rbac` fixture554- **Auth is mocked** via `mock_auth` fixture that patches `get_user_from_request`555- **Mock objects** must have all attributes set explicitly (no auto-generation)556- **Patch at the DAO level**: `patch("superset.daos.chart.ChartDAO.find_by_id", ...)`557- **Schema validation tests** are synchronous (no Client needed)558559## Common Pitfalls to Avoid560561### 1. Forgetting Tool Import in app.py562**Problem**: Tool exists but isn't available to MCP clients.563**Solution**: Add tool import to `app.py` at the bottom (after `initialize_core_mcp_dependencies()`).564565### 2. Adding Tool Imports to server.py566**Problem**: Tools won't register properly.567**Solution**: Tool imports MUST be in `app.py`, not `server.py`.568569### 3. Wrong Decorator Import Path570**Problem**: Using stale import path.571**Solution**: Use `from superset_core.mcp.decorators import tool, ToolAnnotations` (NOT `superset_core.api.mcp`).572573### 4. Missing ToolAnnotations574**Problem**: Tool lacks MCP directory compliance metadata.575**Solution**: Always include `annotations=ToolAnnotations(title=..., readOnlyHint=..., destructiveHint=...)`.576577### 5. Using `Optional` Instead of Union Syntax578**Problem**: Old-style `Optional[T]` is not Python 3.10+ style.579**Solution**: Use `T | None` and `list[str]` instead of `Optional[T]` and `List[str]`.580581### 6. Direct Database Queries582**Problem**: Bypasses Superset's security and caching layers.583**Solution**: Use DAO classes (ChartDAO, DashboardDAO, DatasetDAO, DatabaseDAO).584585### 7. Not Using Core Classes586**Problem**: Duplicating list/get_info logic across tools.587**Solution**: Use `ModelListCore`, `ModelGetInfoCore`, `ModelGetSchemaCore`.588589### 8. Missing Apache License Headers590**Problem**: CI fails on license check.591**Solution**: Add ASF license header to all new `.py` files (see template at top of this doc).592593### 9. Circular Imports594**Problem**: Importing from `app.py` in tool files causes circular dependencies.595**Solution**: Use `from superset_core.mcp.decorators import tool` for tools/prompts. Only import `from superset.mcp_service.app import mcp` in resource files.596597### 10. Missing event_logger Instrumentation598**Problem**: Tool operations are invisible to observability.599**Solution**: Wrap key operations with `event_logger.log_context(action="mcp.tool_name.step")`.600601## Quick Checklist for New Tools602603- [ ] Created tool file in `{module}/tool/{tool_name}.py`604- [ ] Added ASF license header605- [ ] Used `@tool(tags=[...], class_permission_name="...", annotations=ToolAnnotations(...))` decorator606- [ ] Import: `from superset_core.mcp.decorators import tool, ToolAnnotations`607- [ ] Created Pydantic request/response schemas in `{module}/schemas.py`608- [ ] Used DAO classes instead of direct queries609- [ ] Added `event_logger.log_context()` instrumentation610- [ ] Used `await ctx.info/error/debug()` for context logging611- [ ] Exported from `{module}/tool/__init__.py`612- [ ] Added tool import to `app.py` at the bottom613- [ ] Created async unit tests in `tests/unit_tests/mcp_service/{module}/tool/`614- [ ] Updated `DEFAULT_INSTRUCTIONS` in `app.py` if adding new capability615616## Quick Checklist for New Prompts617618- [ ] Created prompt file in `{module}/prompts/{prompt_name}.py`619- [ ] Added ASF license header620- [ ] Used `@prompt("prompt_name")` from `superset_core.mcp.decorators`621- [ ] Made function async: `async def prompt_handler(...) -> str`622- [ ] Added import to `{module}/prompts/__init__.py`623- [ ] Verified module import exists in `app.py`624625## Quick Checklist for New Resources626627- [ ] Created resource file in `{module}/resources/{resource_name}.py`628- [ ] Added ASF license header629- [ ] Used `@mcp.resource("superset://{path}")` decorator630- [ ] Added `@mcp_auth_hook` decorator631- [ ] Added import to `{module}/resources/__init__.py`632- [ ] Verified module import exists in `app.py`633634## Getting Help635636- Check existing tool implementations for patterns (chart/tool/, dashboard/tool/)637- Review core classes in `mcp_core.py` for reusable functionality638- See `CLAUDE.md` in project root for general Superset development guidelines639- Consult Superset documentation: https://superset.apache.org/docs/640
Also in apache/superset
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| apache/supersetAGENTS.md · 74k | AGENTS.md | setuptestlint-formatstyle+9 | 89/100 | today | |
| apache/superset.cursor/rules/dev-standard.mdc · 74k | Cursor rules | setuptestlint-formatstyle+7 | 77/100 | today |
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| microsoft/playwrightCLAUDE.md · 94k | CLAUDE.md | buildtestlint-formatstyle+7 | 100/100 | 3 days ago | |
| Adit-Jain-srm/NightmareNetCLAUDE.md · 45 | CLAUDE.md | buildtestlint-formatstyle+6 | 100/100 | 3 days ago | |
| nimbalyst/nimbalystpackages/android/CLAUDE.md · 1.4k | CLAUDE.md | setupbuildstylearch+2 | 100/100 | 3 days ago | |
| dotCMS/corecore-web/CLAUDE.md · 949 | CLAUDE.md | teststylearchtesting-strategy+3 | 100/100 | 3 days ago | |
| dotCMS/coreCLAUDE.md · 949 | CLAUDE.md | setupbuildteststyle+7 | 99/100 | today | |
| khrnchn/sedekah-jeCLAUDE.md · 89 | CLAUDE.md | testlint-formatstylearch+6 | 97/100 | 3 days ago | |
| luongnv89/claude-howtovi/CLAUDE.md · 41k | CLAUDE.md | setupbuildtestlint-format+8 | 97/100 | 3 days ago | |
| caliber-ai-org/ai-setupCLAUDE.md · 1.2k | CLAUDE.md | buildtestlint-formatstyle+1 | 97/100 | 3 days ago |
