-
Notifications
You must be signed in to change notification settings - Fork 3
new skill: manage-boards #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
58b0c50
new skill: manage-boards
naitik-mixpanel 04c8d95
minor change to skill name in skill.md file
naitik-mixpanel 8908510
changes to skill.md file
naitik-mixpanel 907438d
change to cleanup-dashboards.md files
naitik-mixpanel ddaa405
Merge branch 'main' into manage-board
naitik-mixpanel f4af59a
changes as per review-skill
naitik-mixpanel d99ab9b
# PR #27 — new skill: manage-boards
naitik-mixpanel 89de9e4
Merge branch 'main' into manage-board
naitik-mixpanel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| --- | ||
| name: manage-boards | ||
| description: > | ||
| Full lifecycle dashboard management for Mixpanel — create, template, clone, clean up, | ||
| inventory, and update dashboards across projects and teams. ALWAYS use when a user asks | ||
| to: create a dashboard, build a board, template or clone a dashboard, clean up stale | ||
| dashboards, audit dashboards, list all dashboards, inventory boards, update or rename a | ||
| dashboard, delete a dashboard, or standardize boards across projects. Also trigger on | ||
| "duplicate board", "stale dashboards", "empty boards", "board management", or when | ||
| dashboards are mentioned with "governance", "cleanup", "template", "onboarding", or | ||
| "standardize". Do NOT use for A/B experiments (use `manage-experiment`), feature-flag | ||
| rollouts (use `manage-feature-flags`), or Lexicon/event-and-property metadata cleanup (use | ||
| `manage-lexicon`). Requires Mixpanel MCP. | ||
| compatibility: "Requires Mixpanel MCP (mcp.mixpanel.com or regional variants). No other connectors required." | ||
| --- | ||
|
|
||
| # Manage Boards | ||
|
|
||
| Top-level router: validate project → route command → handle return. | ||
|
|
||
| This skill uses the **Mixpanel MCP only**. No other connectors are required or referenced. | ||
|
|
||
| --- | ||
|
|
||
| ## Execution Philosophy | ||
|
|
||
| **Silent execution.** Do not narrate steps, announce phases, or explain what you are about to do. Run the work, process results, present only final output. The user sees: | ||
| - Command menu (when needed) | ||
| - Confirmation prompts before destructive operations (delete, bulk cleanup) | ||
| - Progress indicators during batch operations | ||
| - Error messages | ||
| - Final output | ||
|
|
||
| Nothing else. No "Starting X...", no "I'll now fetch Y...", no "Let me analyze Z...". Just do the work and show the result. | ||
|
|
||
| --- | ||
|
|
||
| ## Global Rules | ||
|
|
||
| 1. **Silent execution** — see the Execution Philosophy section above. | ||
| 2. **Project ID immutable.** All work uses the confirmed `project_id` unless explicitly cross-project (template command). | ||
| 3. **Surface MCP failures explicitly.** Never silently skip. | ||
| 4. **Confirm before destructive ops.** Always preview + explicit user confirmation before: deleting a dashboard, bulk cleanup, or overwriting dashboard content. | ||
| 5. **'exit' always valid.** Stop, discard uncommitted, return to menu. | ||
| 6. **No per-command "what next" menus.** Commands return control here. The router shows the menu. | ||
| 7. **Cross-project operations.** The template command is the only one that works across projects. It validates both source and target project IDs and reconstructs the board in the target project (see `commands/template-dashboard.md`). | ||
| 8. **Validate every write before reporting success.** After any operation that mutates a board — create, update, duplicate, or a template rebuild — re-read the affected board *including its full layout* and confirm the result matches intent (expected row count, report/text cell counts, title/description). If the readback diverges, do NOT report `✅` — surface what landed vs. what was requested. Layout cells use opaque server-generated IDs, so a write can partially succeed silently; the readback is the only reliable confirmation. Refresh `dashboard_layout_cache` from this readback. | ||
| 9. **Fetching the dashboard set.** Default to the sortable entity-search capability when listing dashboards — it returns richer metadata and supports sorting. Fall back to the plain dashboard-list capability only if search is unavailable. Cache the result in `dashboard_list_cache` and reuse it across commands rather than re-fetching. | ||
| 10. **Shared constraints.** Layout limits, the text-card HTML whitelist, and other API gotchas live once in `references/mcp-tool-reference.md`. Command files point to it by name rather than restating. | ||
|
|
||
| --- | ||
|
|
||
| ## Session Context | ||
|
|
||
| Persist across commands within a session. **Cross-command reuse is mandatory** — never re-fetch what exists in session. | ||
|
|
||
| | Variable | Description | | ||
| |----------|-------------| | ||
| | `project_id` | Immutable after Step 0. | | ||
| | `project_name` | Display name. | | ||
| | `projects_list` | All accessible projects. | | ||
| | `dashboard_list_cache` | `dashboard_id → {title, description, last_modified, ...}` map. Report counts are NOT returned by the list operation — populate them from `dashboard_layout_cache` after reading a board's full layout. | | ||
| | `dashboard_layout_cache` | `dashboard_id → {layout, report_count, text_count, row_count}` map (populated on-demand). | | ||
|
|
||
| --- | ||
|
|
||
| ## Step 0 — Project Validation | ||
|
|
||
| **Direct routing:** If the user's message contains a project ID AND a clear command intent (e.g. "create a dashboard in project 12345", "clean up dashboards in project 67890"), extract both, validate, and route directly — skip the menu. | ||
|
|
||
| **Project ID without command:** Validate and show menu. | ||
|
|
||
| **No project ID:** Ask which project. On 'list' → retrieve the accessible projects and show them in a table. | ||
|
|
||
| **Validation:** | ||
| 1. Retrieve the list of accessible projects and match the ID. If found → `✅ [Project Name] ([project_id])`, proceed. | ||
| 2. Not found → error, ask to re-enter or 'list'. | ||
|
|
||
| **MCP check:** If the project lookup fails because no Mixpanel capability is available → tell the user to connect the Mixpanel MCP, then stop. | ||
|
|
||
| --- | ||
|
|
||
| ## Command Menu | ||
|
|
||
| Show only when no direct command was inferred: | ||
|
|
||
| ``` | ||
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| Manage Boards — [Project Name] ([project_id]) | ||
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| 1. Create Dashboard — Build a new board from description | ||
| 2. Template Dashboard — Clone a reference board to a new project | ||
| 3. Cleanup Dashboards — Audit & remove stale/empty boards | ||
| 4. Dashboard Inventory — Catalog all boards with metadata | ||
| 5. Duplicate Dashboard — Copy a board within the same project | ||
| 6. Update Dashboard — Modify metadata, rows, or layout | ||
| 7. Exit | ||
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| ``` | ||
|
|
||
| | Choice | Action | | ||
| |--------|--------| | ||
| | **1** | Read `commands/create-dashboard.md`, execute | | ||
| | **2** | Read `commands/template-dashboard.md`, execute | | ||
| | **3** | Read `commands/cleanup-dashboards.md`, execute | | ||
| | **4** | Read `commands/dashboard-inventory.md`, execute | | ||
| | **5** | Read `commands/duplicate-dashboard.md`, execute | | ||
| | **6** | Read `commands/update-dashboard.md`, execute | | ||
| | **7** | Exit | | ||
|
|
||
| **Routing matrix** — when the user's phrasing doesn't map to a menu number, route on intent rather than showing the menu: | ||
|
|
||
| | User intent (examples) | Route to | | ||
| |------------------------|----------| | ||
| | "build / make / set up a board", "new dashboard for X" | Create | | ||
| | "copy this board to [other project]", "standardize boards across projects" | Template | | ||
| | "clean up / audit / find stale / empty / duplicate boards" | Cleanup | | ||
| | "list / catalog / inventory all boards", "what boards exist" | Inventory | | ||
| | "copy / duplicate this board here", "clone within this project" | Duplicate | | ||
| | "rename / edit / add a row / change the layout of a board" | Update | | ||
| | "delete [board]" | Cleanup (single-board delete path) | | ||
| | Ambiguous or matches multiple commands | Show the menu — do not guess | | ||
|
|
||
| --- | ||
|
|
||
| ## Return Handler | ||
|
|
||
| When a command completes → `✅ Done.` → re-display command menu. No re-validation. | ||
142 changes: 142 additions & 0 deletions
142
plugins/mixpanel-mcp-eu/skills/manage-boards/commands/cleanup-dashboards.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| # Command: Cleanup Dashboards | ||
|
|
||
| Audits the dashboard estate in a project: identifies **stale** (not touched in a long time), duplicate, empty, and sparse boards. Recommends archive or delete. | ||
|
|
||
| Staleness is a first-class signal here — a board with many reports that nobody has touched in months is still a cleanup candidate. Do not equate "has reports" with "healthy." | ||
|
|
||
| --- | ||
|
|
||
| ## Contents | ||
|
|
||
| - Phase 1 — Fetch all dashboards (with recency) | ||
| - Phase 2 — Deep inspection | ||
| - Phase 3 — Classification (structural + recency flags, duplicates) | ||
| - Phase 4 — Report | ||
| - Phase 5 — Action (interactive delete paths) | ||
| - Output | ||
| - Error Handling | ||
|
|
||
| --- | ||
|
|
||
| ## Execution | ||
|
|
||
| ### Phase 1 — Fetch all dashboards (with recency) | ||
|
|
||
| 1. Fetch the dashboard set per the **Fetching the dashboard set** rule (sortable entity-search preferred, | ||
| sorting on the most recent timestamp the API exposes; plain list as fallback). | ||
| 2. For each dashboard, extract whatever the response provides: | ||
| - `id`, `title`, `description` | ||
| - `last_modified` (a.k.a. modified/updated), `last_viewed` (if present), `created_at` | ||
| - `creator` / `owner` (if available) | ||
| 3. Cache in `dashboard_list_cache`. | ||
|
|
||
| **Recency field discovery (do this once, silently):** Scan the result set — not just the first object — to determine which timestamp fields the API populates, since field coverage can vary board to board. Choose the preferred field by coverage across boards, in priority order: `last_viewed` → `last_modified`/`updated_at` → `created_at`. Per board, if the chosen field is missing, fall back down the same order for that board and label its recency basis accordingly. Record the field(s) used so the report can label the column accurately. If NO board exposes any timestamp field, skip the Stale classification entirely and tell the user in the report header: "Recency data not available from the API — staleness not assessed; showing structural flags only." | ||
|
|
||
| ### Phase 2 — Deep inspection | ||
|
|
||
| For each dashboard from Phase 1, read its full layout. Fire in parallel (batches of 5 to avoid rate limits). Skip any dashboard already in `dashboard_layout_cache`. | ||
|
|
||
| From the layout, derive and cache: | ||
| - **Report count:** number of report cells | ||
| - **Text-only count:** number of text cells | ||
| - **Total cells:** sum of all cells | ||
| - **Row count:** number of rows | ||
|
|
||
| ### Phase 3 — Classification | ||
|
|
||
| Compute `days_since` from the chosen recency timestamp (today minus that date). Then classify. **Staleness and structure are independent axes** — evaluate both; a board may carry a structural flag *and* a stale flag. | ||
|
|
||
| > **Use the bundled helper for staleness and duplicate math.** Run `scripts/dashboard_utils.py` rather than re-deriving title normalization or recency arithmetic by hand each run — it is the single source of truth for `days_since(...)`, `normalize_title(...)`, and `is_probable_duplicate(...)`. This keeps results deterministic across runs. | ||
|
|
||
| **Structural flags** (from layout): | ||
|
|
||
| | Category | Criteria | Flag | | ||
| |----------|----------|------| | ||
| | **Empty** | 0 report cells AND 0 text cells (or only a single default text card) | 🟡 Empty | | ||
| | **Text-only** | 0 report cells but has text cards | 🟡 Text-only | | ||
| | **Sparse** | 1-2 report cells only | 🔵 Sparse | | ||
| | **Potential duplicate** | `is_probable_duplicate()` returns true vs. another board | 🟠 Possible dup | | ||
| | **Healthy** | 3+ report cells | ✅ Active | | ||
|
|
||
| **Recency flag** (from `days_since`, only if a timestamp was found): | ||
|
|
||
| | Category | Criteria | Flag | | ||
| |----------|----------|------| | ||
| | **Stale** | `days_since` ≥ 90 | 🔴 Stale ([N]d) | | ||
| | **Aging** | 60 ≤ `days_since` < 90 | 🟤 Aging ([N]d) | | ||
| | **Recent** | `days_since` < 60 | (no flag) | | ||
|
|
||
| > Default threshold is 90 days (a skill-chosen heuristic). If the user names a different window ("anything untouched for 6 months", "stale = 30 days"), use theirs. State the threshold in the report header. | ||
|
|
||
| **Cleanup priority** — order the action list by: 🔴 Stale **and** (Empty/Text-only/Sparse) first → 🔴 Stale + Active → 🟠 duplicates → 🟡 structural-only. The worst offenders are old *and* thin. | ||
|
|
||
| **Duplicate detection** — delegated to `scripts/dashboard_utils.py`. Run `is_probable_duplicate(a, b)` pairwise across the board list; flag both members of any matching pair. (It matches on normalized-title similarity ≥ 0.80 or one normalized title contained in the other — the module is the source of truth for the exact rule.) | ||
|
|
||
| ### Phase 4 — Report | ||
|
|
||
| Present the audit as a table. Include the recency column and label it with the actual field used. | ||
|
|
||
| ``` | ||
| Dashboard Audit — [Project Name] ([project_id]) | ||
| Recency basis: Last modified | Stale threshold: 90 days | ||
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| ID | Title | Reports | Last modified | Status | ||
| ---------|---------------------------|---------|---------------|------------------ | ||
| 1234567 | Onboarding Funnel | 6 | 5d ago | ✅ Active | ||
| 1234568 | Onboarding Funnel (Copy) | 6 | 142d ago | 🔴 Stale · 🟠 dup | ||
| 1234569 | Test Board | 0 | 311d ago | 🔴 Stale · 🟡 Empty | ||
| 1234570 | Notes | 0 | 18d ago | 🟡 Text-only | ||
| 1234571 | Quick Metrics | 1 | 96d ago | 🔴 Stale · 🔵 Sparse | ||
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
|
||
| Summary: 15 dashboards | 4 stale | 3 empty | 2 possible duplicates | 1 sparse | 8 active | ||
| Top cleanup candidates: #1234569 (stale + empty), #1234568 (stale + dup), #1234571 (stale + sparse) | ||
| ``` | ||
|
|
||
| If recency data was unavailable, drop the "Last modified" column and the stale flags, and note it in the header. | ||
|
|
||
| ### Phase 5 — Action (interactive) | ||
|
|
||
| After showing the audit, ask what the user wants to do: | ||
|
|
||
| **Option A: Delete selected boards** — user provides IDs (comma-separated or range). Show confirmation with titles: "Delete these 3 dashboards? [Title A], [Title B], [Title C]". On confirm, delete each sequentially, showing progress: `Deleted 2/3...` | ||
|
|
||
| **Option B: Delete all top cleanup candidates** — list all boards flagged 🔴 Stale **and** structurally thin (Empty / Text-only / Sparse) — the safest bulk target. Require explicit confirmation with count: "Delete all 4 stale + thin dashboards?" Then execute. | ||
|
|
||
| **Option C: Delete all flagged-empty boards** — list all 🟡 Empty + 🟡 Text-only boards (regardless of age). Require explicit confirmation with count, then execute. | ||
|
|
||
| **Option D: Skip — just wanted the audit** — return control to router. | ||
|
|
||
| **Never auto-delete.** Every deletion path requires explicit user confirmation — no exceptions. | ||
|
|
||
| **Protect high-value stale boards.** Never propose deleting a 🔴 Stale board that still has 3+ reports without flagging that it may be a seasonal/quarterly board worth archiving rather than deleting. | ||
|
|
||
| **Verify deletions.** After running deletions, re-fetch the dashboard list (or attempt to read each deleted ID and expect a not-found) to confirm each target is actually gone before reporting the count. Report any ID that still resolves as a failed deletion rather than a success. | ||
|
|
||
| --- | ||
|
|
||
| ## Output | ||
|
|
||
| After deletions (if any): | ||
|
|
||
| ``` | ||
| ✅ Cleanup complete | ||
| Deleted: [N] dashboards | ||
| Remaining: [M] dashboards | ||
| Freed: [list of deleted titles] | ||
| ``` | ||
|
|
||
| Return control to router. | ||
|
|
||
| --- | ||
|
|
||
| ## Error Handling | ||
|
|
||
| | Situation | Action | | ||
| |-----------|--------| | ||
| | Entity-search unavailable | Fall back to the plain dashboard list (no sort); recency still parsed if present | | ||
| | No timestamp field in response | Skip Stale/Aging classification, note in header, show structural flags only | | ||
| | Fetch returns empty | "No dashboards found in this project." Return. | | ||
| | Layout read fails for one board | Mark as "⚠️ Could not inspect", continue with others | | ||
| | A deletion fails | Note the failure, continue with remaining deletions | | ||
| | User cancels deletion | "No dashboards deleted." Return. | |
90 changes: 90 additions & 0 deletions
90
plugins/mixpanel-mcp-eu/skills/manage-boards/commands/create-dashboard.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # Command: Create Dashboard | ||
|
|
||
| Creates a new dashboard from a natural language description. | ||
|
|
||
| --- | ||
|
|
||
| ## Intake | ||
|
|
||
| The user provides either: | ||
| - **A description** — e.g. "create a dashboard tracking onboarding funnel metrics" | ||
| - **A structured spec** — title, description, specific reports to include | ||
|
|
||
| Extract: | ||
| 1. **Title** (required) — infer from description if not stated | ||
| 2. **Description** (optional) — a 1-2 line summary | ||
| 3. **Report intent** (optional) — what charts/reports the user wants on the board | ||
| 4. **Visibility** — private or shared (default: shared) | ||
| 5. **Time filter** — if the user mentions a date range, set it; otherwise omit | ||
|
|
||
| --- | ||
|
|
||
| ## Execution | ||
|
|
||
| ### Path A: Empty Dashboard (no reports specified or no query_ids available) | ||
|
|
||
| If the user just wants a board created without pre-built reports: | ||
|
|
||
| 1. Create the dashboard with the session `project_id`, the extracted title and | ||
| description, the chosen visibility, and at minimum one row holding a single | ||
| text card, e.g. `<h1>Dashboard Title</h1><p>Description here. Add reports to populate.</p>`. | ||
| 2. Return the dashboard ID and confirm. | ||
|
|
||
| ### Path B: Dashboard with Reports | ||
|
|
||
| If the user wants specific charts, each report needs a `query_id` minted first. | ||
| The flow: | ||
|
|
||
| 1. For each desired report, run its query with results skipped to mint a | ||
| `query_id`. Read the query schema first if unsure about available | ||
| events/properties. Fire queries in parallel where possible. | ||
| 2. Assemble the rows, observing the layout limits and grouping rules in | ||
| `references/mcp-tool-reference.md`. Group related reports in the same row; | ||
| use text cards as section headers between logical groups. | ||
| 3. Create the dashboard with the assembled rows. | ||
| 4. Return the dashboard ID, title, and a summary of what was created. | ||
|
|
||
| ### Path C: Dashboard from a description (AI-inferred layout) | ||
|
|
||
| When the user gives a loose description like "build me a product health dashboard": | ||
|
|
||
| 1. Infer 4-8 logical report categories from the description. | ||
| 2. Create the dashboard with text-card section headers for each category and a | ||
| placeholder description. | ||
| 3. Tell the user: "Created [Title] with section scaffolding. Reports need to be | ||
| added manually or by minting queries — want me to populate any sections?" | ||
|
|
||
| --- | ||
|
|
||
| ## Time Filter | ||
|
|
||
| If the user specifies a date range, set a time filter. Common mappings: | ||
| - "last 7 days" → last 7 days | ||
| - "last month" → last 1 month | ||
| - "last quarter" → last 3 months | ||
| - Specific range → a "between" range with `from`/`to` dates (YYYY-MM-DD) | ||
|
|
||
| --- | ||
|
|
||
| ## Output | ||
|
|
||
| ``` | ||
| ✅ Dashboard created | ||
| ID: [dashboard_id] | ||
| Title: [title] | ||
| Reports: [N reports] | Empty (scaffold only) | ||
| Access: Shared | Private | ||
| ``` | ||
|
|
||
| Then re-read the board to confirm the write landed (see the **Validate every | ||
| write** rule) before reporting `✅`. Return control to router. | ||
|
|
||
| --- | ||
|
|
||
| ## Error Handling | ||
|
|
||
| | Situation | Action | | ||
| |-----------|--------| | ||
| | A report's query fails to mint | Skip that report, note in output, continue with others | | ||
| | Dashboard creation fails | Surface full error to user | | ||
| | User description too vague | Create scaffold with text-card sections, explain next steps | |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A routing matrix would complement this menu section for cases where the agent has to infer routing behavior because the user doesn't exactly follow the choices given.